]> git.sesse.net Git - vlc/blob - modules/gui/macosx/aout.m
* Avoid overflowing the message queue with "audio output is starving"
[vlc] / modules / gui / macosx / aout.m
1 /*****************************************************************************
2  * aout.m: CoreAudio output plugin
3  *****************************************************************************
4  * Copyright (C) 2002 VideoLAN
5  * $Id: aout.m,v 1.13 2002/10/21 20:00:09 massiot Exp $
6  *
7  * Authors: Colin Delacroix <colin@zoy.org>
8  *          Jon Lech Johansen <jon-vl@nanocrew.net>
9  *          Christophe Massiot <massiot@via.ecp.fr>
10  *          Heiko Panther <heiko.panther@web.de>
11  *
12  * This program is free software; you can redistribute it and/or modify
13  * it under the terms of the GNU General Public License as published by
14  * the Free Software Foundation; either version 2 of the License, or
15  * (at your option) any later version.
16  * 
17  * This program is distributed in the hope that it will be useful,
18  * but WITHOUT ANY WARRANTY; without even the implied warranty of
19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20  * GNU General Public License for more details.
21  *
22  * You should have received a copy of the GNU General Public License
23  * along with this program; if not, write to the Free Software
24  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
25  *****************************************************************************/
26
27 /*****************************************************************************
28  * Preamble
29  *****************************************************************************/
30 #include <string.h>
31 #include <stdlib.h>
32
33 #include <vlc/vlc.h>
34 #include <vlc/aout.h>
35 #include "aout_internal.h"
36 #include "asystm.h"
37
38 #include <Carbon/Carbon.h>
39 #include <CoreAudio/AudioHardware.h>
40 #include <CoreAudio/HostTime.h>
41 #include <AudioToolbox/AudioConverter.h>
42
43 #define A52_FRAME_NB 1536
44
45 /*****************************************************************************
46  * aout_sys_t: private audio output method descriptor
47  *****************************************************************************
48  * This structure is part of the audio output thread descriptor.
49  * It describes the CoreAudio specific properties of an output thread.
50  *****************************************************************************/
51 struct aout_sys_t
52 {
53     AudioDeviceID       device;         // the audio device
54
55     AudioStreamBasicDescription stream_format;
56
57     UInt32              i_buffer_size;  // audio device buffer size
58     mtime_t             clock_diff;
59 };
60
61 /*****************************************************************************
62  * Local prototypes.
63  *****************************************************************************/
64 static void     Play            ( aout_instance_t *p_aout );
65
66 static OSStatus IOCallback      ( AudioDeviceID inDevice,
67                                   const AudioTimeStamp *inNow, 
68                                   const void *inInputData, 
69                                   const AudioTimeStamp *inInputTime,
70                                   AudioBufferList *outOutputData, 
71                                   const AudioTimeStamp *inOutputTime, 
72                                   void *threadGlobals );
73
74 /*****************************************************************************
75  * Open: open a CoreAudio HAL device
76  *****************************************************************************/
77 extern MacOSXAudioSystem *gTheMacOSXAudioSystem; // Remove this global, access audio system froma aout some other way
78
79 int E_(OpenAudio)( vlc_object_t * p_this )
80 {
81     OSStatus err;
82     UInt32 i_param_size;
83     aout_instance_t * p_aout = (aout_instance_t *)p_this;
84     struct aout_sys_t * p_sys;
85     msg_Dbg(p_aout, "************************* ENTER OpenAudio ****************************");
86     
87     /* Allocate instance */
88     p_sys = p_aout->output.p_sys = malloc( sizeof( struct aout_sys_t ) );
89     memset( p_sys, 0, sizeof( struct aout_sys_t ) );
90     if( p_aout->output.p_sys == NULL )
91     {
92         msg_Err( p_aout, "out of memory" );
93         return( 1 );
94     }
95
96     /* Get the default output device */
97     // We now ask the GUI for the selected device
98     p_sys->device=[gTheMacOSXAudioSystem getSelectedDeviceSetToRate:p_aout->output.output.i_rate];
99     if(p_sys->device==0)
100     {
101         msg_Err( p_aout, "couldn't get output device");
102         return( -1 );
103     }
104     msg_Dbg(p_aout, "device returned: %ld", p_sys->device);
105
106     p_aout->output.pf_play = Play;
107     aout_VolumeSoftInit( p_aout );
108
109     /* Get a description of the data format used by the device */
110     i_param_size = sizeof(AudioStreamBasicDescription); 
111     err = AudioDeviceGetProperty(p_sys->device, 0, false, kAudioDevicePropertyStreamFormat, &i_param_size, &p_sys->stream_format );
112     if( err != noErr )
113     {
114         msg_Err( p_aout, "failed to get stream format: %4.4s", &err );
115         return -1 ;
116     }
117
118     msg_Dbg( p_aout, "mSampleRate %ld, mFormatID %4.4s, mFormatFlags %ld, mBytesPerPacket %ld, mFramesPerPacket %ld, mBytesPerFrame %ld, mChannelsPerFrame %ld, mBitsPerChannel %ld",
119            (UInt32)p_sys->stream_format.mSampleRate, &p_sys->stream_format.mFormatID,
120            p_sys->stream_format.mFormatFlags, p_sys->stream_format.mBytesPerPacket,
121            p_sys->stream_format.mFramesPerPacket, p_sys->stream_format.mBytesPerFrame,
122            p_sys->stream_format.mChannelsPerFrame, p_sys->stream_format.mBitsPerChannel );
123
124     msg_Dbg( p_aout, "vlc format %4.4s, mac output format '%4.4s'",
125              (char *)&p_aout->output.output.i_format, &p_sys->stream_format.mFormatID );
126
127     switch(p_sys->stream_format.mFormatID)
128     {
129     case 0:
130     case kAudioFormatLinearPCM:
131         p_aout->output.output.i_format = VLC_FOURCC('f','l','3','2');
132         if ( p_sys->stream_format.mChannelsPerFrame < 6 )
133             p_aout->output.output.i_channels = AOUT_CHAN_STEREO;
134         else
135             p_aout->output.output.i_channels = AOUT_CHAN_3F2R | AOUT_CHAN_LFE;
136         break;
137
138     case kAudioFormat60958AC3:
139     case 'IAC3':
140         p_aout->output.output.i_format = VLC_FOURCC('s','p','d','i');
141         //not necessary, use the input's format by default --Meuuh
142         //p_aout->output.output.i_channels = AOUT_CHAN_DOLBY | AOUT_CHAN_LFE;
143         p_aout->output.output.i_bytes_per_frame = AOUT_SPDIF_SIZE; //p_sys->stream_format.mBytesPerFrame;
144         p_aout->output.output.i_frame_length = A52_FRAME_NB; //p_sys->stream_format.mFramesPerPacket;
145         break;
146
147     default:
148         msg_Err( p_aout, "Unknown hardware format '%4.4s'. Go ask Heiko.", &p_sys->stream_format.mFormatID );
149         return -1;
150     }
151
152     /* Set sample rate and channels per frame */
153     p_aout->output.output.i_rate = p_sys->stream_format.mSampleRate;
154
155     /* Get the buffer size that the device uses for IO */
156     i_param_size = sizeof( p_sys->i_buffer_size );
157 #if 1   // i have a feeling we should use the buffer size imposed by the AC3 device (usually about 6144)
158     err = AudioDeviceGetProperty( p_sys->device, 1, false, 
159                                   kAudioDevicePropertyBufferSize, 
160                                   &i_param_size, &p_sys->i_buffer_size );
161     if(err) {
162         msg_Err(p_aout, "failed to get buffer size - err %4.4s, device %ld", &err, p_sys->device);
163         return -1;
164     }
165     else msg_Dbg( p_aout, "native buffer Size: %d", p_sys->i_buffer_size );
166 #else
167     p_sys->i_buffer_size = p_aout->output.output.i_bytes_per_frame;
168     err = AudioDeviceSetProperty( p_sys->device, 0, 1, false,
169                                   kAudioDevicePropertyBufferSize,
170                                   i_param_size, &p_sys->i_buffer_size );
171     if( err != noErr )
172     {
173         msg_Err( p_aout, "failed to set device buffer size: %4.4s", err );
174         return( -1 );
175     }
176     else msg_Dbg(p_aout, "bufferSize set to %d", p_sys->i_buffer_size);
177 #endif
178
179     p_aout->output.i_nb_samples = p_sys->i_buffer_size / p_sys->stream_format.mBytesPerFrame;
180
181     /* Add callback */
182     err = AudioDeviceAddIOProc( p_sys->device,
183                                 (AudioDeviceIOProc)IOCallback,
184                                 (void *)p_aout );
185
186     /* Open the output with callback IOCallback */
187     err = AudioDeviceStart( p_sys->device,
188                             (AudioDeviceIOProc)IOCallback );
189     if( err != noErr )
190     {
191         msg_Err( p_aout, "AudioDeviceStart failed: %d", err );
192         return -1;
193     }
194
195     /* Let's pray for the following operation to be atomic... */
196     p_sys->clock_diff = - (mtime_t)AudioConvertHostTimeToNanos(
197                                  AudioGetCurrentHostTime()) / 1000;
198     p_sys->clock_diff += mdate();
199
200     return 0;
201 }
202
203 /*****************************************************************************
204  * Close: close the CoreAudio HAL device
205  *****************************************************************************/
206 void E_(CloseAudio)( aout_instance_t * p_aout )
207 {
208     struct aout_sys_t * p_sys = p_aout->output.p_sys;
209     OSStatus err; 
210
211     /* Stop playing sound through the device */
212     err = AudioDeviceStop( p_sys->device,
213                            (AudioDeviceIOProc)IOCallback ); 
214     if( err != noErr )
215     {
216         msg_Err( p_aout, "AudioDeviceStop failed: %d", err );
217     }
218
219     free( p_sys );
220 }
221
222 /*****************************************************************************
223  * Play: nothing to do
224  *****************************************************************************/
225 static void Play( aout_instance_t * p_aout )
226 {
227 }
228
229 /*****************************************************************************
230  * IOCallback : callback for audio output
231  *****************************************************************************/
232 static OSStatus IOCallback( AudioDeviceID inDevice,
233                             const AudioTimeStamp *inNow, 
234                             const void *inInputData,
235                             const AudioTimeStamp *inInputTime, 
236                             AudioBufferList *outOutputData,
237                             const AudioTimeStamp *inOutputTime, 
238                             void *threadGlobals )
239 {
240     aout_instance_t * p_aout = (aout_instance_t *)threadGlobals;
241     struct aout_sys_t * p_sys = p_aout->output.p_sys;
242     mtime_t         current_date;
243     AudioTimeStamp  host_time;
244     aout_buffer_t * p_buffer;
245
246     host_time.mFlags = kAudioTimeStampHostTimeValid;
247     AudioDeviceTranslateTime( inDevice, inOutputTime, &host_time );
248     current_date = p_sys->clock_diff
249                  + AudioConvertHostTimeToNanos(host_time.mHostTime) / 1000;
250
251 //    msg_Dbg(p_aout, "Now fetching audio data");
252     p_buffer = aout_OutputNextBuffer( p_aout, current_date, (p_aout->output.output.i_format == VLC_FOURCC('s','p','d','i')) );
253
254     /* move data into output data buffer */
255     if ( p_buffer != NULL )
256     {
257         BlockMoveData( p_buffer->p_buffer,
258                        outOutputData->mBuffers[ 0 ].mData, 
259                        p_sys->i_buffer_size );
260
261 //      msg_Dbg(p_aout, "This buffer has %d bytes, i take %d", p_buffer->i_nb_bytes, p_sys->i_buffer_size);
262     
263         aout_BufferFree( p_buffer );
264     }
265     else
266     {
267         memset(outOutputData->mBuffers[ 0 ].mData, 0, p_sys->i_buffer_size);
268     }
269
270     return noErr;     
271 }
272