2
audio_macosx: audio output on MacOS X
4
copyright ?-2006 by the mpg123 project - free software under the terms of the GPL 2
5
see COPYING and AUTHORS files in distribution or http://mpg123.org
6
initially written by Guillaume Outters
7
modified by Nicholas J Humfrey to use SFIFO code
8
modified by Taihei Monma to use AudioUnit and AudioConverter APIs
17
#include <CoreServices/CoreServices.h>
18
#include <AudioUnit/AudioUnit.h>
19
#include <AudioToolbox/AudioToolbox.h>
24
#define FIFO_DURATION (0.5f)
29
AudioConverterRef converter;
37
/* Convertion buffer */
38
unsigned char * buffer;
45
static struct anEnv *env=NULL;
49
static OSStatus playProc(AudioConverterRef inAudioConverter,
50
UInt32 *ioNumberDataPackets,
51
AudioBufferList *outOutputData,
52
AudioStreamPacketDescription **outDataPacketDescription,
57
if(env->last_buffer) {
62
for(n = 0; n < outOutputData->mNumberBuffers; n++)
64
unsigned int wanted = *ioNumberDataPackets * env->channels * 2;
67
if(env->buffer_size < wanted) {
68
debug1("Allocating %d byte sample conversion buffer", wanted);
69
env->buffer = realloc( env->buffer, wanted);
70
env->buffer_size = wanted;
74
/* Only play if we have data left */
75
if ( sfifo_used( &env->fifo ) < wanted ) {
76
if(!env->decode_done) {
77
warning("Didn't have any audio data in callback (buffer underflow)");
80
wanted = sfifo_used( &env->fifo );
84
/* Read audio from FIFO to SDL's buffer */
85
read = sfifo_read( &env->fifo, dest, wanted );
88
warning2("Error reading from the ring buffer (wanted=%u, read=%u).\n", wanted, read);
90
outOutputData->mBuffers[n].mDataByteSize = read;
91
outOutputData->mBuffers[n].mData = dest;
97
static OSStatus convertProc(void *inRefCon, AudioUnitRenderActionFlags *inActionFlags,
98
const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber,
99
UInt32 inNumFrames, AudioBufferList *ioData)
102
void *inInputDataProcUserData=NULL;
103
AudioStreamPacketDescription* outPacketDescription =NULL;
105
err = AudioConverterFillComplexBuffer(env->converter, playProc, inInputDataProcUserData, &inNumFrames, ioData, outPacketDescription);
111
int audio_open(struct audio_info_struct *ai)
114
ComponentDescription desc;
116
AudioStreamBasicDescription inFormat;
117
AudioStreamBasicDescription outFormat;
118
AURenderCallbackStruct renderCallback;
121
/* Allocate memory for data structure */
123
env = (struct anEnv*)malloc( sizeof( struct anEnv ) );
125
error("failed to malloc memory for 'struct anEnv'");
130
/* Initialize our environment */
133
env->buffer_size = 0;
134
env->last_buffer = 0;
136
env->decode_done = 0;
139
/* Get the default audio output unit */
140
desc.componentType = kAudioUnitType_Output;
141
desc.componentSubType = kAudioUnitSubType_DefaultOutput;
142
desc.componentManufacturer = kAudioUnitManufacturer_Apple;
143
desc.componentFlags = 0;
144
desc.componentFlagsMask = 0;
145
comp = FindNextComponent(NULL, &desc);
147
error("FindNextComponent failed");
151
if(OpenAComponent(comp, &(env->outputUnit))) {
152
error("OpenAComponent failed");
156
if(AudioUnitInitialize(env->outputUnit)) {
157
error("AudioUnitInitialize failed");
161
/* Specify the output PCM format */
162
AudioUnitGetPropertyInfo(env->outputUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, 0, &size, &outWritable);
163
if(AudioUnitGetProperty(env->outputUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, 0, &outFormat, &size)) {
164
error("AudioUnitGetProperty(kAudioUnitProperty_StreamFormat) failed");
168
if(AudioUnitSetProperty(env->outputUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 0, &outFormat, size)) {
169
error("AudioUnitSetProperty(kAudioUnitProperty_StreamFormat) failed");
173
/* Specify the input PCM format */
174
env->channels = ai->channels;
175
inFormat.mSampleRate = ai->rate;
176
inFormat.mChannelsPerFrame = ai->channels;
177
inFormat.mBitsPerChannel = 16;
178
inFormat.mBytesPerPacket = 2*inFormat.mChannelsPerFrame;
179
inFormat.mFramesPerPacket = 1;
180
inFormat.mBytesPerFrame = 2*inFormat.mChannelsPerFrame;
181
inFormat.mFormatID = kAudioFormatLinearPCM;
183
inFormat.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger | kLinearPCMFormatFlagIsPacked | kLinearPCMFormatFlagIsBigEndian;
185
inFormat.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger | kLinearPCMFormatFlagIsPacked;
189
/* Add our callback - but don't start it yet */
190
memset(&renderCallback, 0, sizeof(AURenderCallbackStruct));
191
renderCallback.inputProc = convertProc;
192
renderCallback.inputProcRefCon = 0;
193
if(AudioUnitSetProperty(env->outputUnit, kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Input, 0, &renderCallback, sizeof(AURenderCallbackStruct))) {
194
error("AudioUnitSetProperty(kAudioUnitProperty_SetRenderCallback) failed");
199
/* Open an audio I/O stream and create converter */
200
if (ai->rate > 0 && ai->channels >0 ) {
203
if(AudioConverterNew(&inFormat, &outFormat, &(env->converter))) {
204
error("AudioConverterNew failed");
207
if(ai->channels == 1) {
208
SInt32 channelMap[2] = { 0, 0 };
209
if(AudioConverterSetProperty(env->converter, kAudioConverterChannelMap, sizeof(channelMap), channelMap)) {
210
error("AudioConverterSetProperty(kAudioConverterChannelMap) failed");
215
/* Initialise FIFO */
216
ringbuffer_len = ai->rate * FIFO_DURATION * sizeof(short) *ai->channels;
217
debug2( "Allocating %d byte ring-buffer (%f seconds)", ringbuffer_len, (float)FIFO_DURATION);
218
sfifo_init( &env->fifo, ringbuffer_len );
226
int audio_get_formats(struct audio_info_struct *ai)
228
/* Only support Signed 16-bit output */
229
return AUDIO_FORMAT_SIGNED_16;
233
int audio_play_samples(struct audio_info_struct *ai, unsigned char *buf, int len)
237
/* If there is no room, then sleep for half the length of the FIFO */
238
while (sfifo_space( &env->fifo ) < len ) {
239
usleep( (FIFO_DURATION/2) * 1000000 );
242
/* Store converted audio in ring buffer */
243
written = sfifo_write( &env->fifo, (char*)buf, len);
244
if (written != len) {
245
warning( "Failed to write audio to ring buffer" );
249
/* Start playback now that we have something to play */
252
if(AudioOutputUnitStart(env->outputUnit)) {
253
error("AudioOutputUnitStart failed");
262
int audio_close(struct audio_info_struct *ai)
265
env->decode_done = 1;
266
while(!env->play_done && env->play) usleep(10000);
268
/* No matter the error code, we want to close it (by brute force if necessary) */
269
AudioConverterDispose(env->converter);
270
AudioOutputUnitStop(env->outputUnit);
271
AudioUnitUninitialize(env->outputUnit);
272
CloseComponent(env->outputUnit);
274
/* Free the ring buffer */
275
sfifo_close( &env->fifo );
277
/* Free the conversion buffer */
278
if (env->buffer) free( env->buffer );
280
/* Free environment data structure */
288
void audio_queueflush(struct audio_info_struct *ai)
292
if(AudioOutputUnitStop(env->outputUnit)) {
293
error("AudioOutputUnitStop failed");
297
/* Empty out the ring buffer */
298
sfifo_flush( &env->fifo );