|
Packit |
c32a2d |
/*
|
|
Packit |
c32a2d |
coreaudio: audio output on MacOS X
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
copyright ?-2016 by the mpg123 project - free software under the terms of the GPL 2
|
|
Packit |
c32a2d |
see COPYING and AUTHORS files in distribution or http://mpg123.org
|
|
Packit |
c32a2d |
initially written by Guillaume Outters
|
|
Packit |
c32a2d |
modified by Nicholas J Humfrey to use SFIFO code
|
|
Packit |
c32a2d |
modified by Taihei Monma to use AudioUnit and AudioConverter APIs
|
|
Packit |
c32a2d |
*/
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
#include "out123_int.h"
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
/* has been around since at least 10.4 */
|
|
Packit |
c32a2d |
#include <AvailabilityMacros.h>
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
/* Use AudioComponents API when compiling for >= 10.6, otherwise fall back to
|
|
Packit |
c32a2d |
* Components Manager, which is deprecated since 10.8.
|
|
Packit |
c32a2d |
* MAC_OS_X_VERSION_MIN_REQUIRED defaults to the host system version and can be
|
|
Packit |
c32a2d |
* governed by MACOSX_DEPLOYMENT_TARGET environment variable and
|
|
Packit |
c32a2d |
* -mmacosx-version-min= when running the compiler. */
|
|
Packit |
c32a2d |
#if MAC_OS_X_VERSION_MIN_REQUIRED >= 1060 || __IPHONE_OS_VERSION_MIN_REQUIRED >= 20000
|
|
Packit |
c32a2d |
#define HAVE_AUDIOCOMPONENTS 1
|
|
Packit |
c32a2d |
#endif
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
#if HAVE_AUDIOCOMPONENTS
|
|
Packit |
c32a2d |
#define MPG123_AUDIOCOMPONENTDESCRIPTION AudioComponentDescription
|
|
Packit |
c32a2d |
#define MPG123_AUDIOCOMPONENT AudioComponent
|
|
Packit |
c32a2d |
#define MPG123_AUDIOCOMPONENTFINDNEXT AudioComponentFindNext
|
|
Packit |
c32a2d |
/* Funky API twist: AudioUnit is actually typedef'd AudioComponentInstance */
|
|
Packit |
c32a2d |
#define MPG123_AUDIOCOMPONENTINSTANCENEW AudioComponentInstanceNew
|
|
Packit |
c32a2d |
#define MPG123_AUDIOCOMPONENTINSTANCEDISPOSE AudioComponentInstanceDispose
|
|
Packit |
c32a2d |
#else
|
|
Packit |
c32a2d |
#include <CoreServices/CoreServices.h>
|
|
Packit |
c32a2d |
#define MPG123_AUDIOCOMPONENTDESCRIPTION ComponentDescription
|
|
Packit |
c32a2d |
#define MPG123_AUDIOCOMPONENT Component
|
|
Packit |
c32a2d |
#define MPG123_AUDIOCOMPONENTFINDNEXT FindNextComponent
|
|
Packit |
c32a2d |
#define MPG123_AUDIOCOMPONENTINSTANCENEW OpenAComponent
|
|
Packit |
c32a2d |
#define MPG123_AUDIOCOMPONENTINSTANCEDISPOSE CloseComponent
|
|
Packit |
c32a2d |
#endif
|
|
Packit |
c32a2d |
#include <AudioUnit/AudioUnit.h>
|
|
Packit |
c32a2d |
#include <AudioToolbox/AudioToolbox.h>
|
|
Packit |
c32a2d |
#include <errno.h>
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
/* Including the sfifo code locally, to avoid module linkage issues. */
|
|
Packit |
c32a2d |
#define SFIFO_STATIC
|
|
Packit |
c32a2d |
#include "sfifo.c"
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
#include "debug.h"
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
/* Duration of the ring buffer in seconds.
|
|
Packit |
c32a2d |
Is that all that there is to tunable latency?
|
|
Packit |
c32a2d |
Size of 200 ms should be enough for a default value, rare is the
|
|
Packit |
c32a2d |
hardware that actually allows such large buffers. */
|
|
Packit |
c32a2d |
#define FIFO_DURATION (ao->device_buffer > 0. ? ao->device_buffer : 0.2)
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
typedef struct mpg123_coreaudio
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
AudioConverterRef converter;
|
|
Packit |
c32a2d |
AudioUnit outputUnit;
|
|
Packit |
c32a2d |
int open;
|
|
Packit |
c32a2d |
char play;
|
|
Packit |
c32a2d |
int channels;
|
|
Packit |
c32a2d |
int bps;
|
|
Packit |
c32a2d |
int play_done;
|
|
Packit |
c32a2d |
int decode_done;
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
/* Convertion buffer */
|
|
Packit |
c32a2d |
unsigned char * buffer;
|
|
Packit |
c32a2d |
size_t buffer_size;
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
/* Ring buffer */
|
|
Packit |
c32a2d |
sfifo_t fifo;
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
} mpg123_coreaudio_t;
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
static OSStatus playProc(AudioConverterRef inAudioConverter,
|
|
Packit |
c32a2d |
UInt32 *ioNumberDataPackets,
|
|
Packit |
c32a2d |
AudioBufferList *outOutputData,
|
|
Packit |
c32a2d |
AudioStreamPacketDescription **outDataPacketDescription,
|
|
Packit |
c32a2d |
void* inClientData)
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
out123_handle *ao = (out123_handle*)inClientData;
|
|
Packit |
c32a2d |
mpg123_coreaudio_t *ca = (mpg123_coreaudio_t *)ao->userptr;
|
|
Packit |
c32a2d |
long n;
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
/* This is not actually a loop. See the early break. */
|
|
Packit |
c32a2d |
for(n = 0; n < outOutputData->mNumberBuffers; n++)
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
unsigned int wanted = *ioNumberDataPackets * ca->channels * ca->bps;
|
|
Packit |
c32a2d |
unsigned char *dest;
|
|
Packit |
c32a2d |
unsigned int read;
|
|
Packit |
c32a2d |
int avail;
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
/* Any buffer count > 1 would wreck havoc with this code. */
|
|
Packit |
c32a2d |
if(n > 0)
|
|
Packit |
c32a2d |
break;
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
if(ca->buffer_size < wanted) {
|
|
Packit |
c32a2d |
debug1("Allocating %d byte sample conversion buffer", wanted);
|
|
Packit |
c32a2d |
ca->buffer = realloc( ca->buffer, wanted);
|
|
Packit |
c32a2d |
ca->buffer_size = wanted;
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
dest = ca->buffer;
|
|
Packit |
c32a2d |
if(!dest)
|
|
Packit |
c32a2d |
return -1;
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
/* Only play if we have data left */
|
|
Packit |
c32a2d |
while((avail=sfifo_used( &ca->fifo )) < wanted && !ca->decode_done)
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
int ms = (wanted-avail)/ao->framesize*1000/ao->rate;
|
|
Packit |
c32a2d |
debug3("waiting for more input, %d ms missing (%i < %u)"
|
|
Packit |
c32a2d |
, ms, avail, wanted);
|
|
Packit |
c32a2d |
usleep(ms*100); /* Wait for 1/10th of the missing duration. Might want to adjust. */
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
if(avail > wanted)
|
|
Packit |
c32a2d |
avail = wanted;
|
|
Packit |
c32a2d |
else if(ca->decode_done)
|
|
Packit |
c32a2d |
ca->play_done = 1;
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
/* Read audio from FIFO to CoreAudio's buffer */
|
|
Packit |
c32a2d |
read = sfifo_read(&ca->fifo, dest, avail);
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
if(read!=avail)
|
|
Packit |
c32a2d |
warning2("Error reading from the ring buffer (avail=%u, read=%u).\n", avail, read);
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
outOutputData->mBuffers[n].mDataByteSize = read;
|
|
Packit |
c32a2d |
outOutputData->mBuffers[n].mData = dest;
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
return noErr;
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
static OSStatus convertProc(void *inRefCon, AudioUnitRenderActionFlags *inActionFlags,
|
|
Packit |
c32a2d |
const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber,
|
|
Packit |
c32a2d |
UInt32 inNumFrames, AudioBufferList *ioData)
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
AudioStreamPacketDescription* outPacketDescription = NULL;
|
|
Packit |
c32a2d |
out123_handle *ao = (out123_handle*)inRefCon;
|
|
Packit |
c32a2d |
mpg123_coreaudio_t *ca = (mpg123_coreaudio_t *)ao->userptr;
|
|
Packit |
c32a2d |
OSStatus err= noErr;
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
err = AudioConverterFillComplexBuffer(ca->converter, playProc, inRefCon, &inNumFrames, ioData, outPacketDescription);
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
return err;
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
static int open_coreaudio(out123_handle *ao)
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
mpg123_coreaudio_t* ca = (mpg123_coreaudio_t*)ao->userptr;
|
|
Packit |
c32a2d |
UInt32 size;
|
|
Packit |
c32a2d |
MPG123_AUDIOCOMPONENTDESCRIPTION desc;
|
|
Packit |
c32a2d |
MPG123_AUDIOCOMPONENT comp;
|
|
Packit |
c32a2d |
AudioStreamBasicDescription inFormat;
|
|
Packit |
c32a2d |
AudioStreamBasicDescription outFormat;
|
|
Packit |
c32a2d |
AURenderCallbackStruct renderCallback;
|
|
Packit |
c32a2d |
Boolean outWritable;
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
/* Initialize our environment */
|
|
Packit |
c32a2d |
ca->play = 0;
|
|
Packit |
c32a2d |
ca->buffer = NULL;
|
|
Packit |
c32a2d |
ca->buffer_size = 0;
|
|
Packit |
c32a2d |
ca->play_done = 0;
|
|
Packit |
c32a2d |
ca->decode_done = 0;
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
/* Get the default audio output unit */
|
|
Packit |
c32a2d |
desc.componentType = kAudioUnitType_Output;
|
|
Packit |
c32a2d |
#if TARGET_OS_IPHONE
|
|
Packit |
c32a2d |
desc.componentSubType = kAudioUnitSubType_RemoteIO;
|
|
Packit |
c32a2d |
#else
|
|
Packit |
c32a2d |
desc.componentSubType = kAudioUnitSubType_DefaultOutput;
|
|
Packit |
c32a2d |
#endif
|
|
Packit |
c32a2d |
desc.componentManufacturer = kAudioUnitManufacturer_Apple;
|
|
Packit |
c32a2d |
desc.componentFlags = 0;
|
|
Packit |
c32a2d |
desc.componentFlagsMask = 0;
|
|
Packit |
c32a2d |
comp = MPG123_AUDIOCOMPONENTFINDNEXT(NULL, &desc);
|
|
Packit |
c32a2d |
if(comp == NULL)
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
if(!AOQUIET)
|
|
Packit |
c32a2d |
error("AudioComponentFindNext failed");
|
|
Packit |
c32a2d |
return(-1);
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
if(MPG123_AUDIOCOMPONENTINSTANCENEW(comp, &(ca->outputUnit)))
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
if(!AOQUIET)
|
|
Packit |
c32a2d |
error("AudioComponentInstanceNew failed");
|
|
Packit |
c32a2d |
return (-1);
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
if(AudioUnitInitialize(ca->outputUnit))
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
if(!AOQUIET)
|
|
Packit |
c32a2d |
error("AudioUnitInitialize failed");
|
|
Packit |
c32a2d |
return (-1);
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
/* Specify the output PCM format */
|
|
Packit |
c32a2d |
AudioUnitGetPropertyInfo(ca->outputUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, 0, &size, &outWritable);
|
|
Packit |
c32a2d |
if(AudioUnitGetProperty(ca->outputUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, 0, &outFormat, &size))
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
if(!AOQUIET)
|
|
Packit |
c32a2d |
error("AudioUnitGetProperty(kAudioUnitProperty_StreamFormat) failed");
|
|
Packit |
c32a2d |
return (-1);
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
if(AudioUnitSetProperty(ca->outputUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 0, &outFormat, size))
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
if(!AOQUIET)
|
|
Packit |
c32a2d |
error("AudioUnitSetProperty(kAudioUnitProperty_StreamFormat) failed");
|
|
Packit |
c32a2d |
return (-1);
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
/* Specify the input PCM format */
|
|
Packit |
c32a2d |
ca->channels = ao->channels;
|
|
Packit |
c32a2d |
inFormat.mSampleRate = ao->rate;
|
|
Packit |
c32a2d |
inFormat.mChannelsPerFrame = ao->channels;
|
|
Packit |
c32a2d |
inFormat.mFormatID = kAudioFormatLinearPCM;
|
|
Packit |
c32a2d |
#ifdef _BIG_ENDIAN
|
|
Packit |
c32a2d |
inFormat.mFormatFlags = kLinearPCMFormatFlagIsPacked | kLinearPCMFormatFlagIsBigEndian;
|
|
Packit |
c32a2d |
#else
|
|
Packit |
c32a2d |
inFormat.mFormatFlags = kLinearPCMFormatFlagIsPacked;
|
|
Packit |
c32a2d |
#endif
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
switch(ao->format)
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
case MPG123_ENC_SIGNED_16:
|
|
Packit |
c32a2d |
inFormat.mFormatFlags |= kLinearPCMFormatFlagIsSignedInteger;
|
|
Packit |
c32a2d |
ca->bps = 2;
|
|
Packit |
c32a2d |
break;
|
|
Packit |
c32a2d |
case MPG123_ENC_SIGNED_8:
|
|
Packit |
c32a2d |
inFormat.mFormatFlags |= kLinearPCMFormatFlagIsSignedInteger;
|
|
Packit |
c32a2d |
ca->bps = 1;
|
|
Packit |
c32a2d |
break;
|
|
Packit |
c32a2d |
case MPG123_ENC_UNSIGNED_8:
|
|
Packit |
c32a2d |
ca->bps = 1;
|
|
Packit |
c32a2d |
break;
|
|
Packit |
c32a2d |
case MPG123_ENC_SIGNED_32:
|
|
Packit |
c32a2d |
inFormat.mFormatFlags |= kLinearPCMFormatFlagIsSignedInteger;
|
|
Packit |
c32a2d |
ca->bps = 4;
|
|
Packit |
c32a2d |
break;
|
|
Packit |
c32a2d |
case MPG123_ENC_FLOAT_32:
|
|
Packit |
c32a2d |
inFormat.mFormatFlags |= kLinearPCMFormatFlagIsFloat;
|
|
Packit |
c32a2d |
ca->bps = 4;
|
|
Packit |
c32a2d |
break;
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
inFormat.mBitsPerChannel = ca->bps << 3;
|
|
Packit |
c32a2d |
inFormat.mBytesPerPacket = ca->bps*inFormat.mChannelsPerFrame;
|
|
Packit |
c32a2d |
inFormat.mFramesPerPacket = 1;
|
|
Packit |
c32a2d |
inFormat.mBytesPerFrame = ca->bps*inFormat.mChannelsPerFrame;
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
/* Add our callback - but don't start it yet */
|
|
Packit |
c32a2d |
memset(&renderCallback, 0, sizeof(AURenderCallbackStruct));
|
|
Packit |
c32a2d |
renderCallback.inputProc = convertProc;
|
|
Packit |
c32a2d |
renderCallback.inputProcRefCon = ao;
|
|
Packit |
c32a2d |
if(AudioUnitSetProperty(ca->outputUnit, kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Input, 0, &renderCallback, sizeof(AURenderCallbackStruct)))
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
if(!AOQUIET)
|
|
Packit |
c32a2d |
error("AudioUnitSetProperty(kAudioUnitProperty_SetRenderCallback) failed");
|
|
Packit |
c32a2d |
return(-1);
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
/* Open an audio I/O stream and create converter */
|
|
Packit |
c32a2d |
if (ao->rate > 0 && ao->channels >0 ) {
|
|
Packit |
c32a2d |
int ringbuffer_len;
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
if(AudioConverterNew(&inFormat, &outFormat, &(ca->converter)))
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
if(!AOQUIET)
|
|
Packit |
c32a2d |
error("AudioConverterNew failed");
|
|
Packit |
c32a2d |
return(-1);
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
if(ao->channels == 1) {
|
|
Packit |
c32a2d |
SInt32 channelMap[2] = { 0, 0 };
|
|
Packit |
c32a2d |
if(AudioConverterSetProperty(ca->converter, kAudioConverterChannelMap, sizeof(channelMap), channelMap))
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
if(!AOQUIET)
|
|
Packit |
c32a2d |
error("AudioConverterSetProperty(kAudioConverterChannelMap) failed");
|
|
Packit |
c32a2d |
return(-1);
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
/* Initialise FIFO */
|
|
Packit |
c32a2d |
ringbuffer_len = ao->rate * FIFO_DURATION * ca->bps * ao->channels;
|
|
Packit |
c32a2d |
debug2( "Allocating %d byte ring-buffer (%f seconds)", ringbuffer_len, (float)FIFO_DURATION);
|
|
Packit |
c32a2d |
sfifo_init( &ca->fifo, ringbuffer_len );
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
return(0);
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
static int get_formats_coreaudio(out123_handle *ao)
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
return MPG123_ENC_SIGNED_16|MPG123_ENC_SIGNED_8|MPG123_ENC_UNSIGNED_8|MPG123_ENC_SIGNED_32|MPG123_ENC_FLOAT_32;
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
static int write_coreaudio(out123_handle *ao, unsigned char *buf, int len)
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
mpg123_coreaudio_t* ca = (mpg123_coreaudio_t*)ao->userptr;
|
|
Packit |
c32a2d |
int len_remain = len;
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
/* Some busy waiting, but feed what is possible. */
|
|
Packit |
c32a2d |
while(len_remain) /* Note: input len is multiple of framesize! */
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
int block = sfifo_space(&ca->fifo);
|
|
Packit |
c32a2d |
block -= block % ao->framesize;
|
|
Packit |
c32a2d |
if(block > len_remain)
|
|
Packit |
c32a2d |
block = len_remain;
|
|
Packit |
c32a2d |
if(block)
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
sfifo_write(&ca->fifo, buf, block);
|
|
Packit |
c32a2d |
len_remain -= block;
|
|
Packit |
c32a2d |
buf += block;
|
|
Packit |
c32a2d |
/* Start playback now that we have something to play */
|
|
Packit |
c32a2d |
if(!ca->play && (sfifo_used(&ca->fifo) > (sfifo_size(&ca->fifo)/2)))
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
if(AudioOutputUnitStart(ca->outputUnit))
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
if(!AOQUIET)
|
|
Packit |
c32a2d |
error("AudioOutputUnitStart failed");
|
|
Packit |
c32a2d |
return(-1);
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
ca->play = 1;
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
/* If there is no room, then sleep for a bit, but not too long. */
|
|
Packit |
c32a2d |
if(len_remain)
|
|
Packit |
c32a2d |
usleep( (0.1*FIFO_DURATION) * 1000000 );
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
return len;
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
static int close_coreaudio(out123_handle *ao)
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
mpg123_coreaudio_t* ca = (mpg123_coreaudio_t*)ao->userptr;
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
if (ca) {
|
|
Packit |
c32a2d |
ca->decode_done = 1;
|
|
Packit |
c32a2d |
while(!ca->play_done && ca->play)
|
|
Packit |
c32a2d |
usleep((0.1*FIFO_DURATION)*1000000);
|
|
Packit |
c32a2d |
/* No matter the error code, we want to close it (by brute force if necessary) */
|
|
Packit |
c32a2d |
AudioOutputUnitStop(ca->outputUnit);
|
|
Packit |
c32a2d |
AudioUnitUninitialize(ca->outputUnit);
|
|
Packit |
c32a2d |
MPG123_AUDIOCOMPONENTINSTANCEDISPOSE(ca->outputUnit);
|
|
Packit |
c32a2d |
AudioConverterDispose(ca->converter);
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
/* Free the ring buffer */
|
|
Packit |
c32a2d |
sfifo_close( &ca->fifo );
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
/* Free the conversion buffer */
|
|
Packit |
c32a2d |
if (ca->buffer) {
|
|
Packit |
c32a2d |
free( ca->buffer );
|
|
Packit |
c32a2d |
ca->buffer = NULL;
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
return 0;
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
static void flush_coreaudio(out123_handle *ao)
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
mpg123_coreaudio_t* ca = (mpg123_coreaudio_t*)ao->userptr;
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
/* Flush AudioConverter's buffer */
|
|
Packit |
c32a2d |
if(AudioConverterReset(ca->converter))
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
if(!AOQUIET)
|
|
Packit |
c32a2d |
error("AudioConverterReset failed");
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
/* Empty out the ring buffer */
|
|
Packit |
c32a2d |
sfifo_flush( &ca->fifo );
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
static int deinit_coreaudio(out123_handle* ao)
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
/* Free up memory */
|
|
Packit |
c32a2d |
if (ao->userptr) {
|
|
Packit |
c32a2d |
free( ao->userptr );
|
|
Packit |
c32a2d |
ao->userptr = NULL;
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
/* Success */
|
|
Packit |
c32a2d |
return 0;
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
static int init_coreaudio(out123_handle* ao)
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
if (ao==NULL) return -1;
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
/* Set callbacks */
|
|
Packit |
c32a2d |
ao->open = open_coreaudio;
|
|
Packit |
c32a2d |
ao->flush = flush_coreaudio;
|
|
Packit |
c32a2d |
ao->write = write_coreaudio;
|
|
Packit |
c32a2d |
ao->get_formats = get_formats_coreaudio;
|
|
Packit |
c32a2d |
ao->close = close_coreaudio;
|
|
Packit |
c32a2d |
ao->deinit = deinit_coreaudio;
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
/* Allocate memory for data structure */
|
|
Packit |
c32a2d |
ao->userptr = malloc( sizeof( mpg123_coreaudio_t ) );
|
|
Packit |
c32a2d |
if (ao->userptr==NULL)
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
if(!AOQUIET)
|
|
Packit |
c32a2d |
error("failed to malloc memory for 'mpg123_coreaudio_t'");
|
|
Packit |
c32a2d |
return -1;
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
memset( ao->userptr, 0, sizeof(mpg123_coreaudio_t) );
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
/* Success */
|
|
Packit |
c32a2d |
return 0;
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
/*
|
|
Packit |
c32a2d |
Module information data structure
|
|
Packit |
c32a2d |
*/
|
|
Packit |
c32a2d |
mpg123_module_t mpg123_output_module_info = {
|
|
Packit |
c32a2d |
/* api_version */ MPG123_MODULE_API_VERSION,
|
|
Packit |
c32a2d |
/* name */ "coreaudio",
|
|
Packit |
c32a2d |
/* description */ "Output audio using Mac OS X's CoreAudio.",
|
|
Packit |
c32a2d |
/* revision */ "$Rev:$",
|
|
Packit |
c32a2d |
/* handle */ NULL,
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
/* init_output */ init_coreaudio,
|
|
Packit |
c32a2d |
};
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
|