26e7d4e8a6872b517b16506af7caabd9078c85be
[vlc.git] / modules / audio_output / amem.c
1 /*****************************************************************************
2  * amem.c : virtual LibVLC audio output plugin
3  *****************************************************************************
4  * Copyright (C) 2011 RĂ©mi Denis-Courmont
5  *
6  * This program is free software; you can redistribute it and/or modify it
7  * under the terms of the GNU Lesser General Public License as published by
8  * the Free Software Foundation; either version 2.1 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  * GNU Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public License
17  * along with this program; if not, write to the Free Software Foundation,
18  * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
19  *****************************************************************************/
20
21 #ifdef HAVE_CONFIG_H
22 # include "config.h"
23 #endif
24
25 #include <vlc_common.h>
26 #include <vlc_plugin.h>
27 #include <vlc_aout.h>
28 #include <assert.h>
29
30 static int Open (vlc_object_t *);
31 static void Close (vlc_object_t *);
32
33 vlc_module_begin ()
34     set_shortname (N_("Audio memory"))
35     set_description (N_("Audio memory output"))
36     set_capability ("audio output", 0)
37     set_category (CAT_AUDIO)
38     set_subcategory (SUBCAT_AUDIO_AOUT)
39     set_callbacks (Open, Close)
40
41     add_string ("amem-format", "S16N",
42                 N_("Sample format"), N_("Sample format"), false)
43         change_private()
44     add_integer ("amem-rate", 44100,
45                  N_("Sample rate"), N_("Sample rate"), false)
46         change_integer_range (1, 352800)
47         change_private()
48     add_integer ("amem-channels", 2,
49                  N_("Channels count"), N_("Channels count"), false)
50         change_integer_range (1, AOUT_CHAN_MAX)
51         change_private()
52
53 vlc_module_end ()
54
55 typedef struct
56 {
57     void *opaque;
58     int (*setup) (void **, char *, unsigned *, unsigned *);
59     void (*cleanup) (void *opaque);
60     union
61     {
62         struct
63         {
64             void *setup_opaque;
65         };
66         struct
67         {
68              unsigned rate:18;
69              unsigned channels:14;
70         };
71     };
72     void (*play) (void *opaque, const void *data, unsigned count, int64_t pts);
73     void (*pause) (void *opaque, int64_t pts);
74     void (*resume) (void *opaque, int64_t pts);
75     void (*flush) (void *opaque);
76     void (*drain) (void *opaque);
77     int (*set_volume) (void *opaque, float vol, bool mute);
78     float volume;
79     bool mute;
80     bool ready;
81     vlc_mutex_t lock;
82 } aout_sys_t;
83
84 static void Play(audio_output_t *aout, block_t *block, mtime_t date)
85 {
86     aout_sys_t *sys = aout->sys;
87
88     vlc_mutex_lock(&sys->lock);
89     sys->play(sys->opaque, block->p_buffer, block->i_nb_samples, date);
90     vlc_mutex_unlock(&sys->lock);
91     block_Release (block);
92 }
93
94 static void Pause (audio_output_t *aout, bool paused, mtime_t date)
95 {
96     aout_sys_t *sys = aout->sys;
97     void (*cb) (void *, int64_t) = paused ? sys->pause : sys->resume;
98
99     if (cb != NULL)
100     {
101         vlc_mutex_lock(&sys->lock);
102         cb (sys->opaque, date);
103         vlc_mutex_unlock(&sys->lock);
104     }
105 }
106
107 static void Flush (audio_output_t *aout, bool wait)
108 {
109     aout_sys_t *sys = aout->sys;
110     void (*cb) (void *) = wait ? sys->drain : sys->flush;
111
112     if (cb != NULL)
113     {
114         vlc_mutex_lock(&sys->lock);
115         cb (sys->opaque);
116         vlc_mutex_unlock(&sys->lock);
117     }
118 }
119
120 static int VolumeSet (audio_output_t *aout, float vol)
121 {
122     aout_sys_t *sys = aout->sys;
123     int val;
124
125     sys->volume = vol;
126
127     vlc_mutex_lock(&sys->lock);
128     if (sys->ready)
129         val = sys->set_volume(sys->opaque, vol, sys->mute);
130     else
131         val = 0; /* sys->opaque is not yet defined... */
132     vlc_mutex_unlock(&sys->lock);
133
134     return val ? -1 : 0;
135 }
136
137 static int MuteSet (audio_output_t *aout, bool mute)
138 {
139     aout_sys_t *sys = aout->sys;
140     int val;
141
142     sys->mute = mute;
143
144     vlc_mutex_lock(&sys->lock);
145     if (sys->ready)
146         val = sys->set_volume(sys->opaque, sys->volume, mute);
147     else
148         val = 0; /* sys->opaque is not yet defined... */
149     vlc_mutex_unlock(&sys->lock);
150
151     return val ? -1 : 0;
152 }
153
154 static int SoftVolumeSet (audio_output_t *aout, float vol)
155 {
156     aout_sys_t *sys = aout->sys;
157
158     vol = vol * vol * vol;
159     if (!sys->mute && aout_GainRequest (aout, vol))
160         return -1;
161     sys->volume = vol;
162     return 0;
163 }
164
165 static int SoftMuteSet (audio_output_t *aout, bool mute)
166 {
167     aout_sys_t *sys = aout->sys;
168
169     if (aout_GainRequest (aout, mute ? 0.f : sys->volume))
170         return -1;
171     sys->mute = mute;
172     return 0;
173 }
174
175 static void Stop (audio_output_t *aout)
176 {
177     aout_sys_t *sys = aout->sys;
178
179     vlc_mutex_lock(&sys->lock);
180     if (sys->cleanup != NULL)
181         sys->cleanup (sys->opaque);
182
183     sys->ready = false;
184     vlc_mutex_unlock(&sys->lock);
185 }
186
187 static int Start (audio_output_t *aout, audio_sample_format_t *fmt)
188 {
189     aout_sys_t *sys = aout->sys;
190     char format[5] = "S16N";
191     unsigned channels;
192
193     if (aout_FormatNbChannels(fmt) == 0)
194         return VLC_EGENERIC;
195
196     vlc_mutex_lock(&sys->lock);
197     if (sys->setup != NULL)
198     {
199         channels = aout_FormatNbChannels(fmt);
200
201         sys->opaque = sys->setup_opaque;
202         if (sys->setup (&sys->opaque, format, &fmt->i_rate, &channels))
203         {
204             vlc_mutex_unlock(&sys->lock);
205             return VLC_EGENERIC;
206         }
207     }
208     else
209     {
210         fmt->i_rate = sys->rate;
211         channels = sys->channels;
212     }
213
214     /* Initialize volume (in case the UI changed volume before setup) */
215     sys->ready = true;
216     if (sys->set_volume != NULL)
217         sys->set_volume(sys->opaque, sys->volume, sys->mute);
218     vlc_mutex_unlock(&sys->lock);
219
220     /* Ensure that format is supported */
221     if (fmt->i_rate == 0 || fmt->i_rate > 192000
222      || channels == 0 || channels > AOUT_CHAN_MAX
223      || strcmp(format, "S16N") /* TODO: amem-format */)
224     {
225         msg_Err (aout, "format not supported: %s, %u channel(s), %u Hz",
226                  format, channels, fmt->i_rate);
227         Stop (aout);
228         return VLC_EGENERIC;
229     }
230
231     /* channel mapping */
232     switch (channels)
233     {
234         case 1:
235             fmt->i_physical_channels = AOUT_CHAN_CENTER;
236             break;
237         case 2:
238             fmt->i_physical_channels = AOUT_CHANS_2_0;
239             break;
240         case 3:
241             fmt->i_physical_channels = AOUT_CHANS_2_1;
242             break;
243         case 4:
244             fmt->i_physical_channels = AOUT_CHANS_4_0;
245             break;
246         case 5:
247             fmt->i_physical_channels = AOUT_CHANS_5_0;
248             break;
249         case 6:
250             fmt->i_physical_channels = AOUT_CHANS_5_1;
251             break;
252         case 7:
253             fmt->i_physical_channels =
254                 AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT | AOUT_CHAN_CENTER |
255                 AOUT_CHAN_REARCENTER | AOUT_CHAN_MIDDLELEFT |
256                 AOUT_CHAN_MIDDLERIGHT | AOUT_CHAN_LFE;
257             break;
258         case 8:
259             fmt->i_physical_channels = AOUT_CHANS_7_1;
260             break;
261         default:
262             vlc_assert_unreachable();
263     }
264
265     fmt->i_format = VLC_CODEC_S16N;
266     fmt->channel_type = AUDIO_CHANNEL_TYPE_BITMAP;
267     return VLC_SUCCESS;
268 }
269
270 static int Open (vlc_object_t *obj)
271 {
272     audio_output_t *aout = (audio_output_t *)obj;
273     aout_sys_t *sys = malloc (sizeof (*sys));
274     if (unlikely(sys == NULL))
275         return VLC_ENOMEM;
276
277     void *opaque = var_InheritAddress (obj, "amem-data");
278     sys->setup = var_InheritAddress (obj, "amem-setup");
279     if (sys->setup != NULL)
280     {
281         sys->cleanup = var_InheritAddress (obj, "amem-cleanup");
282         sys->setup_opaque = opaque;
283     }
284     else
285     {
286         sys->cleanup = NULL;
287         sys->opaque = opaque;
288         sys->rate = var_InheritInteger (obj, "amem-rate");
289         sys->channels = var_InheritInteger (obj, "amem-channels");
290     }
291
292     sys->play = var_InheritAddress (obj, "amem-play");
293     sys->pause = var_InheritAddress (obj, "amem-pause");
294     sys->resume = var_InheritAddress (obj, "amem-resume");
295     sys->flush = var_InheritAddress (obj, "amem-flush");
296     sys->drain = var_InheritAddress (obj, "amem-drain");
297     sys->set_volume = var_InheritAddress (obj, "amem-set-volume");
298     sys->volume = 1.;
299     sys->mute = false;
300     sys->ready = false;
301     vlc_mutex_init(&sys->lock);
302
303     if (sys->play == NULL)
304     {
305         free (sys);
306         return VLC_EGENERIC;
307     }
308
309     aout->sys = sys;
310     aout->start = Start;
311     aout->stop = Stop;
312     aout->time_get = NULL;
313     aout->play = Play;
314     aout->pause = Pause;
315     aout->flush = Flush;
316     if (sys->set_volume != NULL)
317     {
318         aout->volume_set = VolumeSet;
319         aout->mute_set = MuteSet;
320     }
321     else
322     {
323         aout->volume_set = SoftVolumeSet;
324         aout->mute_set = SoftMuteSet;
325     }
326     return VLC_SUCCESS;
327 }
328
329 static void Close (vlc_object_t *obj)
330 {
331     audio_output_t *aout = (audio_output_t *)obj;
332     aout_sys_t *sys = aout->sys;
333
334     vlc_mutex_destroy(&sys->lock);
335     free (sys);
336 }