]> git.sesse.net Git - vlc/blob - modules/audio_output/audioqueue.c
AudioQueue: implement volume support just because we can
[vlc] / modules / audio_output / audioqueue.c
1 /*****************************************************************************
2  * audioqueue.c : AudioQueue audio output plugin for vlc
3  *****************************************************************************
4  * Copyright (C) 2010-2012 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 #define FRAME_SIZE 2048
39
40 /*****************************************************************************
41  * aout_sys_t: AudioQueue audio output method descriptor
42  *****************************************************************************
43  * This structure is part of the audio output thread descriptor.
44  * It describes the specific properties of an audio device.
45  *****************************************************************************/
46 struct aout_sys_t
47 {
48     AudioQueueRef audioQueue;
49     bool          b_stopped;
50     float         f_volume;
51 };
52
53 /*****************************************************************************
54  * Local prototypes
55  *****************************************************************************/
56 static int  Open               (vlc_object_t *);
57 static void Close              (vlc_object_t *);
58 static void Play               (audio_output_t *, block_t *);
59 static void Pause              (audio_output_t *p_aout, bool pause, mtime_t date);
60 static void Flush              (audio_output_t *p_aout, bool wait);
61 static int  TimeGet            (audio_output_t *aout, mtime_t *);
62 static void AudioQueueCallback (void *, AudioQueueRef, AudioQueueBufferRef);
63
64 /*****************************************************************************
65  * Module descriptor
66  *****************************************************************************/
67 vlc_module_begin ()
68 set_shortname("AudioQueue")
69 set_description(N_("AudioQueue (iOS / Mac OS) audio output"))
70 set_capability("audio output", 40)
71 set_category(CAT_AUDIO)
72 set_subcategory(SUBCAT_AUDIO_AOUT)
73 add_shortcut("audioqueue")
74 set_callbacks(Open, Close)
75 vlc_module_end ()
76
77 /*****************************************************************************
78  * Start: open the audio device
79  *****************************************************************************/
80
81 static int Start(audio_output_t *p_aout, audio_sample_format_t *restrict fmt)
82 {
83     aout_sys_t *p_sys = p_aout->sys;
84     OSStatus status = 0;
85
86     // Setup the audio device.
87     AudioStreamBasicDescription deviceFormat;
88     deviceFormat.mSampleRate = 44100;
89     deviceFormat.mFormatID = kAudioFormatLinearPCM;
90     deviceFormat.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger; // Signed integer, little endian
91     deviceFormat.mBytesPerPacket = 4;
92     deviceFormat.mFramesPerPacket = 1;
93     deviceFormat.mBytesPerFrame = 4;
94     deviceFormat.mChannelsPerFrame = 2;
95     deviceFormat.mBitsPerChannel = 16;
96     deviceFormat.mReserved = 0;
97
98     // Create a new output AudioQueue for the device.
99     status = AudioQueueNewOutput(&deviceFormat,         // Format
100                                  AudioQueueCallback,    // Callback
101                                  NULL,                  // User data, passed to the callback
102                                  CFRunLoopGetMain(),    // RunLoop
103                                  kCFRunLoopCommonModes, // RunLoop mode
104                                  0,                     // Flags ; must be zero (per documentation)...
105                                  &(p_sys->audioQueue)); // Output
106
107     msg_Dbg(p_aout, "New AudioQueue output created (status = %i)", status);
108     if (status != noErr)
109         return VLC_EGENERIC;
110
111     fmt->i_format = VLC_CODEC_S16L;
112     fmt->i_physical_channels = AOUT_CHANS_STEREO;
113     fmt->i_rate = 44100;
114     aout_FormatPrepare(fmt);
115
116     p_aout->sys->b_stopped = false;
117
118     status = AudioQueueStart(p_sys->audioQueue, NULL);
119     msg_Dbg(p_aout, "Starting AudioQueue (status = %i)", status);
120
121     p_aout->time_get = TimeGet;
122     p_aout->play = Play;
123     p_aout->pause = Pause;
124     p_aout->flush = Flush;
125
126     if (status != noErr)
127         return VLC_EGENERIC;
128
129     return VLC_SUCCESS;
130 }
131
132 /*****************************************************************************
133  * Stop: close the audio device
134  *****************************************************************************/
135
136 static void Stop (audio_output_t *p_aout)
137 {
138     p_aout->sys->b_stopped = true;
139
140     msg_Dbg(p_aout, "Stopping AudioQueue");
141     AudioQueueStop(p_aout->sys->audioQueue, true);
142     msg_Dbg(p_aout, "Disposing AudioQueue");
143     AudioQueueDispose(p_aout->sys->audioQueue, true);
144 }
145
146 /*****************************************************************************
147  * actual playback
148  *****************************************************************************/
149
150 static void Play (audio_output_t *p_aout, block_t *p_block)
151 {
152     if (p_block != NULL) {
153         AudioQueueBufferRef inBuffer = NULL;
154         OSStatus status;
155
156         status = AudioQueueAllocateBuffer(p_aout->sys->audioQueue, FRAME_SIZE * 2, &inBuffer);
157         if (status != noErr) {
158             msg_Err(p_aout, "buffer alloction failed (%i)", status);
159             return;
160         }
161
162         memcpy(inBuffer->mAudioData, p_block->p_buffer, p_block->i_buffer);
163         inBuffer->mAudioDataByteSize = p_block->i_buffer;
164         block_Release(p_block);
165
166         status = AudioQueueEnqueueBuffer(p_aout->sys->audioQueue, inBuffer, 0, NULL);
167         if (status != noErr)
168             msg_Err(p_aout, "enqueuing buffer failed (%i)", status);
169     }
170 }
171
172 void AudioQueueCallback(void * inUserData, AudioQueueRef inAQ, AudioQueueBufferRef inBuffer) {
173     /* this function does nothing, but needs to be here to make the AudioQueue API happy.
174      * without a callback, it will refuse to create an AudioQueue instance. */
175     VLC_UNUSED(inUserData);
176     VLC_UNUSED(inAQ);
177     VLC_UNUSED(inBuffer);
178 }
179
180 static void Pause (audio_output_t *p_aout, bool pause, mtime_t date)
181 {
182     VLC_UNUSED(date);
183
184     if (pause)
185         AudioQueuePause(p_aout->sys->audioQueue);
186     else
187         AudioQueueStart(p_aout->sys->audioQueue, NULL);
188 }
189
190 static void Flush (audio_output_t *p_aout, bool wait)
191 {
192     if (p_aout->sys->b_stopped || !p_aout->sys->audioQueue)
193         return;
194
195     if (wait)
196         AudioQueueFlush(p_aout->sys->audioQueue);
197     else
198         AudioQueueReset(p_aout->sys->audioQueue);
199 }
200
201 static int TimeGet (audio_output_t *p_aout, mtime_t *restrict delay)
202 {
203     // TODO
204
205     VLC_UNUSED(p_aout);
206     VLC_UNUSED(delay);
207
208     return -1;
209 }
210
211 /*****************************************************************************
212  * Module management
213  *****************************************************************************/
214
215 static int VolumeSet(audio_output_t * p_aout, float volume)
216 {
217     struct aout_sys_t *p_sys = p_aout->sys;
218     OSStatus ostatus;
219
220     aout_VolumeReport(p_aout, volume);
221     p_sys->f_volume = volume;
222
223     /* Set volume for output unit */
224     ostatus = AudioQueueSetParameter(p_sys->audioQueue, kAudioQueueParam_Volume, volume * volume * volume);
225
226     return ostatus;
227 }
228
229 /*****************************************************************************
230  * Module management
231  *****************************************************************************/
232
233 static int Open(vlc_object_t *obj)
234 {
235     audio_output_t *aout = (audio_output_t *)obj;
236     aout_sys_t *sys = malloc(sizeof (*sys));
237
238     if (unlikely(sys == NULL))
239         return VLC_ENOMEM;
240
241     aout->sys = sys;
242     aout->start = Start;
243     aout->stop = Stop;
244     aout->volume_set = VolumeSet;
245
246     /* reset volume */
247     aout_VolumeReport(aout, 1.0);
248
249     return VLC_SUCCESS;
250 }
251
252 static void Close(vlc_object_t *obj)
253 {
254     audio_output_t *aout = (audio_output_t *)obj;
255     msg_Dbg( aout, "audioqueue: Close");
256     aout_sys_t *sys = aout->sys;
257
258     free(sys);
259 }