diff --git a/Plugins/MIDI/MIDI.xcodeproj/project.pbxproj b/Plugins/MIDI/MIDI.xcodeproj/project.pbxproj index 89a18eebb..9ddf527da 100644 --- a/Plugins/MIDI/MIDI.xcodeproj/project.pbxproj +++ b/Plugins/MIDI/MIDI.xcodeproj/project.pbxproj @@ -7,6 +7,7 @@ objects = { /* Begin PBXBuildFile section */ + 8398F2E01C438C7D00EB9639 /* AudioUnit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8398F2DF1C438C7D00EB9639 /* AudioUnit.framework */; }; 839CA224180D902100553DBA /* midi_processing.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = 83B066E0180D56BA008E3612 /* midi_processing.framework */; }; 83B0668B180D5668008E3612 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 83B0668A180D5668008E3612 /* Cocoa.framework */; }; 83B06695180D5668008E3612 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 83B06693180D5668008E3612 /* InfoPlist.strings */; }; @@ -24,6 +25,7 @@ 83B06722180D70FE008E3612 /* MIDIDecoder.mm in Sources */ = {isa = PBXBuildFile; fileRef = 83B06721180D70FE008E3612 /* MIDIDecoder.mm */; }; 83C35702180EDB74007E9DF0 /* MIDIContainer.mm in Sources */ = {isa = PBXBuildFile; fileRef = 83C35700180EDB74007E9DF0 /* MIDIContainer.mm */; }; 83C35705180EDD1C007E9DF0 /* MIDIMetadataReader.mm in Sources */ = {isa = PBXBuildFile; fileRef = 83C35703180EDD1C007E9DF0 /* MIDIMetadataReader.mm */; }; + 83E973471C4378880007F413 /* AUPlayer.mm in Sources */ = {isa = PBXBuildFile; fileRef = 83E973451C4378880007F413 /* AUPlayer.mm */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -72,6 +74,7 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ + 8398F2DF1C438C7D00EB9639 /* AudioUnit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AudioUnit.framework; path = System/Library/Frameworks/AudioUnit.framework; sourceTree = SDKROOT; }; 83B06687180D5668008E3612 /* MIDI.bundle */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = MIDI.bundle; sourceTree = BUILT_PRODUCTS_DIR; }; 83B0668A180D5668008E3612 /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = System/Library/Frameworks/Cocoa.framework; sourceTree = SDKROOT; }; 83B0668D180D5668008E3612 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; @@ -99,6 +102,8 @@ 83C35701180EDB74007E9DF0 /* MIDIContainer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MIDIContainer.h; sourceTree = ""; }; 83C35703180EDD1C007E9DF0 /* MIDIMetadataReader.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MIDIMetadataReader.mm; sourceTree = ""; }; 83C35704180EDD1C007E9DF0 /* MIDIMetadataReader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MIDIMetadataReader.h; sourceTree = ""; }; + 83E973451C4378880007F413 /* AUPlayer.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = AUPlayer.mm; sourceTree = ""; }; + 83E973461C4378880007F413 /* AUPlayer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AUPlayer.h; sourceTree = ""; }; 83FAF8A618ADD60100057CAF /* PlaylistController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = PlaylistController.h; path = ../../../Playlist/PlaylistController.h; sourceTree = ""; }; /* End PBXFileReference section */ @@ -107,6 +112,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 8398F2E01C438C7D00EB9639 /* AudioUnit.framework in Frameworks */, 83B0670F180D6F7F008E3612 /* libbass.dylib in Frameworks */, 83B06701180D5747008E3612 /* midi_processing.framework in Frameworks */, 83B06710180D6F7F008E3612 /* libbassmidi.dylib in Frameworks */, @@ -137,6 +143,7 @@ 83B06689180D5668008E3612 /* Frameworks */ = { isa = PBXGroup; children = ( + 8398F2DF1C438C7D00EB9639 /* AudioUnit.framework */, 83B06714180D6FC8008E3612 /* libbass_mpc.dylib */, 83B06715180D6FC8008E3612 /* libbassflac.dylib */, 83B06716180D6FC8008E3612 /* libbassopus.dylib */, @@ -163,6 +170,8 @@ 83B06690180D5668008E3612 /* MIDI */ = { isa = PBXGroup; children = ( + 83E973451C4378880007F413 /* AUPlayer.mm */, + 83E973461C4378880007F413 /* AUPlayer.h */, 83FAF8A618ADD60100057CAF /* PlaylistController.h */, 83C35703180EDD1C007E9DF0 /* MIDIMetadataReader.mm */, 83C35704180EDD1C007E9DF0 /* MIDIMetadataReader.h */, @@ -280,6 +289,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 83E973471C4378880007F413 /* AUPlayer.mm in Sources */, 83B06709180D64DA008E3612 /* MIDIPlayer.cpp in Sources */, 83B06722180D70FE008E3612 /* MIDIDecoder.mm in Sources */, 83C35702180EDB74007E9DF0 /* MIDIContainer.mm in Sources */, diff --git a/Plugins/MIDI/MIDI/AUPlayer.h b/Plugins/MIDI/MIDI/AUPlayer.h new file mode 100644 index 000000000..e4cf7803f --- /dev/null +++ b/Plugins/MIDI/MIDI/AUPlayer.h @@ -0,0 +1,46 @@ +#ifndef __AUPlayer_h__ +#define __AUPlayer_h__ + +#include "MIDIPlayer.h" + +#import +#import +#import + +class AUPlayer : public MIDIPlayer +{ +public: + // zero variables + AUPlayer(); + + // close, unload + virtual ~AUPlayer(); + + // configuration + void showDialog(); + + typedef void (*callback)(const char * name); + void enumComponents(callback cbEnum); + + void setComponent(const char * name); + +protected: + virtual void send_event(uint32_t b); + virtual void render(float * out, unsigned long count); + + virtual void shutdown(); + virtual bool startup(); + +private: + AudioTimeStamp mTimeStamp; + + AudioUnit samplerUnit[3]; + + AudioBufferList *bufferList; + + float *audioBuffer; + + char *mComponentName; +}; + +#endif diff --git a/Plugins/MIDI/MIDI/AUPlayer.mm b/Plugins/MIDI/MIDI/AUPlayer.mm new file mode 100644 index 000000000..b37321419 --- /dev/null +++ b/Plugins/MIDI/MIDI/AUPlayer.mm @@ -0,0 +1,276 @@ +#include "AUPlayer.h" + +#include + +#define SF2PACK + +#define _countof(arr) (sizeof(arr) / sizeof((arr)[0])) + +AUPlayer::AUPlayer() : MIDIPlayer() +{ + samplerUnit[0] = NULL; + samplerUnit[1] = NULL; + samplerUnit[2] = NULL; + bufferList = NULL; + audioBuffer = NULL; + + mComponentName = NULL; +} + +AUPlayer::~AUPlayer() +{ + shutdown(); +} + +void AUPlayer::send_event(uint32_t b) +{ + if (!(b & 0x80000000)) + { + unsigned char event[ 3 ]; + event[ 0 ] = (unsigned char)b; + event[ 1 ] = (unsigned char)( b >> 8 ); + event[ 2 ] = (unsigned char)( b >> 16 ); + unsigned port = (b >> 24) & 0x7F; + if ( port > 2 ) port = 2; + MusicDeviceMIDIEvent(samplerUnit[port], event[0], event[1], event[2], 0); + } + else + { + uint32_t n = b & 0xffffff; + const uint8_t * data; + std::size_t size, port; + mSysexMap.get_entry( n, data, size, port ); + if ( port > 2 ) port = 2; + MusicDeviceSysEx(samplerUnit[port], data, (UInt32) size); + if ( port == 0 ) + { + MusicDeviceSysEx(samplerUnit[1], data, (UInt32) size); + MusicDeviceSysEx(samplerUnit[2], data, (UInt32) size); + } + } +} + +void AUPlayer::render(float * out, unsigned long count) +{ + float *ptrL, *ptrR; + while (count) + { + unsigned long todo = count; + if (todo > 512) + todo = 512; + memset(out, 0, todo * sizeof(float) * 2); + for (unsigned long i = 0; i < 3; ++i) + { + AudioUnitRenderActionFlags ioActionFlags = 0; + UInt32 numberFrames = (UInt32) todo; + + for (unsigned long j = 0; j < 2; j++) + { + bufferList->mBuffers[j].mNumberChannels = 1; + bufferList->mBuffers[j].mDataByteSize = (UInt32) (todo * sizeof(float)); + bufferList->mBuffers[j].mData = audioBuffer + j * 512; + memset(bufferList->mBuffers[j].mData, 0, todo * sizeof(float)); + } + + AudioUnitRender(samplerUnit[i], &ioActionFlags, &mTimeStamp, 0, numberFrames, bufferList); + + ptrL = (float *) bufferList->mBuffers[0].mData; + ptrR = (float *) bufferList->mBuffers[1].mData; + for (unsigned long j = 0; j < todo; ++j) + { + out[j * 2 + 0] += ptrL[j]; + out[j * 2 + 1] += ptrR[j]; + } + } + + mTimeStamp.mSampleTime += (Float64) todo; + + out += todo * 2; + count -= todo; + } +} + +void AUPlayer::shutdown() +{ + if ( samplerUnit[2] ) + { + AudioUnitUninitialize( samplerUnit[2] ); + AudioComponentInstanceDispose( samplerUnit[2] ); + samplerUnit[2] = NULL; + } + if ( samplerUnit[1] ) + { + AudioUnitUninitialize( samplerUnit[1] ); + AudioComponentInstanceDispose( samplerUnit[1] ); + samplerUnit[1] = NULL; + } + if ( samplerUnit[0] ) + { + AudioUnitUninitialize( samplerUnit[0] ); + AudioComponentInstanceDispose( samplerUnit[0] ); + samplerUnit[0] = NULL; + } + if (audioBuffer) + { + free(audioBuffer); + audioBuffer = NULL; + } + if (bufferList) + { + free(bufferList); + bufferList = NULL; + } +} + +void AUPlayer::enumComponents(callback cbEnum) +{ + AudioComponentDescription cd = {0}; + cd.componentType = kAudioUnitType_MusicDevice; + + AudioComponent comp = NULL; + + const char * bytes; + char bytesBuffer[512]; + + comp = AudioComponentFindNext(comp, &cd); + + while (comp != NULL) + { + CFStringRef cfName; + AudioComponentCopyName(comp, &cfName); + bytes = CFStringGetCStringPtr(cfName, kCFStringEncodingUTF8); + if (!bytes) + { + CFStringGetCString(cfName, bytesBuffer, sizeof(bytesBuffer) - 1, kCFStringEncodingUTF8); + bytes = bytesBuffer; + } + cbEnum(bytes); + CFRelease(cfName); + comp = AudioComponentFindNext(comp, &cd); + } +} + +void AUPlayer::setComponent(const char *name) +{ + if (mComponentName) + { + free(mComponentName); + mComponentName = NULL; + } + + size_t size = strlen(name) + 1; + mComponentName = (char *) malloc(size); + memcpy(mComponentName, name, size); +} + +bool AUPlayer::startup() +{ + if (bufferList) return true; + + AudioComponentDescription cd = {0}; + cd.componentType = kAudioUnitType_MusicDevice; + + AudioComponent comp = NULL; + + const char * pComponentName = mComponentName; + + const char * bytes; + char bytesBuffer[512]; + + comp = AudioComponentFindNext(comp, &cd); + + if (pComponentName == NULL) + { + pComponentName = "Roland: SOUND Canvas VA"; + //pComponentName = "Apple: DLSMusicDevice"; + } + + while (comp != NULL) + { + CFStringRef cfName; + AudioComponentCopyName(comp, &cfName); + bytes = CFStringGetCStringPtr(cfName, kCFStringEncodingUTF8); + if (!bytes) + { + CFStringGetCString(cfName, bytesBuffer, sizeof(bytesBuffer) - 1, kCFStringEncodingUTF8); + bytes = bytesBuffer; + } + if (!strcmp(bytes, pComponentName)) + { + CFRelease(cfName); + break; + } + CFRelease(cfName); + comp = AudioComponentFindNext(comp, &cd); + } + + if (!comp) + return false; + + OSStatus error; + + for (int i = 0; i < 3; i++) + { + error = AudioComponentInstanceNew(comp, &samplerUnit[i]); + + if (error != noErr) + return false; + + Float64 sampleRateIn = 0, sampleRateOut = 0; + UInt32 sampleRateSize = sizeof (sampleRateIn); + const Float64 sr = uSampleRate; + + AudioUnitGetProperty(samplerUnit[i], kAudioUnitProperty_SampleRate, kAudioUnitScope_Input, 0, &sampleRateIn, &sampleRateSize); + + if (sampleRateIn != sr) + AudioUnitSetProperty(samplerUnit[i], kAudioUnitProperty_SampleRate, kAudioUnitScope_Input, 0, &sr, sizeof (sr)); + + AudioUnitGetProperty (samplerUnit[i], kAudioUnitProperty_SampleRate, kAudioUnitScope_Output, 0, &sampleRateOut, &sampleRateSize); + + if (sampleRateOut != sr) + AudioUnitSetProperty (samplerUnit[i], kAudioUnitProperty_SampleRate, kAudioUnitScope_Output, i, &sr, sizeof (sr)); + + AudioUnitReset (samplerUnit[i], kAudioUnitScope_Input, 0); + AudioUnitReset (samplerUnit[i], kAudioUnitScope_Output, 0); + + AudioUnitReset (samplerUnit[i], kAudioUnitScope_Global, 0); + + { + AudioStreamBasicDescription stream = { 0 }; + stream.mSampleRate = uSampleRate; + stream.mFormatID = kAudioFormatLinearPCM; + stream.mFormatFlags = kAudioFormatFlagsNativeFloatPacked | kAudioFormatFlagIsNonInterleaved | kAudioFormatFlagsNativeEndian; + stream.mFramesPerPacket = 1; + stream.mBytesPerPacket = 4; + stream.mBytesPerFrame = 4; + stream.mBitsPerChannel = 32; + stream.mChannelsPerFrame = 2; + + AudioUnitSetProperty (samplerUnit[i], kAudioUnitProperty_StreamFormat, + kAudioUnitScope_Input, 0, &stream, sizeof (stream)); + + AudioUnitSetProperty (samplerUnit[i], kAudioUnitProperty_StreamFormat, + kAudioUnitScope_Output, 0, &stream, sizeof (stream)); + } + + error = AudioUnitInitialize(samplerUnit[i]); + + if (error != noErr) + return false; + } + + bufferList = (AudioBufferList *) calloc(1, sizeof(AudioBufferList) + sizeof(AudioBuffer)); + if (!bufferList) + return false; + + audioBuffer = (float *) malloc(1024 * sizeof(float)); + if (!audioBuffer) + return false; + + bufferList->mNumberBuffers = 2; + + memset(&mTimeStamp, 0, sizeof(mTimeStamp)); + mTimeStamp.mFlags = kAudioTimeStampSampleTimeValid; + + return true; +}