Blame src/libout123/modules/coreaudio.c

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