]> git.sesse.net Git - vlc/blob - modules/gui/macosx/aout.m
* Added a third argument to aout_OutputNextBuffer. In case the buffer
[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.4 2002/08/14 00:43:52 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  *
11  * This program is free software; you can redistribute it and/or modify
12  * it under the terms of the GNU General Public License as published by
13  * the Free Software Foundation; either version 2 of the License, or
14  * (at your option) any later version.
15  * 
16  * This program is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19  * GNU General Public License for more details.
20  *
21  * You should have received a copy of the GNU General Public License
22  * along with this program; if not, write to the Free Software
23  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
24  *****************************************************************************/
25
26 /*****************************************************************************
27  * Preamble
28  *****************************************************************************/
29 #include <string.h>
30 #include <stdlib.h>
31
32 #include <vlc/vlc.h>
33 #include <vlc/aout.h>
34 #include "aout_internal.h"
35
36 #include <Carbon/Carbon.h>
37 #include <CoreAudio/AudioHardware.h>
38 #include <CoreAudio/HostTime.h>
39 #include <AudioToolbox/AudioConverter.h>
40
41 /*****************************************************************************
42  * aout_sys_t: private audio output method descriptor
43  *****************************************************************************
44  * This structure is part of the audio output thread descriptor.
45  * It describes the CoreAudio specific properties of an output thread.
46  *****************************************************************************/
47 struct aout_sys_t
48 {
49     AudioDeviceID       device;         // the audio device
50
51     AudioStreamBasicDescription stream_format;
52
53     UInt32              i_buffer_size;  // audio device buffer size
54     mtime_t             clock_diff;
55 };
56
57 /*****************************************************************************
58  * Local prototypes.
59  *****************************************************************************/
60 static int      SetFormat       ( aout_instance_t *p_aout );
61 static void     Play            ( aout_instance_t *p_aout,
62                                   aout_buffer_t *p_buffer );
63
64 static OSStatus IOCallback      ( AudioDeviceID inDevice,
65                                   const AudioTimeStamp *inNow, 
66                                   const void *inInputData, 
67                                   const AudioTimeStamp *inInputTime,
68                                   AudioBufferList *outOutputData, 
69                                   const AudioTimeStamp *inOutputTime, 
70                                   void *threadGlobals );
71
72 /*****************************************************************************
73  * Open: open a CoreAudio HAL device
74  *****************************************************************************/
75 int E_(OpenAudio)( vlc_object_t * p_this )
76 {
77     OSStatus err;
78     UInt32 i_param_size;
79     aout_instance_t * p_aout = (aout_instance_t *)p_this;
80     struct aout_sys_t * p_sys;
81
82     /* Allocate instance */
83     p_sys = p_aout->output.p_sys = malloc( sizeof( struct aout_sys_t ) );
84     memset( p_sys, 0, sizeof( struct aout_sys_t ) );
85     if( p_aout->output.p_sys == NULL )
86     {
87         msg_Err( p_aout, "out of memory" );
88         return( 1 );
89     }
90
91     /* Get the default output device */
92     /* FIXME : be more clever in choosing from several devices */
93     i_param_size = sizeof( p_sys->device );
94     err = AudioHardwareGetProperty( kAudioHardwarePropertyDefaultOutputDevice,
95                                     &i_param_size, 
96                                     (void *)&p_sys->device );
97     if( err != noErr ) 
98     {
99         msg_Err( p_aout, "failed to get the device: %d", err );
100         return( -1 );
101     }
102
103     p_aout->output.pf_setformat = SetFormat;
104     p_aout->output.pf_play = Play;
105
106     return 0;
107 }
108
109 /*****************************************************************************
110  * SetFormat: find the closest available format from p_format
111  *****************************************************************************/
112 static int SetFormat( aout_instance_t * p_aout )
113 {
114     struct aout_sys_t * p_sys = p_aout->output.p_sys;
115     OSErr err;
116
117     /* Get a description of the data format used by the device */
118     UInt32 i_param_size = sizeof( p_sys->stream_format ); 
119     err = AudioDeviceGetProperty( p_sys->device, 0, false, 
120                                   kAudioDevicePropertyStreamFormat, 
121                                   &i_param_size,
122                                   &p_sys->stream_format );
123     if( err != noErr )
124     {
125         msg_Err( p_aout, "failed to get stream format: %d", err );
126         return -1 ;
127     }
128
129     if( p_sys->stream_format.mFormatID != kAudioFormatLinearPCM )
130     {
131         msg_Err( p_aout, "kAudioFormatLinearPCM required" );
132         return -1 ;
133     }
134
135     /* We only deal with floats */
136     if ( p_aout->output.output.i_format != AOUT_FMT_FLOAT32 )
137     {
138         msg_Err( p_aout, "cannot set format 0x%x",
139                  p_aout->output.output.i_format );
140         return -1;
141     }
142     p_sys->stream_format.mFormatFlags |=
143         kLinearPCMFormatFlagIsFloat;
144
145     /* Set sample rate and channels per frame */
146     p_sys->stream_format.mSampleRate
147                  = p_aout->output.output.i_rate; 
148     p_sys->stream_format.mChannelsPerFrame
149                  = p_aout->output.output.i_channels;
150
151     /* Get the buffer size that the device uses for IO */
152     i_param_size = sizeof( p_sys->i_buffer_size );
153 #if 0
154     err = AudioDeviceGetProperty( p_sys->device, 0, false, 
155                                   kAudioDevicePropertyBufferSize, 
156                                   &i_param_size, &p_sys->i_buffer_size );
157 msg_Dbg( p_aout, "toto : %d", p_sys->i_buffer_size );
158 #else
159     p_sys->i_buffer_size = sizeof(float) * p_aout->output.output.i_channels
160                             * 4096;
161     err = AudioDeviceSetProperty( p_sys->device, 0, 0, false,
162                                   kAudioDevicePropertyBufferSize,
163                                   i_param_size, &p_sys->i_buffer_size );
164 #endif
165     if( err != noErr )
166     {
167         msg_Err( p_aout, "failed to set device buffer size: %d", err );
168         return( -1 );
169     }
170
171     p_aout->output.i_nb_samples = p_sys->i_buffer_size / sizeof(float)
172                                    / p_aout->output.output.i_channels;
173
174     /* Add callback */
175     err = AudioDeviceAddIOProc( p_sys->device,
176                                 (AudioDeviceIOProc)IOCallback,
177                                 (void *)p_aout );
178
179     /* Open the output with callback IOCallback */
180     err = AudioDeviceStart( p_sys->device,
181                             (AudioDeviceIOProc)IOCallback );
182     if( err != noErr )
183     {
184         msg_Err( p_aout, "AudioDeviceStart failed: %d", err );
185         return -1;
186     }
187
188     /* Let's pray for the following operation to be atomic... */
189     p_sys->clock_diff = mdate()
190          - AudioConvertHostTimeToNanos(AudioGetCurrentHostTime()) / 1000;
191
192     return 0;
193 }
194
195 /*****************************************************************************
196  * Close: close the CoreAudio HAL device
197  *****************************************************************************/
198 void E_(CloseAudio)( aout_instance_t * p_aout )
199 {
200     struct aout_sys_t * p_sys = p_aout->output.p_sys;
201     OSStatus err; 
202
203     /* Stop playing sound through the device */
204     err = AudioDeviceStop( p_sys->device,
205                            (AudioDeviceIOProc)IOCallback ); 
206     if( err != noErr )
207     {
208         msg_Err( p_aout, "AudioDeviceStop failed: %d", err );
209     }
210
211     free( p_sys );
212 }
213
214 /*****************************************************************************
215  * Play: queue a buffer for playing by IOCallback
216  *****************************************************************************/
217 static void Play( aout_instance_t * p_aout, aout_buffer_t * p_buffer )
218 {
219     aout_FifoPush( p_aout, &p_aout->output.fifo, p_buffer );
220 }
221
222 /*****************************************************************************
223  * IOCallback : callback for audio output
224  *****************************************************************************/
225 static OSStatus IOCallback( AudioDeviceID inDevice,
226                             const AudioTimeStamp *inNow, 
227                             const void *inInputData,
228                             const AudioTimeStamp *inInputTime, 
229                             AudioBufferList *outOutputData,
230                             const AudioTimeStamp *inOutputTime, 
231                             void *threadGlobals )
232 {
233     aout_instance_t * p_aout = (aout_instance_t *)threadGlobals;
234     struct aout_sys_t * p_sys = p_aout->output.p_sys;
235     mtime_t         current_date;
236     AudioTimeStamp  host_time;
237     aout_buffer_t * p_buffer;
238
239     host_time.mFlags = kAudioTimeStampHostTimeValid;
240     AudioDeviceTranslateTime( inDevice, inOutputTime, &host_time );
241     current_date = p_sys->clock_diff
242                  + AudioConvertHostTimeToNanos(host_time.mHostTime) / 1000;
243
244     p_buffer = aout_OutputNextBuffer( p_aout, current_date, 0 );
245
246     /* move data into output data buffer */
247     if ( p_buffer != NULL )
248     {
249         BlockMoveData( p_buffer->p_buffer,
250                        outOutputData->mBuffers[ 0 ].mData, 
251                        p_sys->i_buffer_size );
252         aout_BufferFree( p_buffer );
253     }
254     else
255     {
256         memset(outOutputData->mBuffers[ 0 ].mData, 0, p_sys->i_buffer_size);
257     }
258
259     return noErr;     
260 }
261