5 * Original Copyright (C) Timothy J. Wood - Aug 2000
7 * This file is part of libao, a cross-platform library. See
8 * README for a history of this source code.
10 * libao 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, or (at your option)
15 * libao 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.
20 * You should have received a copy of the GNU General Public License
21 * along with GNU Make; see the file COPYING. If not, write to
22 * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
26 * The MacOS X CoreAudio framework doesn't mesh as simply as some
27 * simpler frameworks do. This is due to the fact that CoreAudio pulls
28 * audio samples rather than having them pushed at it (which is nice
29 * when you are wanting to do good buffering of audio).
34 * 14/5-2003: Ported to MPlayer libao2 by Dan Christiansen
36 * AC-3 and MPEG audio passthrough is possible, but I don't have
37 * access to a sound card that supports it.
40 #include <CoreAudio/AudioHardware.h>
46 #include "../mp_msg.h"
48 #include "audio_out.h"
49 #include "audio_out_internal.h"
52 static ao_info_t info
=
54 "Darwin/Mac OS X native audio output",
56 "Timothy J. Wood & Dan Christiansen",
62 /* Prefix for all mp_msg() calls */
63 #define ao_msg(a, b, c...) mp_msg(a, b, "AO: [macosx] " c)
65 /* This is large, but best (maybe it should be even larger).
66 * CoreAudio supposedly has an internal latency in the order of 2ms */
69 typedef struct ao_macosx_s
72 AudioDeviceID outputDeviceID
;
73 AudioStreamBasicDescription outputStreamBasicDescription
;
76 pthread_mutex_t buffer_mutex
; /* mutex covering buffer variables */
78 unsigned char *buffer
[NUM_BUFS
];
79 unsigned int buffer_len
;
81 unsigned int buf_read
;
82 unsigned int buf_write
;
83 unsigned int buf_read_pos
;
84 unsigned int buf_write_pos
;
89 static ao_macosx_t
*ao
;
91 /* General purpose Ring-buffering routines */
92 static int write_buffer(unsigned char* data
,int len
){
97 if(ao
->full_buffers
==NUM_BUFS
) {
98 ao_msg(MSGT_AO
,MSGL_V
, "Buffer overrun\n");
102 x
=ao
->buffer_len
-ao
->buf_write_pos
;
104 memcpy(ao
->buffer
[ao
->buf_write
]+ao
->buf_write_pos
,data
+len2
,x
);
106 /* accessing common variables, locking mutex */
107 pthread_mutex_lock(&ao
->buffer_mutex
);
109 ao
->buffered_bytes
+=x
; ao
->buf_write_pos
+=x
;
110 if(ao
->buf_write_pos
>=ao
->buffer_len
) {
111 /* block is full, find next! */
112 ao
->buf_write
=(ao
->buf_write
+1)%NUM_BUFS
;
116 pthread_mutex_unlock(&ao
->buffer_mutex
);
122 static int read_buffer(unsigned char* data
,int len
){
127 if(ao
->full_buffers
==0) {
128 ao_msg(MSGT_AO
,MSGL_V
, "Buffer underrun\n");
132 x
=ao
->buffer_len
-ao
->buf_read_pos
;
134 memcpy(data
+len2
,ao
->buffer
[ao
->buf_read
]+ao
->buf_read_pos
,x
);
137 /* accessing common variables, locking mutex */
138 pthread_mutex_lock(&ao
->buffer_mutex
);
139 ao
->buffered_bytes
-=x
; ao
->buf_read_pos
+=x
;
140 if(ao
->buf_read_pos
>=ao
->buffer_len
){
141 /* block is empty, find next! */
142 ao
->buf_read
=(ao
->buf_read
+1)%NUM_BUFS
;
146 pthread_mutex_unlock(&ao
->buffer_mutex
);
153 /* end ring buffer stuff */
155 /* The function that the CoreAudio thread calls when it wants more data */
156 static OSStatus
audioDeviceIOProc(AudioDeviceID inDevice
, const AudioTimeStamp
*inNow
, const AudioBufferList
*inInputData
, const AudioTimeStamp
*inInputTime
, AudioBufferList
*outOutputData
, const AudioTimeStamp
*inOutputTime
, void *inClientData
)
158 outOutputData
->mBuffers
[0].mDataByteSize
=
159 read_buffer((char *)outOutputData
->mBuffers
[0].mData
, ao
->buffer_len
);
165 static int control(int cmd
,void *arg
){
167 case AOCONTROL_SET_DEVICE
:
168 case AOCONTROL_GET_DEVICE
:
169 case AOCONTROL_GET_VOLUME
:
170 case AOCONTROL_SET_VOLUME
:
171 /* unimplemented/meaningless */
172 return CONTROL_FALSE
;
173 case AOCONTROL_QUERY_FORMAT
:
174 /* stick with what CoreAudio requests */
175 return CONTROL_FALSE
;
177 return CONTROL_FALSE
;
183 static int init(int rate
,int channels
,int format
,int flags
)
190 ao
= (ao_macosx_t
*)malloc(sizeof(ao_macosx_t
));
192 /* initialise mutex */
193 pthread_mutex_init(&ao
->buffer_mutex
, NULL
);
194 pthread_mutex_unlock(&ao
->buffer_mutex
);
196 /* get default output device */
197 propertySize
= sizeof(ao
->outputDeviceID
);
198 status
= AudioHardwareGetProperty(kAudioHardwarePropertyDefaultOutputDevice
, &propertySize
, &(ao
->outputDeviceID
));
200 ao_msg(MSGT_AO
,MSGL_WARN
,
201 "AudioHardwareGetProperty returned %d\n",
203 return CONTROL_FALSE
;
206 if (ao
->outputDeviceID
== kAudioDeviceUnknown
) {
207 ao_msg(MSGT_AO
,MSGL_WARN
, "AudioHardwareGetProperty: ao->outputDeviceID is kAudioDeviceUnknown\n");
208 return CONTROL_FALSE
;
211 /* get default output format
212 * TODO: get all support formats and iterate through them
214 propertySize
= sizeof(ao
->outputStreamBasicDescription
);
215 status
= AudioDeviceGetProperty(ao
->outputDeviceID
, 0, false, kAudioDevicePropertyStreamFormat
, &propertySize
, &ao
->outputStreamBasicDescription
);
217 ao_msg(MSGT_AO
,MSGL_WARN
, "AudioDeviceGetProperty returned %d when getting kAudioDevicePropertyStreamFormat\n", (int)status
);
218 return CONTROL_FALSE
;
221 ao_msg(MSGT_AO
,MSGL_V
, "hardware format...\n");
222 ao_msg(MSGT_AO
,MSGL_V
, "%f mSampleRate\n", ao
->outputStreamBasicDescription
.mSampleRate
);
223 ao_msg(MSGT_AO
,MSGL_V
, " %c%c%c%c mFormatID\n",
224 (int)(ao
->outputStreamBasicDescription
.mFormatID
& 0xff000000) >> 24,
225 (int)(ao
->outputStreamBasicDescription
.mFormatID
& 0x00ff0000) >> 16,
226 (int)(ao
->outputStreamBasicDescription
.mFormatID
& 0x0000ff00) >> 8,
227 (int)(ao
->outputStreamBasicDescription
.mFormatID
& 0x000000ff) >> 0);
228 ao_msg(MSGT_AO
,MSGL_V
, "%5d mBytesPerPacket\n",
229 (int)ao
->outputStreamBasicDescription
.mBytesPerPacket
);
230 ao_msg(MSGT_AO
,MSGL_V
, "%5d mFramesPerPacket\n",
231 (int)ao
->outputStreamBasicDescription
.mFramesPerPacket
);
232 ao_msg(MSGT_AO
,MSGL_V
, "%5d mBytesPerFrame\n",
233 (int)ao
->outputStreamBasicDescription
.mBytesPerFrame
);
234 ao_msg(MSGT_AO
,MSGL_V
, "%5d mChannelsPerFrame\n",
235 (int)ao
->outputStreamBasicDescription
.mChannelsPerFrame
);
237 /* get requested buffer length */
238 propertySize
= sizeof(ao
->buffer_len
);
239 status
= AudioDeviceGetProperty(ao
->outputDeviceID
, 0, false, kAudioDevicePropertyBufferSize
, &propertySize
, &ao
->buffer_len
);
241 ao_msg(MSGT_AO
,MSGL_WARN
, "AudioDeviceGetProperty returned %d when getting kAudioDevicePropertyBufferSize\n", (int)status
);
242 return CONTROL_FALSE
;
244 ao_msg(MSGT_AO
,MSGL_V
, "%5d ao->buffer_len\n", (int)ao
->buffer_len
);
248 * Resampling of 32-bit float audio is broken in MPlayer. Refuse to
249 * handle anything other than the native format until this is fixed
250 * or this module is rewritten, whichever comes first.
252 if (ao_data
.samplerate
== ao
->outputStreamBasicDescription
.mSampleRate
) {
253 ao_data
.samplerate
= (int)ao
->outputStreamBasicDescription
.mSampleRate
;
255 ao_msg(MSGT_AO
,MSGL_WARN
, "Resampling not supported yet.\n");
259 ao_data
.channels
= ao
->outputStreamBasicDescription
.mChannelsPerFrame
;
260 ao_data
.outburst
= ao_data
.buffersize
= ao
->buffer_len
;
262 ao_data
.samplerate
* ao
->outputStreamBasicDescription
.mBytesPerFrame
;
264 if (ao
->outputStreamBasicDescription
.mFormatID
== kAudioFormatLinearPCM
) {
265 uint32_t flags
= ao
->outputStreamBasicDescription
.mFormatFlags
;
266 if (flags
& kAudioFormatFlagIsFloat
) {
267 ao_data
.format
= AFMT_FLOAT
;
269 ao_msg(MSGT_AO
,MSGL_WARN
, "Unsupported audio output "
270 "format %d. Please report this to the developer\n",
272 return CONTROL_FALSE
;
276 /* TODO: handle AFMT_AC3, AFMT_MPEG & friends */
277 ao_msg(MSGT_AO
,MSGL_WARN
, "Default Audio Device doesn't "
278 "support Linear PCM!\n");
279 return CONTROL_FALSE
;
282 /* Allocate ring-buffer memory */
283 for(i
=0;i
<NUM_BUFS
;i
++)
284 ao
->buffer
[i
]=(unsigned char *) malloc(ao
->buffer_len
);
287 /* Prepare for playback */
291 /* Set the IO proc that CoreAudio will call when it needs data */
292 status
= AudioDeviceAddIOProc(ao
->outputDeviceID
, audioDeviceIOProc
, NULL
);
294 ao_msg(MSGT_AO
,MSGL_WARN
, "AudioDeviceAddIOProc returned %d\n", (int)status
);
295 return CONTROL_FALSE
;
299 status
= AudioDeviceStart(ao
->outputDeviceID
, audioDeviceIOProc
);
301 ao_msg(MSGT_AO
,MSGL_WARN
, "AudioDeviceStart returned %d\n",
303 return CONTROL_FALSE
;
310 static int play(void* output_samples
,int num_bytes
,int flags
)
312 return write_buffer(output_samples
, num_bytes
);
315 /* set variables and buffer to initial state */
320 pthread_mutex_lock(&ao
->buffer_mutex
);
322 /* reset ring-buffer state */
329 ao
->buffered_bytes
=0;
331 /* zero output buffer */
332 for (i
= 0; i
< NUM_BUFS
; i
++)
333 bzero(ao
->buffer
[i
], ao
->buffer_len
);
335 pthread_mutex_unlock(&ao
->buffer_mutex
);
341 /* return available space */
342 static int get_space()
344 return (NUM_BUFS
-ao
->full_buffers
)*ao_data
.buffersize
- ao
->buf_write_pos
;
348 /* return delay until audio is played */
349 static float get_delay()
351 return (float)(ao
->buffered_bytes
)/(float)ao_data
.bps
;
355 /* unload plugin and deregister from coreaudio */
363 status
= AudioDeviceRemoveIOProc(ao
->outputDeviceID
, audioDeviceIOProc
);
365 ao_msg(MSGT_AO
,MSGL_WARN
, "AudioDeviceRemoveIOProc "
366 "returned %d\n", (int)status
);
368 for(i
=0;i
<NUM_BUFS
;i
++) free(ao
->buffer
[i
]);
373 /* stop playing, keep buffers (for pause) */
374 static void audio_pause()
379 status
= AudioDeviceStop(ao
->outputDeviceID
, audioDeviceIOProc
);
381 ao_msg(MSGT_AO
,MSGL_WARN
, "AudioDeviceStop returned %d\n",
386 /* resume playing, after audio_pause() */
387 static void audio_resume()
389 OSErr status
= AudioDeviceStart(ao
->outputDeviceID
, audioDeviceIOProc
);
391 ao_msg(MSGT_AO
,MSGL_WARN
, "AudioDeviceStart returned %d\n",