You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

279 lines
7.0 KiB

/* Extended Module Player
* Copyright (C) 1996-2012 Claudio Matsuoka and Hipolito Carraro Jr
* CoreAudio helpers (C) 2000 Timothy J. Wood
*
* This file is part of the Extended Module Player and is distributed
* under the terms of the GNU General Public License. See doc/COPYING
* for more information.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <CoreAudio/CoreAudio.h>
#include <AudioUnit/AudioUnit.h>
#include <AudioToolbox/AudioToolbox.h>
#include <CoreServices/CoreServices.h>
#include <unistd.h>
#include "common.h"
#include "driver.h"
#include "mixer.h"
static int init (struct context_data *ctx);
static void bufdump (struct context_data *, void *, int);
static void shutdown (struct context_data *);
static void dummy () { }
struct xmp_drv_info drv_osx = {
"osx", /* driver ID */
"OSX CoreAudio", /* driver description */
NULL, /* help */
init, /* init */
shutdown, /* shutdown */
dummy, /* starttimer */
dummy, /* flush */
bufdump, /* bufdump */
};
static AudioUnit au;
/*
* CoreAudio helpers from mplayer/libao
* The player fills a ring buffer, OSX retrieves data from the buffer
*/
static int paused;
static uint8 *buffer;
static int buffer_len;
static int buf_write_pos;
static int buf_read_pos;
static int num_chunks;
static int chunk_size;
static int packet_size;
/* return minimum number of free bytes in buffer, value may change between
* two immediately following calls, and the real number of free bytes
* might actually be larger! */
static int buf_free()
{
int free = buf_read_pos - buf_write_pos - chunk_size;
if (free < 0)
free += buffer_len;
return free;
}
/* return minimum number of buffered bytes, value may change between
* two immediately following calls, and the real number of buffered bytes
* might actually be larger! */
static int buf_used()
{
int used = buf_write_pos - buf_read_pos;
if (used < 0)
used += buffer_len;
return used;
}
/* add data to ringbuffer */
static int write_buffer(unsigned char *data, int len)
{
int first_len = buffer_len - buf_write_pos;
int free = buf_free();
if (len > free)
len = free;
if (first_len > len)
first_len = len;
/* till end of buffer */
memcpy(buffer + buf_write_pos, data, first_len);
if (len > first_len) { /* we have to wrap around */
/* remaining part from beginning of buffer */
memcpy(buffer, data + first_len, len - first_len);
}
buf_write_pos = (buf_write_pos + len) % buffer_len;
return len;
}
/* remove data from ringbuffer */
static int read_buffer(unsigned char *data, int len)
{
int first_len = buffer_len - buf_read_pos;
int buffered = buf_used();
if (len > buffered)
len = buffered;
if (first_len > len)
first_len = len;
/* till end of buffer */
memcpy(data, buffer + buf_read_pos, first_len);
if (len > first_len) { /* we have to wrap around */
/* remaining part from beginning of buffer */
memcpy(data + first_len, buffer, len - first_len);
}
buf_read_pos = (buf_read_pos + len) % buffer_len;
return len;
}
OSStatus render_proc(void *inRefCon,
AudioUnitRenderActionFlags *inActionFlags,
const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber,
UInt32 inNumFrames, AudioBufferList *ioData)
{
int amt = buf_used();
int req = inNumFrames * packet_size;
if (amt > req)
amt = req;
read_buffer((unsigned char *)ioData->mBuffers[0].mData, amt);
ioData->mBuffers[0].mDataByteSize = amt;
return noErr;
}
/*
* end of CoreAudio helpers
*/
static int init(struct context_data *ctx)
{
struct xmp_options *o = &ctx->o;
AudioStreamBasicDescription ad;
Component comp;
ComponentDescription cd;
AURenderCallbackStruct rc;
//char *token, **parm;
OSStatus err;
UInt32 size, max_frames;
//parm_init();
//parm_end();
ad.mSampleRate = o->freq;
ad.mFormatID = kAudioFormatLinearPCM;
ad.mFormatFlags = kAudioFormatFlagIsPacked |
kAudioFormatFlagIsSignedInteger;
if (o->big_endian)
ad.mFormatFlags |= kAudioFormatFlagIsBigEndian;
else
ad.mFormatFlags &= ~kAudioFormatFlagIsBigEndian;
ad.mChannelsPerFrame = o->outfmt & XMP_FORMAT_MONO ? 1 : 2;
ad.mBitsPerChannel = o->resol;
ad.mBytesPerFrame = o->resol / 8 * ad.mChannelsPerFrame;
ad.mBytesPerPacket = ad.mBytesPerFrame;
ad.mFramesPerPacket = 1;
packet_size = ad.mFramesPerPacket * ad.mChannelsPerFrame *
(ad.mBitsPerChannel / 8);
cd.componentType = kAudioUnitType_Output;
cd.componentSubType = kAudioUnitSubType_DefaultOutput;
cd.componentManufacturer = kAudioUnitManufacturer_Apple;
cd.componentFlags = 0;
cd.componentFlagsMask = 0;
if ((comp = FindNextComponent(NULL, &cd)) == NULL) {
fprintf(stderr, "error: FindNextComponent\n");
return XMP_ERR_DINIT;
}
if ((err = OpenAComponent(comp, &au))) {
fprintf(stderr, "error: OpenAComponent (%d)\n", (int)err);
return XMP_ERR_DINIT;
}
if ((err = AudioUnitInitialize(au))) {
fprintf(stderr, "error: AudioUnitInitialize (%d)\n", (int)err);
return XMP_ERR_DINIT;
}
if ((err = AudioUnitSetProperty(au, kAudioUnitProperty_StreamFormat,
kAudioUnitScope_Input, 0, &ad, sizeof(ad)))) {
fprintf(stderr, "error: AudioUnitSetProperty: StreamFormat (%d)\n", (int)err);
fprintf(stderr, "mSampleRate = %lf\n", ad.mSampleRate);
fprintf(stderr, "mFormatID = 0x%x\n", (unsigned)ad.mFormatID);
fprintf(stderr, "mFormatFlags = 0x%x\n", (unsigned)ad.mFormatFlags);
fprintf(stderr, "mChannelsPerFrame = %d\n", (int)ad.mChannelsPerFrame);
fprintf(stderr, "mBitsPerChannel = %d\n", (int)ad.mBitsPerChannel);
fprintf(stderr, "mBytesPerFrame = %d\n", (int)ad.mBytesPerFrame);
fprintf(stderr, "mBytesPerPacket = %d\n", (int)ad.mBytesPerPacket);
fprintf(stderr, "mFramesPerPacket = %d\n", (int)ad.mFramesPerPacket);
return XMP_ERR_DINIT;
}
size = sizeof(UInt32);
if ((err = AudioUnitGetProperty(au, kAudioDevicePropertyBufferSize,
kAudioUnitScope_Input, 0, &max_frames, &size))) {
fprintf(stderr, "error: AudioUnitGetProperty: BufferSize (%d)\n", (int)err);
return XMP_ERR_DINIT;
}
chunk_size = max_frames;
num_chunks = (o->freq * ad.mBytesPerFrame + chunk_size - 1) /
chunk_size;
buffer_len = (num_chunks + 1) * chunk_size;
buffer = calloc(num_chunks + 1, chunk_size);
rc.inputProc = render_proc;
rc.inputProcRefCon = 0;
buf_read_pos = 0;
buf_write_pos = 0;
paused = 1;
if ((err = AudioUnitSetProperty(au, kAudioUnitProperty_SetRenderCallback,
kAudioUnitScope_Input, 0, &rc, sizeof(rc)))) {
fprintf(stderr, "error: AudioUnitSetProperty: SetRenderCallback (%d)\n", (int)err);
return XMP_ERR_DINIT;
}
return 0;
}
/* Build and write one tick (one PAL frame or 1/50 s in standard vblank
* timed mods) of audio data to the output device.
*/
static void bufdump(struct context_data *ctx, void *b, int i)
{
int j = 0;
/* block until we have enough free space in the buffer */
while (buf_free() < i)
usleep(100000);
while (i) {
if ((j = write_buffer(b, i)) > 0) {
i -= j;
b += j;
} else
break;
}
if (paused) {
AudioOutputUnitStart(au);
paused = 0;
}
}
static void shutdown(struct context_data *ctx)
{
AudioOutputUnitStop(au);
AudioUnitUninitialize(au);
CloseComponent(au);
free(buffer);
}