]> git.sesse.net Git - vlc/blob - modules/audio_output/audioqueue.c
audioqueue: feed the decoder's sample rate to the OS and enforce FL32 instead of...
[vlc] / modules / audio_output / audioqueue.c
1 /*****************************************************************************
2  * audioqueue.c : AudioQueue audio output plugin for vlc
3  *****************************************************************************
4  * Copyright (C) 2010-2013 VLC authors and VideoLAN
5  * $Id$
6  *
7  * Authors: Romain Goyet <romain.goyet@likid.org>
8  *          Felix Paul Kühne <fkuehne@videolan.org>
9  *
10  * This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation; either version 2 of the License, or
13  * (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, write to the Free Software
22  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
23  *****************************************************************************/
24
25 /*****************************************************************************
26  * Preamble
27  *****************************************************************************/
28 #ifdef HAVE_CONFIG_H
29 # include "config.h"
30 #endif
31
32 #include <vlc_common.h>
33 #include <vlc_plugin.h>
34 #include <vlc_aout.h>
35
36 #include <AudioToolBox/AudioToolBox.h>
37
38 /*****************************************************************************
39  * aout_sys_t: AudioQueue audio output method descriptor
40  *****************************************************************************
41  * This structure is part of the audio output thread descriptor.
42  * It describes the specific properties of an audio device.
43  *****************************************************************************/
44 struct aout_sys_t
45 {
46     AudioQueueRef audioQueue;
47     bool          b_stopped;
48     float         f_volume;
49 };
50
51 /*****************************************************************************
52  * Local prototypes
53  *****************************************************************************/
54 static int  Open               (vlc_object_t *);
55 static void Close              (vlc_object_t *);
56 static void Play               (audio_output_t *, block_t *);
57 static void Pause              (audio_output_t *p_aout, bool pause, mtime_t date);
58 static void Flush              (audio_output_t *p_aout, bool wait);
59 static int  TimeGet            (audio_output_t *aout, mtime_t *);
60 static void AudioQueueCallback (void *, AudioQueueRef, AudioQueueBufferRef);
61
62 /*****************************************************************************
63  * Module descriptor
64  *****************************************************************************/
65 vlc_module_begin ()
66 set_shortname("AudioQueue")
67 set_description(N_("AudioQueue (iOS / Mac OS) audio output"))
68 set_capability("audio output", 40)
69 set_category(CAT_AUDIO)
70 set_subcategory(SUBCAT_AUDIO_AOUT)
71 add_shortcut("audioqueue")
72 set_callbacks(Open, Close)
73 vlc_module_end ()
74
75 /*****************************************************************************
76  * Start: open the audio device
77  *****************************************************************************/
78
79 static int Start(audio_output_t *p_aout, audio_sample_format_t *restrict fmt)
80 {
81     aout_sys_t *p_sys = p_aout->sys;
82     OSStatus status = 0;
83
84     // Setup the audio device.
85     AudioStreamBasicDescription deviceFormat;
86     deviceFormat.mSampleRate = fmt->i_rate;
87     deviceFormat.mFormatID = kAudioFormatLinearPCM;
88     deviceFormat.mFormatFlags = kAudioFormatFlagsNativeFloatPacked; // FL32
89     deviceFormat.mFramesPerPacket = 1;
90     deviceFormat.mChannelsPerFrame = 2;
91     deviceFormat.mBitsPerChannel = 32;
92     deviceFormat.mBytesPerFrame = deviceFormat.mBitsPerChannel * deviceFormat.mChannelsPerFrame / 8;
93     deviceFormat.mBytesPerPacket = deviceFormat.mBytesPerFrame * deviceFormat.mFramesPerPacket;
94
95     // Create a new output AudioQueue for the device.
96     status = AudioQueueNewOutput(&deviceFormat,         // Format
97                                  AudioQueueCallback,    // Callback
98                                  NULL,                  // User data, passed to the callback
99                                  NULL,                  // RunLoop
100                                  kCFRunLoopCommonModes, // RunLoop mode
101                                  0,                     // Flags ; must be zero (per documentation)...
102                                  &(p_sys->audioQueue)); // Output
103
104     msg_Dbg(p_aout, "New AudioQueue output created (status = %li)", status);
105     if (status != noErr)
106         return VLC_EGENERIC;
107
108     fmt->i_format = VLC_CODEC_FL32;
109     fmt->i_physical_channels = AOUT_CHANS_STEREO;
110     aout_FormatPrepare(fmt);
111
112     p_aout->sys->b_stopped = false;
113
114     status = AudioQueueStart(p_sys->audioQueue, NULL);
115     msg_Dbg(p_aout, "Starting AudioQueue (status = %li)", status);
116
117     p_aout->time_get = TimeGet;
118     p_aout->play = Play;
119     p_aout->pause = Pause;
120     p_aout->flush = Flush;
121
122     if (status != noErr)
123         return VLC_EGENERIC;
124
125     return VLC_SUCCESS;
126 }
127
128 /*****************************************************************************
129  * Stop: close the audio device
130  *****************************************************************************/
131
132 static void Stop (audio_output_t *p_aout)
133 {
134     p_aout->sys->b_stopped = true;
135
136     msg_Dbg(p_aout, "Stopping AudioQueue");
137     AudioQueueStop(p_aout->sys->audioQueue, true);
138     msg_Dbg(p_aout, "Disposing AudioQueue");
139     AudioQueueDispose(p_aout->sys->audioQueue, true);
140 }
141
142 /*****************************************************************************
143  * actual playback
144  *****************************************************************************/
145
146 static void Play (audio_output_t *p_aout, block_t *p_block)
147 {
148     if (p_block != NULL) {
149         AudioQueueBufferRef inBuffer = NULL;
150         OSStatus status;
151
152         status = AudioQueueAllocateBuffer(p_aout->sys->audioQueue, p_block->i_buffer, &inBuffer);
153         if (status != noErr) {
154             msg_Err(p_aout, "buffer alloction failed (%li)", status);
155             return;
156         }
157
158         memcpy(inBuffer->mAudioData, p_block->p_buffer, p_block->i_buffer);
159         inBuffer->mAudioDataByteSize = p_block->i_buffer;
160         block_Release(p_block);
161
162         status = AudioQueueEnqueueBuffer(p_aout->sys->audioQueue, inBuffer, 0, NULL);
163         if (status != noErr)
164             msg_Err(p_aout, "enqueuing buffer failed (%li)", status);
165     }
166 }
167
168 void AudioQueueCallback(void * inUserData, AudioQueueRef inAQ, AudioQueueBufferRef inBuffer) {
169     /* this function does nothing, but needs to be here to make the AudioQueue API happy.
170      * without a callback, it will refuse to create an AudioQueue instance. */
171     VLC_UNUSED(inUserData);
172     VLC_UNUSED(inAQ);
173     VLC_UNUSED(inBuffer);
174 }
175
176 static void Pause (audio_output_t *p_aout, bool pause, mtime_t date)
177 {
178     VLC_UNUSED(date);
179
180     if (pause)
181         AudioQueuePause(p_aout->sys->audioQueue);
182     else
183         AudioQueueStart(p_aout->sys->audioQueue, NULL);
184 }
185
186 static void Flush (audio_output_t *p_aout, bool wait)
187 {
188     if (p_aout->sys->b_stopped || !p_aout->sys->audioQueue)
189         return;
190
191     if (wait)
192         AudioQueueFlush(p_aout->sys->audioQueue);
193     else
194         AudioQueueReset(p_aout->sys->audioQueue);
195 }
196
197 static int TimeGet (audio_output_t *p_aout, mtime_t *restrict delay)
198 {
199     // TODO
200
201     VLC_UNUSED(p_aout);
202     VLC_UNUSED(delay);
203
204     return -1;
205 }
206
207 /*****************************************************************************
208  * Module management
209  *****************************************************************************/
210
211 static int VolumeSet(audio_output_t * p_aout, float volume)
212 {
213     struct aout_sys_t *p_sys = p_aout->sys;
214     OSStatus ostatus;
215
216     aout_VolumeReport(p_aout, volume);
217     p_sys->f_volume = volume;
218
219     /* Set volume for output unit */
220     ostatus = AudioQueueSetParameter(p_sys->audioQueue, kAudioQueueParam_Volume, volume * volume * volume);
221
222     return ostatus;
223 }
224
225 /*****************************************************************************
226  * Module management
227  *****************************************************************************/
228
229 static int Open(vlc_object_t *obj)
230 {
231     audio_output_t *aout = (audio_output_t *)obj;
232     aout_sys_t *sys = malloc(sizeof (*sys));
233
234     if (unlikely(sys == NULL))
235         return VLC_ENOMEM;
236
237     aout->sys = sys;
238     aout->start = Start;
239     aout->stop = Stop;
240     aout->volume_set = VolumeSet;
241
242     /* reset volume */
243     aout_VolumeReport(aout, 1.0);
244
245     return VLC_SUCCESS;
246 }
247
248 static void Close(vlc_object_t *obj)
249 {
250     audio_output_t *aout = (audio_output_t *)obj;
251     msg_Dbg( aout, "audioqueue: Close");
252     aout_sys_t *sys = aout->sys;
253
254     free(sys);
255 }