#ifdef __APPLE__ #include #include #include #include #include #include #include #include "musapi.h" enum { CAErr_Warning = -2, CAErr_Error = -1, CAErr_Ok = 0, CAErr_Uninitialised, CAErr_AssembleAUGraph, CAErr_InitialiseAUGraph, CAErr_SetPCMFormat, CAErr_Mutex, CAErr_SetSoundBank }; enum { CASystem_none = 0, CASystem_pcm = 1, CASystem_midi = 2 }; static int ErrorCode = CAErr_Ok; static int Initialised = CASystem_none; static pthread_mutex_t pcmmutex, midimutex; static AUGraph graph = 0; static AudioUnit mixerunit, pcmunit, synthunit; static unsigned int MidiFrameOffset = 0; static unsigned int MidiTick = 0; static unsigned int MidiFramesPerTick = 0; static char *MixBuffer = 0; static int MixBufferSize = 0; static int MixBufferCount = 0; static int MixBufferCurrent = 0; static int MixBufferUsed = 0; static void ( *MixCallBack )( void ) = 0; static void ( *MidiCallBack)(void) = 0; static char soundBankName[PATH_MAX+1] = ""; /*************************************************************************** _ASS_MessageOutputString() - ***************************************************************************/ static void _ASS_MessageOutputString( const char *str ) { fputs(str, stderr); } void (*ASS_MessageOutputString)(const char *) = _ASS_MessageOutputString; /*************************************************************************** ASS_Message() - ***************************************************************************/ void ASS_Message( const char *fmt, ... ) { char text[256]; va_list va; va_start(va, fmt); vsnprintf(text, sizeof(text), fmt, va); va_end(va); ASS_MessageOutputString(text); } #define check_result(fcall, errval) \ if ((result = (fcall)) != noErr) {\ ASS_Message("CoreAudioDrv: error %d at line %d:" #fcall "\n", (int)result, __LINE__);\ ErrorCode = errval;\ return CAErr_Error;\ } /*************************************************************************** CoreAudioDrv_PCM_Unlock() - ***************************************************************************/ void CoreAudioDrv_PCM_Unlock( void ) { if (!(Initialised & CASystem_pcm)) { return; } pthread_mutex_unlock(&pcmmutex); } /*************************************************************************** CoreAudioDrv_PCM_Lock() - ***************************************************************************/ void CoreAudioDrv_PCM_Lock( void ) { if (!(Initialised & CASystem_pcm)) { return; } pthread_mutex_lock(&pcmmutex); } /*************************************************************************** CoreAudioDrv_MIDI_Lock() - ***************************************************************************/ void CoreAudioDrv_MIDI_Lock( void ) { if (!(Initialised & CASystem_midi)) { return; } pthread_mutex_lock(&midimutex); } /*************************************************************************** CoreAudioDrv_MIDI_Unlock() - ***************************************************************************/ void CoreAudioDrv_MIDI_Unlock( void ) { if (!(Initialised & CASystem_midi)) { return; } pthread_mutex_unlock(&midimutex); } /*************************************************************************** pcmService() - ***************************************************************************/ static OSStatus pcmService( void *inRefCon, AudioUnitRenderActionFlags *inActionFlags, const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber, UInt32 inNumberFrames, AudioBufferList *ioData ) { UInt32 remaining, len, bufn; char *ptr, *sptr; (void)inRefCon; (void)inActionFlags; (void)inTimeStamp; (void)inBusNumber; (void)inNumberFrames; if (MixCallBack == 0) return noErr; CoreAudioDrv_PCM_Lock(); for (bufn = 0; bufn < ioData->mNumberBuffers; bufn++) { remaining = ioData->mBuffers[bufn].mDataByteSize; ptr = (char*) ioData->mBuffers[bufn].mData; while (remaining > 0) { if (MixBufferUsed == MixBufferSize) { MixCallBack(); MixBufferUsed = 0; MixBufferCurrent++; if (MixBufferCurrent >= MixBufferCount) { MixBufferCurrent -= MixBufferCount; } } while (remaining > 0 && MixBufferUsed < MixBufferSize) { sptr = MixBuffer + (MixBufferCurrent * MixBufferSize) + MixBufferUsed; len = MixBufferSize - MixBufferUsed; if (remaining < len) { len = remaining; } memcpy(ptr, sptr, len); ptr += len; MixBufferUsed += len; remaining -= len; } } } CoreAudioDrv_PCM_Unlock(); return noErr; } /*************************************************************************** midiService() - ***************************************************************************/ static OSStatus midiService( void *inRefCon, AudioUnitRenderActionFlags *ioActionFlags, const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber, UInt32 inNumberFrames, AudioBufferList *ioData ) { int secondsThisCall = (inNumberFrames << 16) / 44100; (void)inRefCon; (void)inTimeStamp; (void)inBusNumber; (void)ioData; if (MidiCallBack == 0) return noErr; if (!(*ioActionFlags & kAudioUnitRenderAction_PreRender)) return noErr; CoreAudioDrv_MIDI_Lock(); while (MidiFrameOffset < inNumberFrames) { MidiCallBack(); MidiTick++; MidiFrameOffset += MidiFramesPerTick; } MidiFrameOffset -= inNumberFrames; CoreAudioDrv_MIDI_Unlock(); return noErr; } /*************************************************************************** CORE_Init() - ***************************************************************************/ int CORE_Init( int subsystem ) { OSStatus result; AudioComponentDescription desc; AURenderCallbackStruct callback; AudioStreamBasicDescription pcmDesc; AUNode outputnode, mixernode, pcmnode, synthnode; memset(&pcmDesc, 0, sizeof(pcmDesc)); if (Initialised) { Initialised |= subsystem; return CAErr_Ok; } if (pthread_mutex_init(&pcmmutex, 0) < 0) { ErrorCode = CAErr_Mutex; return CAErr_Error; } if (pthread_mutex_init(&midimutex, 0) < 0) { ErrorCode = CAErr_Mutex; return CAErr_Error; } // create the graph check_result(NewAUGraph(&graph), CAErr_AssembleAUGraph); // add the output node to the graph desc.componentType = kAudioUnitType_Output; desc.componentSubType = kAudioUnitSubType_DefaultOutput; desc.componentManufacturer = kAudioUnitManufacturer_Apple; desc.componentFlags = 0; desc.componentFlagsMask = 0; check_result(AUGraphAddNode(graph, &desc, &outputnode), CAErr_AssembleAUGraph); // add the mixer node to the graph desc.componentType = kAudioUnitType_Mixer; desc.componentSubType = kAudioUnitSubType_StereoMixer; desc.componentManufacturer = kAudioUnitManufacturer_Apple; desc.componentFlags = 0; desc.componentFlagsMask = 0; check_result(AUGraphAddNode(graph, &desc, &mixernode), CAErr_AssembleAUGraph); // add a node for PCM audio desc.componentType = kAudioUnitType_FormatConverter; desc.componentSubType = kAudioUnitSubType_AUConverter; desc.componentManufacturer = kAudioUnitManufacturer_Apple; desc.componentFlags = 0; desc.componentFlagsMask = 0; check_result(AUGraphAddNode(graph, &desc, &pcmnode), CAErr_AssembleAUGraph); // add a node for a MIDI synth desc.componentType = kAudioUnitType_MusicDevice; desc.componentSubType = kAudioUnitSubType_DLSSynth; desc.componentManufacturer = kAudioUnitManufacturer_Apple; desc.componentFlags = 0; desc.componentFlagsMask = 0; check_result(AUGraphAddNode(graph, &desc, &synthnode), CAErr_AssembleAUGraph); // connect the nodes together check_result(AUGraphConnectNodeInput(graph, pcmnode, 0, mixernode, 0), CAErr_AssembleAUGraph); check_result(AUGraphConnectNodeInput(graph, synthnode, 0, mixernode, 1), CAErr_AssembleAUGraph); check_result(AUGraphConnectNodeInput(graph, mixernode, 0, outputnode, 0), CAErr_AssembleAUGraph); // open the nodes check_result(AUGraphOpen(graph), CAErr_AssembleAUGraph); // get the units check_result(AUGraphNodeInfo(graph, pcmnode, NULL, &pcmunit), CAErr_AssembleAUGraph); check_result(AUGraphNodeInfo(graph, synthnode, NULL, &synthunit), CAErr_AssembleAUGraph); check_result(AUGraphNodeInfo(graph, mixernode, NULL, &mixerunit), CAErr_AssembleAUGraph); // set the mixer bus count UInt32 buscount = 2; check_result(AudioUnitSetProperty(mixerunit, kAudioUnitProperty_ElementCount, kAudioUnitScope_Input, 0, &buscount, sizeof(buscount)), CAErr_InitialiseAUGraph); // set the pcm audio callback callback.inputProc = pcmService; callback.inputProcRefCon = 0; check_result(AUGraphSetNodeInputCallback(graph, pcmnode, 0, &callback), CAErr_InitialiseAUGraph); // set a default pcm audio format pcmDesc.mFormatID = kAudioFormatLinearPCM; pcmDesc.mFormatFlags = kLinearPCMFormatFlagIsPacked; pcmDesc.mChannelsPerFrame = 2; pcmDesc.mSampleRate = 44100; pcmDesc.mBitsPerChannel = 16; pcmDesc.mFormatFlags |= kLinearPCMFormatFlagIsSignedInteger; #ifdef __POWERPC__ pcmDesc.mFormatFlags |= kLinearPCMFormatFlagIsBigEndian; #endif pcmDesc.mFramesPerPacket = 1; pcmDesc.mBytesPerFrame = pcmDesc.mBitsPerChannel * pcmDesc.mChannelsPerFrame / 8; pcmDesc.mBytesPerPacket = pcmDesc.mBytesPerFrame * pcmDesc.mFramesPerPacket; check_result(AudioUnitSetProperty(pcmunit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 0, &pcmDesc, sizeof(pcmDesc)), CAErr_SetPCMFormat); // Set a sound bank for the DLS synth if (soundBankName[0]) { CFURLRef url = CFURLCreateFromFileSystemRepresentation(NULL, (const UInt8 *)soundBankName, strlen(soundBankName), FALSE); if (url) { check_result(AudioUnitSetProperty(synthunit, kMusicDeviceProperty_SoundBankURL, kAudioUnitScope_Global, 0, &url, sizeof(url)), CAErr_SetSoundBank); CFRelease(url); } } // set the synth notify callback check_result(AudioUnitAddRenderNotify(synthunit, midiService, NULL), CAErr_InitialiseAUGraph); // initialise the graph check_result(AUGraphInitialize(graph), CAErr_InitialiseAUGraph); // start the graph check_result(AUGraphStart(graph), CAErr_InitialiseAUGraph); //CAShow(graph); Initialised |= subsystem; return CAErr_Ok; } /*************************************************************************** CORE_DeInit() - ***************************************************************************/ void CORE_DeInit( void ) { OSStatus result; CoreAudioDrv_PCM_Lock(); CoreAudioDrv_MIDI_Lock(); // clean up the graph AUGraphStop(graph); DisposeAUGraph(graph); graph = 0; CoreAudioDrv_MIDI_Unlock(); CoreAudioDrv_PCM_Unlock(); pthread_mutex_destroy(&pcmmutex); pthread_mutex_destroy(&midimutex); } /*************************************************************************** MPU_MapChannel() - ***************************************************************************/ static unsigned int MPU_MapChannel( unsigned chan ) { if (chan < 9) return chan; if (chan == 15) return 9; return chan +1; } /*************************************************************************** KeyOffEvent() - ***************************************************************************/ static void KeyOffEvent( unsigned int chan, unsigned int key ) { MusicDeviceMIDIEvent(synthunit, 0x80 | MPU_MapChannel(chan), key, 0, MidiFrameOffset); } /*************************************************************************** KeyOnEvent() - ***************************************************************************/ static void KeyOnEvent( int chan, unsigned int key, unsigned int volume ) { MusicDeviceMIDIEvent(synthunit, 0x90 | MPU_MapChannel(chan), key, volume, MidiFrameOffset); } /*************************************************************************** ProgramEvent() - ***************************************************************************/ static void ProgramEvent( unsigned int chan, unsigned int param ) { MusicDeviceMIDIEvent(synthunit, 0xC0 | MPU_MapChannel(chan), param, 0, MidiFrameOffset); } /*************************************************************************** PitchBendEvent() - ***************************************************************************/ static void PitchBendEvent( unsigned int chan, int bend ) { int lsb; int msb; bend <<= 6; lsb = bend; msb = bend; lsb &= 0x7F; msb >>= 7; MusicDeviceMIDIEvent(synthunit, 0xE0 | MPU_MapChannel(chan), lsb, msb, MidiFrameOffset); } /*************************************************************************** AllNotesOffEvent() - ***************************************************************************/ static void AllNotesOffEvent( unsigned int chan, unsigned int param ) { MusicDeviceMIDIEvent(synthunit, 0x7B | chan, param, 0, MidiFrameOffset); } /*************************************************************************** ControllerEvent() - ***************************************************************************/ static void ControllerEvent( unsigned int chan, unsigned int controller, unsigned int param ) { static int event_map[] = { 0, 0, 1, 7, 10, 11, 91, 93, 64, 67, 120, 123, -1, -1, 121, -1 }; MusicDeviceMIDIEvent(synthunit, 0xB0 | MPU_MapChannel(chan), event_map[controller], param, MidiFrameOffset); } musdevice_t mus_device_corea = { CORE_Init, CORE_DeInit, NULL, KeyOffEvent, KeyOnEvent, ControllerEvent, PitchBendEvent, ProgramEvent, AllNotesOffEvent }; #endif