]> git.sesse.net Git - vlc/blob - modules/audio_output/audiotrack.c
audiotrack: missing software gain setting
[vlc] / modules / audio_output / audiotrack.c
1 /*****************************************************************************
2  * audiotrack.c: Android native AudioTrack audio output module
3  *****************************************************************************
4  * Copyright © 2012 VLC authors and VideoLAN
5  *
6  * Authors: Ming Hu <tewilove@gmail.com>
7  *
8  * This program is free software; you can redistribute it and/or modify it
9  * under the terms of the GNU Lesser General Public License as published by
10  * the Free Software Foundation; either version 2.1 of the License, or
11  * (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16  * GNU Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public License
19  * along with this program; if not, write to the Free Software Foundation,
20  * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
21  *****************************************************************************/
22
23 #ifdef HAVE_CONFIG_H
24 # include "config.h"
25 #endif
26
27 #include <vlc_common.h>
28 #include <vlc_plugin.h>
29 #include <vlc_aout.h>
30
31 #include <dlfcn.h>
32 #include <assert.h>
33
34 #define SIZE_OF_AUDIOTRACK 256
35
36 /* From AudioSystem.h */
37 #define MUSIC 3
38
39 enum pcm_sub_format {
40     PCM_SUB_16_BIT          = 0x1, // must be 1 for backward compatibility
41     PCM_SUB_8_BIT           = 0x2  // must be 2 for backward compatibility
42 };
43
44 enum audio_format {
45     PCM                 = 0x00000000, // must be 0 for backward compatibility
46     PCM_16_BIT          = (PCM|PCM_SUB_16_BIT),
47     PCM_8_BIT           = (PCM|PCM_SUB_8_BIT)
48 };
49
50 enum audio_channels {
51     CHANNEL_OUT_FRONT_LEFT            = 0x4,
52     CHANNEL_OUT_FRONT_RIGHT           = 0x8,
53     CHANNEL_OUT_FRONT_CENTER          = 0x10,
54     CHANNEL_OUT_LOW_FREQUENCY         = 0x20,
55     CHANNEL_OUT_BACK_LEFT             = 0x40,
56     CHANNEL_OUT_BACK_RIGHT            = 0x80,
57     CHANNEL_OUT_FRONT_LEFT_OF_CENTER  = 0x100,
58     CHANNEL_OUT_FRONT_RIGHT_OF_CENTER = 0x200,
59     CHANNEL_OUT_BACK_CENTER           = 0x400,
60     CHANNEL_OUT_MONO = CHANNEL_OUT_FRONT_LEFT,
61     CHANNEL_OUT_STEREO = (CHANNEL_OUT_FRONT_LEFT | CHANNEL_OUT_FRONT_RIGHT)
62 };
63
64 // _ZN7android11AudioSystem19getOutputFrameCountEPii
65 typedef int (*AudioSystem_getOutputFrameCount)(int *, int);
66 // _ZN7android11AudioSystem16getOutputLatencyEPji
67 typedef int (*AudioSystem_getOutputLatency)(unsigned int *, int);
68 // _ZN7android11AudioSystem21getOutputSamplingRateEPii
69 typedef int (*AudioSystem_getOutputSamplingRate)(int *, int);
70
71 // _ZN7android10AudioTrack16getMinFrameCountEPiij
72 typedef int (*AudioTrack_getMinFrameCount)(int *, int, unsigned int);
73
74 // _ZN7android11AudioSystem17getRenderPositionEPjS1_i
75 typedef int (*AudioTrack_getRenderPosition)(uint32_t *, uint32_t *, int);
76 // _ZN7android10AudioTrackC1EijiiijPFviPvS1_ES1_ii
77 typedef void (*AudioTrack_ctor)(void *, int, unsigned int, int, int, int, unsigned int, void (*)(int, void *, void *), void *, int, int);
78 // _ZN7android10AudioTrackC1EijiiijPFviPvS1_ES1_i
79 typedef void (*AudioTrack_ctor_legacy)(void *, int, unsigned int, int, int, int, unsigned int, void (*)(int, void *, void *), void *, int);
80 // _ZN7android10AudioTrackD1Ev
81 typedef void (*AudioTrack_dtor)(void *);
82 // _ZNK7android10AudioTrack9initCheckEv
83 typedef int (*AudioTrack_initCheck)(void *);
84 // _ZN7android10AudioTrack5startEv
85 typedef int (*AudioTrack_start)(void *);
86 // _ZN7android10AudioTrack4stopEv
87 typedef int (*AudioTrack_stop)(void *);
88 // _ZN7android10AudioTrack5writeEPKvj
89 typedef int (*AudioTrack_write)(void *, void  const*, unsigned int);
90 // _ZN7android10AudioTrack5flushEv
91 typedef int (*AudioTrack_flush)(void *);
92 // _ZN7android10AudioTrack5pauseEv
93 typedef int (*AudioTrack_pause)(void *);
94
95 struct aout_sys_t {
96     float soft_gain;
97     bool soft_mute;
98
99     int rate;
100     uint32_t samples_written;
101     uint32_t initial;
102     int bytes_per_frame;
103
104     void *libmedia;
105     void *AudioTrack;
106
107     AudioSystem_getOutputFrameCount as_getOutputFrameCount;
108     AudioSystem_getOutputLatency as_getOutputLatency;
109     AudioSystem_getOutputSamplingRate as_getOutputSamplingRate;
110
111     AudioTrack_getMinFrameCount at_getMinFrameCount;
112     AudioTrack_ctor at_ctor;
113     AudioTrack_ctor_legacy at_ctor_legacy;
114     AudioTrack_dtor at_dtor;
115     AudioTrack_initCheck at_initCheck;
116     AudioTrack_start at_start;
117     AudioTrack_stop at_stop;
118     AudioTrack_write at_write;
119     AudioTrack_flush at_flush;
120     AudioTrack_pause at_pause;
121     AudioTrack_getRenderPosition at_getRenderPosition;
122 };
123
124 /* Soft volume helper */
125 #include "volume.h"
126
127 static void *InitLibrary(struct aout_sys_t *p_sys);
128
129 static int  Open(vlc_object_t *);
130 static void Close(vlc_object_t *);
131 static void Play(audio_output_t*, block_t*);
132 static void Pause (audio_output_t *, bool, mtime_t);
133 static void Flush (audio_output_t *, bool);
134
135 vlc_module_begin ()
136     set_shortname("AudioTrack")
137     set_description(N_("Android AudioTrack audio output"))
138     set_capability("audio output", 225)
139     set_category(CAT_AUDIO)
140     set_subcategory(SUBCAT_AUDIO_AOUT)
141     add_sw_gain()
142     add_shortcut("android")
143     set_callbacks(Open, Close)
144 vlc_module_end ()
145
146 static void *InitLibrary(struct aout_sys_t *p_sys)
147 {
148     /* DL Open libmedia */
149     void *p_library;
150     p_library = dlopen("libmedia.so", RTLD_NOW|RTLD_LOCAL);
151     if (!p_library)
152         return NULL;
153
154     /* Register symbols */
155     p_sys->as_getOutputFrameCount = (AudioSystem_getOutputFrameCount)(dlsym(p_library, "_ZN7android11AudioSystem19getOutputFrameCountEPii"));
156     p_sys->as_getOutputLatency = (AudioSystem_getOutputLatency)(dlsym(p_library, "_ZN7android11AudioSystem16getOutputLatencyEPji"));
157     if(p_sys->as_getOutputLatency == NULL) {
158         /* 4.1 Jellybean prototype */
159         p_sys->as_getOutputLatency = (AudioSystem_getOutputLatency)(dlsym(p_library, "_ZN7android11AudioSystem16getOutputLatencyEPj19audio_stream_type_t"));
160     }
161     p_sys->as_getOutputSamplingRate = (AudioSystem_getOutputSamplingRate)(dlsym(p_library, "_ZN7android11AudioSystem21getOutputSamplingRateEPii"));
162     p_sys->at_getMinFrameCount = (AudioTrack_getMinFrameCount)(dlsym(p_library, "_ZN7android10AudioTrack16getMinFrameCountEPiij"));
163     if(p_sys->at_getMinFrameCount == NULL) {
164         /* 4.1 Jellybean prototype */
165         p_sys->at_getMinFrameCount = (AudioTrack_getMinFrameCount)(dlsym(p_library, "_ZN7android10AudioTrack16getMinFrameCountEPi19audio_stream_type_tj"));
166     }
167     p_sys->at_ctor = (AudioTrack_ctor)(dlsym(p_library, "_ZN7android10AudioTrackC1EijiiijPFviPvS1_ES1_ii"));
168     p_sys->at_ctor_legacy = (AudioTrack_ctor_legacy)(dlsym(p_library, "_ZN7android10AudioTrackC1EijiiijPFviPvS1_ES1_i"));
169     p_sys->at_dtor = (AudioTrack_dtor)(dlsym(p_library, "_ZN7android10AudioTrackD1Ev"));
170     p_sys->at_initCheck = (AudioTrack_initCheck)(dlsym(p_library, "_ZNK7android10AudioTrack9initCheckEv"));
171     p_sys->at_start = (AudioTrack_start)(dlsym(p_library, "_ZN7android10AudioTrack5startEv"));
172     p_sys->at_stop = (AudioTrack_stop)(dlsym(p_library, "_ZN7android10AudioTrack4stopEv"));
173     p_sys->at_write = (AudioTrack_write)(dlsym(p_library, "_ZN7android10AudioTrack5writeEPKvj"));
174     p_sys->at_flush = (AudioTrack_flush)(dlsym(p_library, "_ZN7android10AudioTrack5flushEv"));
175     p_sys->at_pause = (AudioTrack_pause)(dlsym(p_library, "_ZN7android10AudioTrack5pauseEv"));
176
177     /* this symbol can have different names depending on the mangling */
178     p_sys->at_getRenderPosition = (AudioTrack_getRenderPosition)(dlsym(p_library, "_ZN7android11AudioSystem17getRenderPositionEPjS1_i"));
179     if (!p_sys->at_getRenderPosition)
180         p_sys->at_getRenderPosition = (AudioTrack_getRenderPosition)(dlsym(p_library, "_ZN7android11AudioSystem17getRenderPositionEPjS1_19audio_stream_type_t"));
181
182     /* We need the first 3 or the last 1 */
183     if (!((p_sys->as_getOutputFrameCount && p_sys->as_getOutputLatency && p_sys->as_getOutputSamplingRate)
184         || p_sys->at_getMinFrameCount)) {
185         dlclose(p_library);
186         return NULL;
187     }
188
189     // We need all the other Symbols
190     if (!((p_sys->at_ctor || p_sys->at_ctor_legacy) && p_sys->at_dtor && p_sys->at_initCheck &&
191            p_sys->at_start && p_sys->at_stop && p_sys->at_write && p_sys->at_flush)) {
192         dlclose(p_library);
193         return NULL;
194     }
195     return p_library;
196 }
197
198 static int TimeGet(audio_output_t *p_aout, mtime_t *restrict delay)
199 {
200     aout_sys_t *p_sys = p_aout->sys;
201     uint32_t hal, dsp;
202
203     if (!p_sys->at_getRenderPosition)
204         return -1;
205
206     if (p_sys->at_getRenderPosition(&hal, &dsp, MUSIC))
207         return -1;
208
209     hal = (uint32_t)((uint64_t)hal * p_sys->rate / 44100);
210
211     if (p_sys->samples_written == 0) {
212         p_sys->initial = hal;
213         return -1;
214     }
215
216     hal -= p_sys->initial;
217     if (hal == 0)
218         return -1;
219
220     if (delay)
221         *delay = ((mtime_t)p_sys->samples_written - hal) * CLOCK_FREQ / p_sys->rate;
222
223     return 0;
224 }
225
226 static int Start(audio_output_t *aout, audio_sample_format_t *restrict fmt)
227 {
228     struct aout_sys_t *p_sys = aout->sys;
229
230     int status, size;
231     int afSampleRate, afFrameCount, afLatency, minBufCount, minFrameCount;
232     int stream_type, channel, rate, format;
233
234     /* 4000 <= frequency <= 48000 */
235     rate = fmt->i_rate;
236     if (rate < 4000)
237         rate = 4000;
238     if (rate > 48000)
239         rate = 48000;
240
241     stream_type = MUSIC;
242
243     /* We can only accept U8 and S16N */
244     if (fmt->i_format != VLC_CODEC_U8 && fmt->i_format != VLC_CODEC_S16N)
245         fmt->i_format = VLC_CODEC_S16N;
246     format = (fmt->i_format == VLC_CODEC_S16N) ? PCM_16_BIT : PCM_8_BIT;
247
248     /* TODO: android supports more channels */
249     fmt->i_original_channels = fmt->i_physical_channels;
250     switch(aout_FormatNbChannels(fmt))
251     {
252     case 1:
253         channel = CHANNEL_OUT_MONO;
254         fmt->i_physical_channels = AOUT_CHAN_CENTER;
255         break;
256     case 2:
257     default:
258         channel = CHANNEL_OUT_STEREO;
259         fmt->i_physical_channels = AOUT_CHANS_STEREO;
260         break;
261     }
262
263     /* Get the minimum buffer value */
264     if (!p_sys->at_getMinFrameCount) {
265         status = p_sys->as_getOutputSamplingRate(&afSampleRate, stream_type);
266         status ^= p_sys->as_getOutputFrameCount(&afFrameCount, stream_type);
267         status ^= p_sys->as_getOutputLatency((uint32_t*)(&afLatency), stream_type);
268         if (status != 0) {
269             msg_Err(aout, "Could not query the AudioStream parameters");
270             return VLC_EGENERIC;
271         }
272         minBufCount = afLatency / ((1000 * afFrameCount) / afSampleRate);
273         if (minBufCount < 2)
274             minBufCount = 2;
275         minFrameCount = (afFrameCount * rate * minBufCount) / afSampleRate;
276     }
277     else {
278         status = p_sys->at_getMinFrameCount(&minFrameCount, stream_type, rate);
279         if (status != 0) {
280             msg_Err(aout, "Could not query the AudioTrack parameters");
281             return VLC_EGENERIC;
282         }
283     }
284
285     size = minFrameCount * (channel == CHANNEL_OUT_STEREO ? 2 : 1) * 4;
286
287     /* Sizeof(AudioTrack) == 0x58 (not sure) on 2.2.1, this should be enough */
288     p_sys->AudioTrack = malloc(SIZE_OF_AUDIOTRACK);
289     if (!p_sys->AudioTrack)
290         return VLC_ENOMEM;
291
292     *((uint32_t *) ((uint32_t)p_sys->AudioTrack + SIZE_OF_AUDIOTRACK - 4)) = 0xbaadbaad;
293     // Higher than android 2.2
294     if (p_sys->at_ctor)
295         p_sys->at_ctor(p_sys->AudioTrack, stream_type, rate, format, channel, size, 0, NULL, NULL, 0, 0);
296     // Higher than android 1.6
297     else if (p_sys->at_ctor_legacy)
298         p_sys->at_ctor_legacy(p_sys->AudioTrack, stream_type, rate, format, channel, size, 0, NULL, NULL, 0);
299
300     assert( (*((uint32_t *) ((uint32_t)p_sys->AudioTrack + SIZE_OF_AUDIOTRACK - 4)) == 0xbaadbaad) );
301
302     /* And Init */
303     status = p_sys->at_initCheck(p_sys->AudioTrack);
304
305     /* android 1.6 uses channel count instead of stream_type */
306     if (status != 0) {
307         channel = (channel == CHANNEL_OUT_STEREO) ? 2 : 1;
308         p_sys->at_ctor_legacy(p_sys->AudioTrack, stream_type, rate, format, channel, size, 0, NULL, NULL, 0);
309         status = p_sys->at_initCheck(p_sys->AudioTrack);
310     }
311     if (status != 0) {
312         msg_Err(aout, "Cannot create AudioTrack!");
313         free(p_sys->AudioTrack);
314         return VLC_EGENERIC;
315     }
316
317     aout_SoftVolumeStart(aout);
318
319     aout->sys = p_sys;
320     aout->time_get = NULL;
321     aout->play = Play;
322     aout->pause = Pause;
323     aout->flush = Flush;
324     aout->time_get = TimeGet;
325
326     p_sys->rate = rate;
327     p_sys->samples_written = 0;
328     p_sys->bytes_per_frame = aout_FormatNbChannels(fmt) * (format == PCM_16_BIT) ? 2 : 1;
329
330     p_sys->at_start(p_sys->AudioTrack);
331     TimeGet(aout, NULL); /* Gets the initial value of DAC samples counter */
332
333     fmt->i_rate = rate;
334
335     return VLC_SUCCESS;
336 }
337
338 static void Stop(audio_output_t* p_aout)
339 {
340     aout_sys_t *p_sys = p_aout->sys;
341
342     p_sys->at_stop(p_sys->AudioTrack);
343     p_sys->at_flush(p_sys->AudioTrack);
344     p_sys->at_dtor(p_sys->AudioTrack);
345     free(p_sys->AudioTrack);
346 }
347
348 static void Play(audio_output_t* p_aout, block_t* p_buffer)
349 {
350     aout_sys_t *p_sys = p_aout->sys;
351
352     while (p_buffer->i_buffer) {
353         int ret = p_sys->at_write(p_sys->AudioTrack, p_buffer->p_buffer, p_buffer->i_buffer);
354         if (ret < 0) {
355             msg_Err(p_aout, "Write failed (error %d)", ret);
356             break;
357         }
358
359         p_sys->samples_written += ret / p_sys->bytes_per_frame;
360         p_buffer->p_buffer += ret;
361         p_buffer->i_buffer -= ret;
362     }
363
364     block_Release( p_buffer );
365 }
366
367 static void Pause(audio_output_t *p_aout, bool pause, mtime_t date)
368 {
369     VLC_UNUSED(date);
370
371     aout_sys_t *p_sys = p_aout->sys;
372
373     if (pause) {
374         p_sys->at_pause(p_sys->AudioTrack);
375     } else {
376         p_sys->at_start(p_sys->AudioTrack);
377     }
378 }
379
380 static void Flush (audio_output_t *p_aout, bool wait)
381 {
382     aout_sys_t *p_sys = p_aout->sys;
383     if (wait) {
384         mtime_t delay;
385         if (!TimeGet(p_aout, &delay))
386             msleep(delay);
387     } else {
388         p_sys->at_stop(p_sys->AudioTrack);
389         p_sys->at_flush(p_sys->AudioTrack);
390         p_sys->samples_written = 0;
391         p_sys->at_start(p_sys->AudioTrack);
392     }
393 }
394
395 static int Open(vlc_object_t *obj)
396 {
397     audio_output_t *aout = (audio_output_t *)obj;
398     aout_sys_t *sys = malloc(sizeof (*sys));
399
400     if (unlikely(sys == NULL))
401         return VLC_ENOMEM;
402
403     sys->libmedia = InitLibrary(sys);
404     if (sys->libmedia == NULL) {
405         msg_Err(aout, "Could not initialize libmedia.so!");
406         free(sys);
407         return VLC_EGENERIC;
408     }
409
410     aout->sys = sys;
411     aout->start = Start;
412     aout->stop = Stop;
413     aout_SoftVolumeInit(aout);
414     return VLC_SUCCESS;
415 }
416
417 static void Close(vlc_object_t *obj)
418 {
419     audio_output_t *aout = (audio_output_t *)obj;
420     aout_sys_t *sys = aout->sys;
421
422     dlclose(sys->libmedia);
423     free(sys);
424 }