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 $
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>
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.
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.
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 *****************************************************************************/
27 /*****************************************************************************
29 *****************************************************************************/
35 #include "aout_internal.h"
38 #include <Carbon/Carbon.h>
39 #include <CoreAudio/AudioHardware.h>
40 #include <CoreAudio/HostTime.h>
41 #include <AudioToolbox/AudioConverter.h>
43 #define A52_FRAME_NB 1536
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 *****************************************************************************/
53 AudioDeviceID device; // the audio device
55 AudioStreamBasicDescription stream_format;
57 UInt32 i_buffer_size; // audio device buffer size
61 /*****************************************************************************
63 *****************************************************************************/
64 static void Play ( aout_instance_t *p_aout );
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 );
74 /*****************************************************************************
75 * Open: open a CoreAudio HAL device
76 *****************************************************************************/
77 extern MacOSXAudioSystem *gTheMacOSXAudioSystem; // Remove this global, access audio system froma aout some other way
79 int E_(OpenAudio)( vlc_object_t * p_this )
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 ****************************");
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 )
92 msg_Err( p_aout, "out of memory" );
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];
101 msg_Err( p_aout, "couldn't get output device");
104 msg_Dbg(p_aout, "device returned: %ld", p_sys->device);
106 p_aout->output.pf_play = Play;
107 aout_VolumeSoftInit( p_aout );
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 );
114 msg_Err( p_aout, "failed to get stream format: %4.4s", &err );
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 );
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 );
127 switch(p_sys->stream_format.mFormatID)
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;
135 p_aout->output.output.i_channels = AOUT_CHAN_3F2R | AOUT_CHAN_LFE;
138 case kAudioFormat60958AC3:
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;
148 msg_Err( p_aout, "Unknown hardware format '%4.4s'. Go ask Heiko.", &p_sys->stream_format.mFormatID );
152 /* Set sample rate and channels per frame */
153 p_aout->output.output.i_rate = p_sys->stream_format.mSampleRate;
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 );
162 msg_Err(p_aout, "failed to get buffer size - err %4.4s, device %ld", &err, p_sys->device);
165 else msg_Dbg( p_aout, "native buffer Size: %d", p_sys->i_buffer_size );
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 );
173 msg_Err( p_aout, "failed to set device buffer size: %4.4s", err );
176 else msg_Dbg(p_aout, "bufferSize set to %d", p_sys->i_buffer_size);
179 p_aout->output.i_nb_samples = p_sys->i_buffer_size / p_sys->stream_format.mBytesPerFrame;
182 err = AudioDeviceAddIOProc( p_sys->device,
183 (AudioDeviceIOProc)IOCallback,
186 /* Open the output with callback IOCallback */
187 err = AudioDeviceStart( p_sys->device,
188 (AudioDeviceIOProc)IOCallback );
191 msg_Err( p_aout, "AudioDeviceStart failed: %d", err );
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();
203 /*****************************************************************************
204 * Close: close the CoreAudio HAL device
205 *****************************************************************************/
206 void E_(CloseAudio)( aout_instance_t * p_aout )
208 struct aout_sys_t * p_sys = p_aout->output.p_sys;
211 /* Stop playing sound through the device */
212 err = AudioDeviceStop( p_sys->device,
213 (AudioDeviceIOProc)IOCallback );
216 msg_Err( p_aout, "AudioDeviceStop failed: %d", err );
222 /*****************************************************************************
223 * Play: nothing to do
224 *****************************************************************************/
225 static void Play( aout_instance_t * p_aout )
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 )
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;
246 host_time.mFlags = kAudioTimeStampHostTimeValid;
247 AudioDeviceTranslateTime( inDevice, inOutputTime, &host_time );
248 current_date = p_sys->clock_diff
249 + AudioConvertHostTimeToNanos(host_time.mHostTime) / 1000;
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')) );
254 /* move data into output data buffer */
255 if ( p_buffer != NULL )
257 BlockMoveData( p_buffer->p_buffer,
258 outOutputData->mBuffers[ 0 ].mData,
259 p_sys->i_buffer_size );
261 // msg_Dbg(p_aout, "This buffer has %d bytes, i take %d", p_buffer->i_nb_bytes, p_sys->i_buffer_size);
263 aout_BufferFree( p_buffer );
267 memset(outOutputData->mBuffers[ 0 ].mData, 0, p_sys->i_buffer_size);