]> git.sesse.net Git - vlc/blob - modules/gui/macosx/aout.m
* ALL: More hooks for audio volume management.
[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.10 2002/09/18 21:21:24 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 void     Play            ( aout_instance_t *p_aout );
61
62 static OSStatus IOCallback      ( AudioDeviceID inDevice,
63                                   const AudioTimeStamp *inNow, 
64                                   const void *inInputData, 
65                                   const AudioTimeStamp *inInputTime,
66                                   AudioBufferList *outOutputData, 
67                                   const AudioTimeStamp *inOutputTime, 
68                                   void *threadGlobals );
69
70 /*****************************************************************************
71  * Open: open a CoreAudio HAL device
72  *****************************************************************************/
73 int E_(OpenAudio)( vlc_object_t * p_this )
74 {
75     OSStatus err;
76     UInt32 i_param_size;
77     aout_instance_t * p_aout = (aout_instance_t *)p_this;
78     struct aout_sys_t * p_sys;
79
80     /* Allocate instance */
81     p_sys = p_aout->output.p_sys = malloc( sizeof( struct aout_sys_t ) );
82     memset( p_sys, 0, sizeof( struct aout_sys_t ) );
83     if( p_aout->output.p_sys == NULL )
84     {
85         msg_Err( p_aout, "out of memory" );
86         return( 1 );
87     }
88
89     /* Get the default output device */
90     /* FIXME : be more clever in choosing from several devices */
91     i_param_size = sizeof( p_sys->device );
92     err = AudioHardwareGetProperty( kAudioHardwarePropertyDefaultOutputDevice,
93                                     &i_param_size, 
94                                     (void *)&p_sys->device );
95     if( err != noErr ) 
96     {
97         msg_Err( p_aout, "failed to get the device: %d", err );
98         return( -1 );
99     }
100
101     p_aout->output.pf_play = Play;
102     aout_VolumeSoftInit( p_aout );
103
104     /* Get a description of the data format used by the device */
105     i_param_size = sizeof( p_sys->stream_format ); 
106     err = AudioDeviceGetProperty( p_sys->device, 0, false, 
107                                   kAudioDevicePropertyStreamFormat, 
108                                   &i_param_size,
109                                   &p_sys->stream_format );
110     if( err != noErr )
111     {
112         msg_Err( p_aout, "failed to get stream format: %d", err );
113         return -1 ;
114     }
115
116     if( p_sys->stream_format.mFormatID != kAudioFormatLinearPCM )
117     {
118         msg_Err( p_aout, "kAudioFormatLinearPCM required" );
119         return -1 ;
120     }
121
122     /* We only deal with floats. FIXME : this is where we should do S/PDIF. */
123     p_aout->output.output.i_format = AOUT_FMT_FLOAT32;
124
125     /* Set sample rate and channels per frame */
126     p_aout->output.output.i_rate = p_sys->stream_format.mSampleRate;
127     /* FIXME : this is where we should ask for downmixing. */
128     p_aout->output.output.i_channels = 2; //p_sys->stream_format.mChannelsPerFrame;
129
130     /* Get the buffer size that the device uses for IO */
131     i_param_size = sizeof( p_sys->i_buffer_size );
132 #if 0
133     err = AudioDeviceGetProperty( p_sys->device, 0, false, 
134                                   kAudioDevicePropertyBufferSize, 
135                                   &i_param_size, &p_sys->i_buffer_size );
136 msg_Dbg( p_aout, "toto : %d", p_sys->i_buffer_size );
137 #else
138     p_sys->i_buffer_size = sizeof(float) * p_aout->output.output.i_channels
139                             * 4096;
140     err = AudioDeviceSetProperty( p_sys->device, 0, 0, false,
141                                   kAudioDevicePropertyBufferSize,
142                                   i_param_size, &p_sys->i_buffer_size );
143 #endif
144     if( err != noErr )
145     {
146         msg_Err( p_aout, "failed to set device buffer size: %d", err );
147         return( -1 );
148     }
149
150     p_aout->output.i_nb_samples = p_sys->i_buffer_size / sizeof(float)
151                                    / p_aout->output.output.i_channels;
152
153     /* Add callback */
154     err = AudioDeviceAddIOProc( p_sys->device,
155                                 (AudioDeviceIOProc)IOCallback,
156                                 (void *)p_aout );
157
158     /* Open the output with callback IOCallback */
159     err = AudioDeviceStart( p_sys->device,
160                             (AudioDeviceIOProc)IOCallback );
161     if( err != noErr )
162     {
163         msg_Err( p_aout, "AudioDeviceStart failed: %d", err );
164         return -1;
165     }
166
167     /* Let's pray for the following operation to be atomic... */
168     p_sys->clock_diff = - (mtime_t)AudioConvertHostTimeToNanos(
169                                  AudioGetCurrentHostTime()) / 1000;
170     p_sys->clock_diff += mdate();
171
172     return 0;
173 }
174
175 /*****************************************************************************
176  * Close: close the CoreAudio HAL device
177  *****************************************************************************/
178 void E_(CloseAudio)( aout_instance_t * p_aout )
179 {
180     struct aout_sys_t * p_sys = p_aout->output.p_sys;
181     OSStatus err; 
182
183     /* Stop playing sound through the device */
184     err = AudioDeviceStop( p_sys->device,
185                            (AudioDeviceIOProc)IOCallback ); 
186     if( err != noErr )
187     {
188         msg_Err( p_aout, "AudioDeviceStop failed: %d", err );
189     }
190
191     free( p_sys );
192 }
193
194 /*****************************************************************************
195  * Play: nothing to do
196  *****************************************************************************/
197 static void Play( aout_instance_t * p_aout )
198 {
199 }
200
201 /*****************************************************************************
202  * IOCallback : callback for audio output
203  *****************************************************************************/
204 static OSStatus IOCallback( AudioDeviceID inDevice,
205                             const AudioTimeStamp *inNow, 
206                             const void *inInputData,
207                             const AudioTimeStamp *inInputTime, 
208                             AudioBufferList *outOutputData,
209                             const AudioTimeStamp *inOutputTime, 
210                             void *threadGlobals )
211 {
212     aout_instance_t * p_aout = (aout_instance_t *)threadGlobals;
213     struct aout_sys_t * p_sys = p_aout->output.p_sys;
214     mtime_t         current_date;
215     AudioTimeStamp  host_time;
216     aout_buffer_t * p_buffer;
217
218     host_time.mFlags = kAudioTimeStampHostTimeValid;
219     AudioDeviceTranslateTime( inDevice, inOutputTime, &host_time );
220     current_date = p_sys->clock_diff
221                  + AudioConvertHostTimeToNanos(host_time.mHostTime) / 1000;
222
223     p_buffer = aout_OutputNextBuffer( p_aout, current_date, VLC_FALSE );
224
225     /* move data into output data buffer */
226     if ( p_buffer != NULL )
227     {
228         BlockMoveData( p_buffer->p_buffer,
229                        outOutputData->mBuffers[ 0 ].mData, 
230                        p_sys->i_buffer_size );
231         aout_BufferFree( p_buffer );
232     }
233     else
234     {
235         memset(outOutputData->mBuffers[ 0 ].mData, 0, p_sys->i_buffer_size);
236     }
237
238     return noErr;     
239 }
240