From 6c8a08fff1bde0008320d58af8f5bac8102c23ed Mon Sep 17 00:00:00 2001 From: Chris Moeller Date: Tue, 12 Apr 2016 21:16:25 -0700 Subject: [PATCH] Implemented MIDI flavor override control for Sound Canvas VA Audio Unit --- Application/AppController.m | 2 + Plugins/MIDI/MIDI.xcodeproj/project.pbxproj | 12 + Plugins/MIDI/MIDI/AUPlayer.h | 4 +- Plugins/MIDI/MIDI/AUPlayer.mm | 55 ++-- Plugins/MIDI/MIDI/BMPlayer.cpp | 43 --- Plugins/MIDI/MIDI/BMPlayer.h | 16 +- Plugins/MIDI/MIDI/MIDIDecoder.mm | 49 ++- Plugins/MIDI/MIDI/MIDIPlayer.cpp | 87 +---- Plugins/MIDI/MIDI/MIDIPlayer.h | 12 +- Plugins/MIDI/MIDI/SCCore.cpp | 183 +++++++++++ Plugins/MIDI/MIDI/SCCore.h | 54 ++++ Plugins/MIDI/MIDI/SCPlayer.cpp | 297 ++++++++++++++++++ Plugins/MIDI/MIDI/SCPlayer.h | 59 ++++ .../General/English.lproj/Preferences.xib | 67 +++- .../General/General.xcodeproj/project.pbxproj | 6 + .../MIDIFlavorBehaviorArrayController.h | 13 + .../MIDIFlavorBehaviorArrayController.m | 45 +++ Preferences/General/MIDIPane.h | 2 + Preferences/General/MIDIPane.m | 14 + .../MIDIPluginBehaviorArrayController.h | 2 +- .../MIDIPluginBehaviorArrayController.m | 2 +- 21 files changed, 830 insertions(+), 194 deletions(-) create mode 100644 Plugins/MIDI/MIDI/SCCore.cpp create mode 100644 Plugins/MIDI/MIDI/SCCore.h create mode 100644 Plugins/MIDI/MIDI/SCPlayer.cpp create mode 100644 Plugins/MIDI/MIDI/SCPlayer.h create mode 100644 Preferences/General/MIDIFlavorBehaviorArrayController.h create mode 100644 Preferences/General/MIDIFlavorBehaviorArrayController.m diff --git a/Application/AppController.m b/Application/AppController.m index 3fb2daa6f..e6d56e16b 100644 --- a/Application/AppController.m +++ b/Application/AppController.m @@ -501,6 +501,8 @@ increase/decrease as long as the user holds the left/right, plus/minus button */ [userDefaultsValuesDict setObject:@"dls appl" forKey:@"midi.plugin"]; + [userDefaultsValuesDict setObject:@"sc55" forKey:@"midi.flavor"]; + //Register and sync defaults [[NSUserDefaults standardUserDefaults] registerDefaults:userDefaultsValuesDict]; [[NSUserDefaults standardUserDefaults] synchronize]; diff --git a/Plugins/MIDI/MIDI.xcodeproj/project.pbxproj b/Plugins/MIDI/MIDI.xcodeproj/project.pbxproj index 14ec07366..3c7074882 100644 --- a/Plugins/MIDI/MIDI.xcodeproj/project.pbxproj +++ b/Plugins/MIDI/MIDI.xcodeproj/project.pbxproj @@ -27,6 +27,8 @@ 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 */; }; + 83DFEA071CBC87BB00BCC565 /* SCCore.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83DFEA031CBC87BB00BCC565 /* SCCore.cpp */; }; + 83DFEA081CBC87BB00BCC565 /* SCPlayer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83DFEA051CBC87BB00BCC565 /* SCPlayer.cpp */; }; 83E973471C4378880007F413 /* AUPlayer.mm in Sources */ = {isa = PBXBuildFile; fileRef = 83E973451C4378880007F413 /* AUPlayer.mm */; }; /* End PBXBuildFile section */ @@ -108,6 +110,10 @@ 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 = ""; }; + 83DFEA031CBC87BB00BCC565 /* SCCore.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SCCore.cpp; sourceTree = ""; }; + 83DFEA041CBC87BB00BCC565 /* SCCore.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SCCore.h; sourceTree = ""; }; + 83DFEA051CBC87BB00BCC565 /* SCPlayer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SCPlayer.cpp; sourceTree = ""; }; + 83DFEA061CBC87BB00BCC565 /* SCPlayer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SCPlayer.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 = ""; }; @@ -179,6 +185,10 @@ 83B06690180D5668008E3612 /* MIDI */ = { isa = PBXGroup; children = ( + 83DFEA031CBC87BB00BCC565 /* SCCore.cpp */, + 83DFEA041CBC87BB00BCC565 /* SCCore.h */, + 83DFEA051CBC87BB00BCC565 /* SCPlayer.cpp */, + 83DFEA061CBC87BB00BCC565 /* SCPlayer.h */, 83686AAD1C5C6A2700671C7A /* AUPlayerView.h */, 83686AAB1C5C69D400671C7A /* AUPlayerView.mm */, 83E973451C4378880007F413 /* AUPlayer.mm */, @@ -302,7 +312,9 @@ files = ( 83E973471C4378880007F413 /* AUPlayer.mm in Sources */, 83686AAC1C5C69D400671C7A /* AUPlayerView.mm in Sources */, + 83DFEA071CBC87BB00BCC565 /* SCCore.cpp in Sources */, 83B06709180D64DA008E3612 /* MIDIPlayer.cpp in Sources */, + 83DFEA081CBC87BB00BCC565 /* SCPlayer.cpp in Sources */, 83B06722180D70FE008E3612 /* MIDIDecoder.mm in Sources */, 83C35702180EDB74007E9DF0 /* MIDIContainer.mm in Sources */, 83B0670C180D6665008E3612 /* BMPlayer.cpp in Sources */, diff --git a/Plugins/MIDI/MIDI/AUPlayer.h b/Plugins/MIDI/MIDI/AUPlayer.h index 942e88c59..65185876d 100644 --- a/Plugins/MIDI/MIDI/AUPlayer.h +++ b/Plugins/MIDI/MIDI/AUPlayer.h @@ -31,8 +31,8 @@ public: void setComponent(OSType uSubType, OSType uManufacturer); protected: - virtual void send_event(uint32_t b, uint32_t sample_offset); - virtual void render_512(float * out); + virtual void send_event(uint32_t b); + virtual void render(float * out, unsigned long count); virtual void shutdown(); virtual bool startup(); diff --git a/Plugins/MIDI/MIDI/AUPlayer.mm b/Plugins/MIDI/MIDI/AUPlayer.mm index 83f3baaea..681dda739 100644 --- a/Plugins/MIDI/MIDI/AUPlayer.mm +++ b/Plugins/MIDI/MIDI/AUPlayer.mm @@ -37,7 +37,7 @@ AUPlayer::~AUPlayer() shutdown(); } -void AUPlayer::send_event(uint32_t b, uint32_t sample_offset) +void AUPlayer::send_event(uint32_t b) { #ifdef AUPLAYERVIEW int _port = -1; @@ -53,7 +53,7 @@ void AUPlayer::send_event(uint32_t b, uint32_t sample_offset) #ifdef AUPLAYERVIEW _port = (int)port; #endif - MusicDeviceMIDIEvent(samplerUnit[port], event[0], event[1], event[2], sample_offset); + MusicDeviceMIDIEvent(samplerUnit[port], event[0], event[1], event[2], 0); } else { @@ -83,35 +83,42 @@ void AUPlayer::send_event(uint32_t b, uint32_t sample_offset) #endif } -void AUPlayer::render_512(float * out) +void AUPlayer::render(float * out, unsigned long count) { float *ptrL, *ptrR; - memset(out, 0, 512 * sizeof(float) * 2); - for (unsigned long i = 0; i < 3; ++i) - { - AudioUnitRenderActionFlags ioActionFlags = 0; - UInt32 numberFrames = 512; - - for (unsigned long j = 0; j < 2; j++) + memset(out, 0, count * sizeof(float) * 2); + while (count) + { + UInt32 numberFrames = count > 512 ? 512 : count; + + for (unsigned long i = 0; i < 3; ++i) { - bufferList->mBuffers[j].mNumberChannels = 1; - bufferList->mBuffers[j].mDataByteSize = (UInt32) (512 * sizeof(float)); - bufferList->mBuffers[j].mData = audioBuffer + j * 512; - memset(bufferList->mBuffers[j].mData, 0, 512 * sizeof(float)); - } + AudioUnitRenderActionFlags ioActionFlags = 0; - AudioUnitRender(samplerUnit[i], &ioActionFlags, &mTimeStamp, 0, numberFrames, bufferList); + for (unsigned long j = 0; j < 2; j++) + { + bufferList->mBuffers[j].mNumberChannels = 1; + bufferList->mBuffers[j].mDataByteSize = (UInt32) (numberFrames * sizeof(float)); + bufferList->mBuffers[j].mData = audioBuffer + j * 512; + memset(bufferList->mBuffers[j].mData, 0, numberFrames * sizeof(float)); + } - ptrL = (float *) bufferList->mBuffers[0].mData; - ptrR = (float *) bufferList->mBuffers[1].mData; - for (unsigned long j = 0; j < 512; ++j) - { - out[j * 2 + 0] += ptrL[j]; - out[j * 2 + 1] += ptrR[j]; + 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 < numberFrames; ++j) + { + out[j * 2 + 0] += ptrL[j]; + out[j * 2 + 1] += ptrR[j]; + } } + + out += numberFrames * 2; + count -= numberFrames; + + mTimeStamp.mSampleTime += (double)numberFrames; } - - mTimeStamp.mSampleTime += 512.0; } void AUPlayer::shutdown() diff --git a/Plugins/MIDI/MIDI/BMPlayer.cpp b/Plugins/MIDI/MIDI/BMPlayer.cpp index 499ddc887..a74aa5132 100644 --- a/Plugins/MIDI/MIDI/BMPlayer.cpp +++ b/Plugins/MIDI/MIDI/BMPlayer.cpp @@ -248,11 +248,6 @@ void BMPlayer::send_event(uint32_t b) } } -void BMPlayer::send_event(uint32_t b, uint32_t sample_offset) -{ - _eventQueue.push_back(BASS_event(b, sample_offset)); -} - void BMPlayer::render(float * out, unsigned long count) { float buffer[1024]; @@ -275,44 +270,6 @@ void BMPlayer::render(float * out, unsigned long count) } } -void BMPlayer::render_512(float *out) -{ - unsigned long queue_index = 0; - unsigned long queue_count = _eventQueue.size(); - uint32_t current = 0; - uint32_t next = 512; - if (queue_count) - next = _eventQueue[0].sample_offset; - do - { - if (next > 512) - next = 512; - - if (next > current) - render(out + current * 2, next - current); - - current = next; - - while (queue_index < queue_count && _eventQueue[queue_index].sample_offset <= next) - { - send_event(_eventQueue[queue_index].b); - queue_index++; - } - if (queue_index < queue_count) - next = _eventQueue[queue_index].sample_offset; - else - next = 512; - } while (current < 512); - - if (queue_index < queue_count) - { - memmove(&_eventQueue[0], &_eventQueue[queue_index], sizeof(BASS_event) * (queue_count - queue_index)); - _eventQueue.resize(queue_count - queue_index); - } - else - _eventQueue.resize(0); -} - void BMPlayer::setSoundFont( const char * in ) { sSoundFontName = in; diff --git a/Plugins/MIDI/MIDI/BMPlayer.h b/Plugins/MIDI/MIDI/BMPlayer.h index 7a8267099..12567c712 100644 --- a/Plugins/MIDI/MIDI/BMPlayer.h +++ b/Plugins/MIDI/MIDI/BMPlayer.h @@ -20,11 +20,8 @@ public: void setSincInterpolation(bool enable = true); private: - void send_event(uint32_t b); - void render(float * out, unsigned long count); - - virtual void send_event(uint32_t b, uint32_t sample_offset); - virtual void render_512(float * out); + virtual void send_event(uint32_t b); + virtual void render(float * out, unsigned long count); virtual void shutdown(); virtual bool startup(); @@ -37,15 +34,6 @@ private: std::string sSoundFontName; std::string sFileSoundFontName; - typedef struct BASS_event - { - uint32_t b; - uint32_t sample_offset; - BASS_event() : b(0), sample_offset(0) { } - BASS_event(uint32_t _b, uint32_t _sample_offset) : b(_b), sample_offset(_sample_offset) { } - } BASS_event; - std::vector _eventQueue; - HSTREAM _stream[3]; bool bSincInterpolation; diff --git a/Plugins/MIDI/MIDI/MIDIDecoder.mm b/Plugins/MIDI/MIDI/MIDIDecoder.mm index 056b71032..8d7635a53 100755 --- a/Plugins/MIDI/MIDI/MIDIDecoder.mm +++ b/Plugins/MIDI/MIDI/MIDIDecoder.mm @@ -10,6 +10,7 @@ #import "AUPlayer.h" #import "BMPlayer.h" +#import "SCPlayer.h" #import "Logging.h" @@ -147,19 +148,49 @@ static OSType getOSType(const char * in_) componentSubType = getOSType(cplugin); componentManufacturer = getOSType(cplugin + 4); - - auplayer = new AUPlayer; - auplayer->setComponent(componentSubType, componentManufacturer); - auplayer->setSampleRate( 44100 ); - - if ( [soundFontPath length] ) + if (componentManufacturer == 'rolD' && componentSubType == 'Sc55') { - auplayer->setSoundFont( [soundFontPath UTF8String] ); - soundFontsAssigned = YES; + SCPlayer * scplayer = new SCPlayer; + + SCPlayer::sc_mode mode = SCPlayer::sc_sc55; + NSString * flavor = [[NSUserDefaults standardUserDefaults] stringForKey:@"midi.flavor"]; + if ([flavor isEqualToString:@"gm"]) + mode = SCPlayer::sc_gm; + else if ([flavor isEqualToString:@"gm2"]) + mode = SCPlayer::sc_gm2; + else if ([flavor isEqualToString:@"sc55"]) + mode = SCPlayer::sc_sc55; + else if ([flavor isEqualToString:@"sc88"]) + mode = SCPlayer::sc_sc88; + else if ([flavor isEqualToString:@"sc88pro"]) + mode = SCPlayer::sc_sc88pro; + else if ([flavor isEqualToString:@"sc8850"]) + mode = SCPlayer::sc_sc8850; + else if ([flavor isEqualToString:@"xg"]) + mode = SCPlayer::sc_xg; + + scplayer->set_sccore_path("/Library/Audio/Plug-Ins/Components/SOUND Canvas VA.component/Contents/Resources/SCCore00.dylib"); + scplayer->set_mode( mode ); + scplayer->setSampleRate( 44100 ); + + player = scplayer; } + else + { + auplayer = new AUPlayer; - player = auplayer; + auplayer->setComponent(componentSubType, componentManufacturer); + auplayer->setSampleRate( 44100 ); + + if ( [soundFontPath length] ) + { + auplayer->setSoundFont( [soundFontPath UTF8String] ); + soundFontsAssigned = YES; + } + + player = auplayer; + } } unsigned int loop_mode = framesFade ? MIDIPlayer::loop_mode_enable | MIDIPlayer::loop_mode_force : 0; diff --git a/Plugins/MIDI/MIDI/MIDIPlayer.cpp b/Plugins/MIDI/MIDI/MIDIPlayer.cpp index 2c55c3f23..8b6de007e 100644 --- a/Plugins/MIDI/MIDI/MIDIPlayer.cpp +++ b/Plugins/MIDI/MIDI/MIDIPlayer.cpp @@ -9,7 +9,6 @@ MIDIPlayer::MIDIPlayer() uTimeCurrent = 0; uTimeEnd = 0; uTimeLoopStart = 0; - uSamplesInBuffer = 0; } void MIDIPlayer::setSampleRate(unsigned long rate) @@ -159,52 +158,26 @@ unsigned long MIDIPlayer::Play(float * out, unsigned long count) midi_stream_event * me = &mStream[uStreamPosition]; unsigned long samples_todo = me->m_timestamp - uTimeCurrent; - while ( samples_todo >= 512 ) + if ( samples_todo ) { if ( samples_todo > count - done ) { uSamplesRemaining = samples_todo - ( count - done ); samples_todo = count - done; } - if ( uSamplesInBuffer ) - { - if ( samples_todo >= uSamplesInBuffer ) - { - unsigned int localSamplesInBuffer = uSamplesInBuffer; - render( out + done * 2, localSamplesInBuffer ); - done += localSamplesInBuffer; - uTimeCurrent += localSamplesInBuffer; - samples_todo -= localSamplesInBuffer; - } - else - { - render( out + done * 2, (uint32_t) samples_todo ); - done += samples_todo; - uTimeCurrent += samples_todo; - return done; - } - } - if ( samples_todo >= 512 ) - { - render_512( out + done * 2 ); - done += 512; - uTimeCurrent += 512; - samples_todo -= 512; - } - else - { - render( out + done * 2, (uint32_t) samples_todo ); - done += samples_todo; - uTimeCurrent += samples_todo; - } + render( out + done * 2, samples_todo ); + done += samples_todo; if ( uSamplesRemaining ) { + uTimeCurrent = me->m_timestamp; return done; } } - send_event( me->m_event, (uint32_t) samples_todo ); + send_event( me->m_event ); + + uTimeCurrent = me->m_timestamp; } } @@ -215,7 +188,7 @@ unsigned long MIDIPlayer::Play(float * out, unsigned long count) else samples_todo = uTimeEnd; samples_todo -= uTimeCurrent; if ( samples_todo > count - done ) samples_todo = count - done; - render( out + done * 2, (uint32_t) samples_todo ); + render( out + done * 2, samples_todo ); done += samples_todo; } @@ -227,7 +200,7 @@ unsigned long MIDIPlayer::Play(float * out, unsigned long count) { for (; uStreamPosition < mStream.size(); uStreamPosition++) { - send_event( mStream[ uStreamPosition ].m_event, 0 ); + send_event( mStream[ uStreamPosition ].m_event ); } } @@ -274,8 +247,6 @@ void MIDIPlayer::Seek(unsigned long sample) } if (!startup()) return; - - uSamplesInBuffer = 0; uTimeCurrent = sample; @@ -324,45 +295,7 @@ void MIDIPlayer::Seek(unsigned long sample) for (i = 0; i < stream_start; i++) { if (me[i].m_event) - send_event(me[i].m_event, 0); - } - } -} - -void MIDIPlayer::render(float * out, uint32_t count) -{ - if (uSamplesInBuffer) - { - if (uSamplesInBuffer >= count) - { - memcpy(out, fSampleBuffer, sizeof(float) * 2 * count); - uSamplesInBuffer -= count; - memmove(fSampleBuffer, fSampleBuffer + count * 2, sizeof(float) * 2 * uSamplesInBuffer); - return; - } - else - { - memcpy(out, fSampleBuffer, sizeof(float) * 2 * uSamplesInBuffer); - out += uSamplesInBuffer * 2; - count -= uSamplesInBuffer; - uSamplesInBuffer = 0; - } - } - while (count > 0) - { - if (count >= 512) - { - render_512(out); - out += 512 * 2; - count -= 512; - } - else - { - render_512(fSampleBuffer); - memcpy(out, fSampleBuffer, sizeof(float) * 2 * count); - uSamplesInBuffer = 512 - count; - memmove(fSampleBuffer, fSampleBuffer + count * 2, sizeof(float) * 2 * uSamplesInBuffer); - count = 0; + send_event(me[i].m_event); } } } diff --git a/Plugins/MIDI/MIDI/MIDIPlayer.h b/Plugins/MIDI/MIDI/MIDIPlayer.h index 8ce57a895..440f822b5 100644 --- a/Plugins/MIDI/MIDI/MIDIPlayer.h +++ b/Plugins/MIDI/MIDI/MIDIPlayer.h @@ -20,16 +20,15 @@ public: // setup void setSampleRate(unsigned long rate); + void SetLoopMode(unsigned int mode); bool Load(const midi_container & midi_file, unsigned subsong, unsigned loop_mode, unsigned clean_flags); unsigned long Play(float * out, unsigned long count); void Seek(unsigned long sample); - - void SetLoopMode(unsigned mode); protected: - virtual void send_event(uint32_t b, uint32_t sample_offset) {} - virtual void render_512(float * out) {} + virtual void send_event(uint32_t b) {} + virtual void render(float * out, unsigned long count) {} virtual void shutdown() {}; virtual bool startup() {return false;} @@ -38,8 +37,6 @@ protected: system_exclusive_table mSysexMap; private: - void render(float * out, uint32_t count); - unsigned long uSamplesRemaining; unsigned uLoopMode; @@ -53,9 +50,6 @@ private: unsigned long uStreamLoopStart; unsigned long uTimeLoopStart; unsigned long uStreamEnd; - - unsigned int uSamplesInBuffer; - float fSampleBuffer[512 * 2]; }; #endif diff --git a/Plugins/MIDI/MIDI/SCCore.cpp b/Plugins/MIDI/MIDI/SCCore.cpp new file mode 100644 index 000000000..af90bcf00 --- /dev/null +++ b/Plugins/MIDI/MIDI/SCCore.cpp @@ -0,0 +1,183 @@ +#include +#include +#include +#include +#include + +#include "SCCore.h" + +SCCore::SCCore() +{ + duped = false; + path = 0; + + handle = 0; + + TG_initialize = 0; + //TG_terminate = 0; + TG_activate = 0; + TG_deactivate = 0; + TG_setSampleRate = 0; + TG_setMaxBlockSize = 0; + TG_flushMidi = 0; + TG_setInterruptThreadIdAtThisTime = 0; + // TG_PMidiIn = 0; + TG_ShortMidiIn = 0; + TG_LongMidiIn = 0; + // TG_isFatalError = 0; + // TG_getErrorStrings = 0; + TG_XPgetCurTotalRunningVoices = 0; + // TG_XPsetSystemConfig = 0; + // TG_XPgetCurSystemConfig = 0; + TG_Process = 0; +} + +SCCore::~SCCore() +{ + Unload(); +} + +void SCCore::Unload() +{ + if (handle) + { + dlclose(handle); + handle = 0; + } + if (duped && path) + { + unlink(path); + duped = false; + } + if (path) + { + free(path); + path = 0; + } +} + +static const char name_template[] = "/tmp/SCCore.dylib.XXXXXXXX"; + +bool SCCore::Load(const char * _path, bool dupe) +{ + uintptr_t size = 0; + + if (dupe) + { + path = (char *) malloc(strlen(name_template) + 1); + strcpy(path, name_template); + mktemp(path); + + const char * uniq = path + strlen(path) - 8; + + FILE * f = fopen(_path, "rb"); + if (!f) return false; + + fseek(f, 0, SEEK_END); + size_t fs = ftell(f); + fseek(f, 0, SEEK_SET); + + size = fs; + + unsigned char * buffer = (unsigned char *) malloc(fs); + if (!fs) + { + fclose(f); + return false; + } + + fread(buffer, 1, fs, f); + fclose(f); + + for (size_t i = 0; i < fs - 14; ++i) + { + if (memcmp(buffer + i, "SCCore00.dylib", 14) == 0) + { + memcpy(buffer + i, uniq, 8); + i += 13; + } + } + + duped = true; + + f = fopen(path, "wb"); + if (!f) + { + free(buffer); + return false; + } + + fwrite(buffer, 1, fs, f); + fclose(f); + + free(buffer); + } + else + { + path = (char *) malloc(strlen(_path) + 1); + strcpy(path, _path); + + FILE * f = fopen(path, "rb"); + fseek(f, 0, SEEK_END); + size = ftell(f); + fclose(f); + } + + handle = dlopen(path, RTLD_LAZY | RTLD_LOCAL); + if (handle) + { + *(void**)&TG_initialize = dlsym(handle, "TG_initialize"); + //*(void**)&TG_terminate = dlsym(handle, "TG_terminate"); + *(void**)&TG_activate = dlsym(handle, "TG_activate"); + *(void**)&TG_deactivate = dlsym(handle, "TG_deactivate"); + *(void**)&TG_setSampleRate = dlsym(handle, "TG_setSampleRate"); + *(void**)&TG_setMaxBlockSize = dlsym(handle, "TG_setMaxBlockSize"); + *(void**)&TG_flushMidi = dlsym(handle, "TG_flushMidi"); + *(void**)&TG_setInterruptThreadIdAtThisTime = dlsym(handle, "TG_setInterruptThreadIdAtThisTime"); + //*(void**)&TG_PMidiIn = dlsym(handle, "TG_PMidiIn"); + *(void**)&TG_ShortMidiIn = dlsym(handle, "TG_ShortMidiIn"); + *(void**)&TG_LongMidiIn = dlsym(handle, "TG_LongMidiIn"); + //*(void**)&TG_isFatalError = dlsym(handle, "TG_isFatalError"); + //*(void**)&TG_getErrorStrings = dlsym(handle, "TG_getErrorStrings"); + *(void**)&TG_XPgetCurTotalRunningVoices = dlsym(handle, "TG_XPgetCurTotalRunningVoices"); + //*(void**)&TG_XPsetSystemConfig = dlsym(handle, "TG_XPsetSystemConfig"); + //*(void**)&TG_XPgetCurSystemConfig = dlsym(handle, "TG_XPgetCurSystemConfig"); + *(void**)&TG_Process = dlsym(handle, "TG_Process"); + + if (TG_initialize && /*TG_terminate &&*/ TG_activate && TG_deactivate && + TG_setSampleRate && TG_setMaxBlockSize && TG_flushMidi && + TG_setInterruptThreadIdAtThisTime && /*TG_PMidiIn &&*/ + TG_ShortMidiIn && TG_LongMidiIn && /*TG_isFatalError && + TG_getErrorStrings &&*/ TG_XPgetCurTotalRunningVoices && + /*TG_XPsetSystemConfig && TG_XPgetCurSystemConfig &&*/ + TG_Process) + { + return true; + } + else + { + TG_initialize = 0; + //TG_terminate = 0; + TG_activate = 0; + TG_deactivate = 0; + TG_setSampleRate = 0; + TG_setMaxBlockSize = 0; + TG_flushMidi = 0; + TG_setInterruptThreadIdAtThisTime = 0; + // TG_PMidiIn = 0; + TG_ShortMidiIn = 0; + TG_LongMidiIn = 0; + // TG_isFatalError = 0; + // TG_getErrorStrings = 0; + TG_XPgetCurTotalRunningVoices = 0; + // TG_XPsetSystemConfig = 0; + // TG_XPgetCurSystemConfig = 0; + TG_Process = 0; + + dlclose(handle); + handle = 0; + } + } + + return false; +} diff --git a/Plugins/MIDI/MIDI/SCCore.h b/Plugins/MIDI/MIDI/SCCore.h new file mode 100644 index 000000000..daa999bf1 --- /dev/null +++ b/Plugins/MIDI/MIDI/SCCore.h @@ -0,0 +1,54 @@ +#ifndef _SCCore_h_ +#define _SCCore_h_ + +// Static single instance - duplicate library to temp path for unique instance + +class SCCore +{ + bool duped; + char * path; + void * handle; + +public: + int (* TG_initialize)(int i); // i = 0, returns negative on failure + + //void (* TG_terminate)(); // Unused + + void (* TG_activate)(float sampleRate, int blockSize); + + void (* TG_deactivate)(); // Unused - hopefully cleans up + + void (*TG_setSampleRate)(float sampleRate); + + void (*TG_setMaxBlockSize)(unsigned int blockSize); + + void (*TG_flushMidi)(); // Called after applying presets + + void (*TG_setInterruptThreadIdAtThisTime)(); + + //void (*TG_PMidiIn)(MpPacket *, int count); // Unknown + + void (*TG_ShortMidiIn)(unsigned int eventCode, unsigned int deltaFrames); + + void (*TG_LongMidiIn)(const unsigned char * sysEx, unsigned int deltaFrames); + + //void (*TG_isFatalError)(int errCode); // Unused + + //void (*TG_getErrorStrings)(int errCode); // Unused + + unsigned int (*TG_XPgetCurTotalRunningVoices)(); // Unused + + //void (*TG_XPsetSystemConfig)(); + + //void (*TG_XPgetCurSystemConfig)(); + + void (*TG_Process)(float * left, float * right, unsigned int count); + + SCCore(); + ~SCCore(); + + bool Load(const char * path, bool dupe); + void Unload(); +}; + +#endif diff --git a/Plugins/MIDI/MIDI/SCPlayer.cpp b/Plugins/MIDI/MIDI/SCPlayer.cpp new file mode 100644 index 000000000..592825dec --- /dev/null +++ b/Plugins/MIDI/MIDI/SCPlayer.cpp @@ -0,0 +1,297 @@ +#include +#include + +#include +#include +#include + +#include "SCPlayer.h" + +// YAY! OS X doesn't unload dylibs on dlclose, so we cache up to two sets of instances here + +static pthread_mutex_t g_lock = PTHREAD_MUTEX_INITIALIZER; +static const unsigned int g_max_instances = 2; +static std::vector g_instances_open; +static SCCore g_sampler[3 * g_max_instances]; + +SCPlayer::SCPlayer() : MIDIPlayer(), initialized(false), mode(sc_default), sccore_path(0) +{ + pthread_mutex_lock(&g_lock); + while (g_instances_open.size() >= g_max_instances) + { + pthread_mutex_unlock(&g_lock); + usleep(10000); + pthread_mutex_lock(&g_lock); + } + unsigned int i; + for (i = 0; i < g_max_instances; ++i) + { + if (std::find(g_instances_open.begin(), g_instances_open.end(), i) == g_instances_open.end()) + break; + } + g_instances_open.push_back(i); + instance_id = i; + sampler = &g_sampler[i * 3]; + pthread_mutex_unlock(&g_lock); +} + +SCPlayer::~SCPlayer() +{ + shutdown(); + if (sccore_path) + free(sccore_path); + if (sampler) + { + pthread_mutex_lock(&g_lock); + auto it = std::find(g_instances_open.begin(), g_instances_open.end(), instance_id); + it = g_instances_open.erase(it); + pthread_mutex_unlock(&g_lock); + } +} + +static const uint8_t syx_reset_gm[] = { 0xF0, 0x7E, 0x7F, 0x09, 0x01, 0xF7 }; +static const uint8_t syx_reset_gm2[] = { 0xF0, 0x7E, 0x7F, 0x09, 0x03, 0xF7 }; +static const uint8_t syx_reset_gs[] = { 0xF0, 0x41, 0x10, 0x42, 0x12, 0x40, 0x00, 0x7F, 0x00, 0x41, 0xF7 }; +static const uint8_t syx_reset_xg[] = { 0xF0, 0x43, 0x10, 0x4C, 0x00, 0x00, 0x7E, 0x00, 0xF7 }; + +static const uint8_t syx_gs_limit_bank_lsb[] = { 0xF0, 0x41, 0x10, 0x42, 0x12, 0x40, 0x41, 0x00, 0x03, 0x00, 0xF7 }; + +static bool syx_equal(const uint8_t * a, const uint8_t * b) +{ + while (*a != 0xF7 && *b != 0xF7 && *a == *b) + { + a++; b++; + } + + return *a == *b; +} + +static bool syx_is_reset(const uint8_t * data) +{ + return syx_equal(data, syx_reset_gm) || syx_equal(data, syx_reset_gm2) || syx_equal(data, syx_reset_gs) || syx_equal(data, syx_reset_xg); +} + +void SCPlayer::send_sysex(uint32_t port, const uint8_t * data) +{ + sampler[port].TG_LongMidiIn( data, 0 ); + if (syx_is_reset(data)) + { + reset(port); + } +} + +void SCPlayer::send_gs(uint32_t port, uint8_t * data) +{ + unsigned long i; + unsigned char checksum = 0; + for (i = 5; data[i+1] != 0xF7; ++i) + checksum += data[i]; + checksum = (128 - checksum) & 127; + data[i] = checksum; + sampler[port].TG_LongMidiIn( data, 0 ); +} + +void SCPlayer::reset_sc(uint32_t port) +{ + unsigned int i; + uint8_t message[11]; + + memcpy(message, syx_gs_limit_bank_lsb, 11); + + message[7] = 1; + + switch (mode) + { + default: break; + + case sc_sc55: + message[8] = 1; + break; + + case sc_sc88: + message[8] = 2; + break; + + case sc_sc88pro: + message[8] = 3; + break; + + case sc_sc8850: + message[8] = 4; + break; + } + + for (i = 0x41; i <= 0x49; ++i) + { + message[6] = i; + send_gs(port, message); + } + message[6] = 0x40; + send_gs(port, message); + for (i = 0x4A; i <= 0x4F; ++i) + { + message[6] = i; + send_gs(port, message); + } +} + +void SCPlayer::reset(uint32_t port) +{ + if (initialized) + { + switch (mode) + { + case sc_gm: + sampler[port].TG_LongMidiIn( syx_reset_gm, 0 ); + break; + + case sc_gm2: + sampler[port].TG_LongMidiIn( syx_reset_gm2, 0 ); + break; + + case sc_sc55: + case sc_sc88: + case sc_sc88pro: + case sc_sc8850: + reset_sc(port); + break; + + case sc_xg: + sampler[port].TG_LongMidiIn( syx_reset_xg, 0 ); + sampler[port].TG_ShortMidiIn( 0x7F00B9, 0 ); // fix drum channel + break; + } + } +} + +void SCPlayer::set_mode(sc_mode m) +{ + mode = m; + reset(0); + reset(1); + reset(2); +} + +void SCPlayer::set_sccore_path(const char *path) +{ + size_t len; + if (sccore_path) free(sccore_path); + len = strlen(path); + sccore_path = (char *) malloc(len + 1); + if (sccore_path) + memcpy(sccore_path, path, len + 1); +} + +void SCPlayer::send_event(uint32_t b) +{ + if (!(b & 0x80000000)) + { + unsigned port = (b >> 24) & 0x7F; + if ( port > 2 ) port = 2; + sampler[port].TG_ShortMidiIn(b, 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; + send_sysex( (uint32_t) port, data ); + if ( port == 0 ) + { + send_sysex( 1, data ); + send_sysex( 2, data ); + } + } +} + +void SCPlayer::render(float * out, unsigned long count) +{ + memset(out, 0, count * sizeof(float) * 2); + while (count) + { + float buffer[2][4096]; + unsigned long todo = count > 4096 ? 4096 : count; + for (unsigned long i = 0; i < 3; ++i) + { + memset(buffer[0], 0, todo * sizeof(float)); + memset(buffer[1], 0, todo * sizeof(float)); + + sampler[i].TG_setInterruptThreadIdAtThisTime(); + sampler[i].TG_Process(buffer[0], buffer[1], (unsigned int) todo); + + for (unsigned long j = 0; j < todo; ++j) + { + out[j * 2 + 0] += buffer[0][j]; + out[j * 2 + 1] += buffer[1][j]; + } + } + out += todo * 2; + count -= todo; + } +} + +void SCPlayer::shutdown() +{ + for (int i = 0; i < 3; i++) + { + if (sampler[i].TG_deactivate) + { + sampler[i].TG_flushMidi(); + sampler[i].TG_deactivate(); + } + } + initialized = false; +} + +bool SCPlayer::startup() +{ + int i; + + if (initialized) return true; + + if (!sccore_path) return false; + + for (i = 0; i < 3; i++) + { + if (!sampler[i].TG_initialize) + { + if (!sampler[i].Load(sccore_path, true)) + return false; + + if (sampler[i].TG_initialize(0) < 0) + return false; + } + + sampler[i].TG_activate(44100.0, 1024); + sampler[i].TG_setMaxBlockSize(256); + sampler[i].TG_setSampleRate((float)uSampleRate); + sampler[i].TG_setSampleRate((float)uSampleRate); + sampler[i].TG_setMaxBlockSize(4096); + } + + initialized = true; + + for (int i = 0; i < 3; i++) + { + reset(i); + sampler[i].TG_flushMidi(); + } + + return true; +} + +unsigned int SCPlayer::get_playing_note_count() +{ + unsigned int total = 0; + unsigned int i; + + if (!initialized) + return 0; + + for (i = 0; i < 3; i++) + total += sampler[i].TG_XPgetCurTotalRunningVoices(); + + return total; +} diff --git a/Plugins/MIDI/MIDI/SCPlayer.h b/Plugins/MIDI/MIDI/SCPlayer.h new file mode 100644 index 000000000..d15635ac8 --- /dev/null +++ b/Plugins/MIDI/MIDI/SCPlayer.h @@ -0,0 +1,59 @@ +#ifndef __SCPlayer_h__ +#define __SCPlayer_h__ + +#include "MIDIPlayer.h" + +#include "SCCore.h" + +class SCPlayer : public MIDIPlayer +{ +public: + // zero variables + SCPlayer(); + + // close, unload + virtual ~SCPlayer(); + + unsigned int get_playing_note_count(); + + typedef enum + { + sc_gm = 0, + sc_gm2, + sc_sc55, + sc_sc88, + sc_sc88pro, + sc_sc8850, + sc_xg, + sc_default = sc_sc55 + } + sc_mode; + + void set_mode(sc_mode m); + + void set_sccore_path(const char * path); + +protected: + virtual void send_event(uint32_t b); + virtual void render(float * out, unsigned long count); + + virtual void shutdown(); + virtual bool startup(); + +private: + void send_sysex(uint32_t port, const uint8_t * data); + void send_gs(uint32_t port, uint8_t * data); + void reset_sc(uint32_t port); + + void reset(uint32_t port); + + unsigned int instance_id; + bool initialized; + SCCore * sampler; + + sc_mode mode; + + char * sccore_path; +}; + +#endif diff --git a/Preferences/General/English.lproj/Preferences.xib b/Preferences/General/English.lproj/Preferences.xib index 8009ad086..aa14187b1 100644 --- a/Preferences/General/English.lproj/Preferences.xib +++ b/Preferences/General/English.lproj/Preferences.xib @@ -1,8 +1,8 @@ - + - + @@ -270,7 +270,8 @@ - + + @@ -483,11 +484,11 @@ - + - + @@ -507,7 +508,7 @@ - + @@ -524,16 +525,16 @@ - + - + - + @@ -542,7 +543,7 @@ - + @@ -556,15 +557,46 @@ + - + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -584,7 +616,7 @@ - + @@ -600,5 +632,12 @@ preference + + + name + slug + preference + + diff --git a/Preferences/General/General.xcodeproj/project.pbxproj b/Preferences/General/General.xcodeproj/project.pbxproj index 31dfb3971..f888eb8ac 100644 --- a/Preferences/General/General.xcodeproj/project.pbxproj +++ b/Preferences/General/General.xcodeproj/project.pbxproj @@ -26,6 +26,7 @@ 8384918C1808596A00E7332D /* NDHotKey.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = 838491841808588D00E7332D /* NDHotKey.framework */; }; 83B06729180D85B8008E3612 /* MIDIPane.m in Sources */ = {isa = PBXBuildFile; fileRef = 83B06728180D85B8008E3612 /* MIDIPane.m */; }; 83B0672B180D8B39008E3612 /* midi.png in Resources */ = {isa = PBXBuildFile; fileRef = 83B0672A180D8B39008E3612 /* midi.png */; }; + 83DFEA0B1CBC94DE00BCC565 /* MIDIFlavorBehaviorArrayController.m in Sources */ = {isa = PBXBuildFile; fileRef = 83DFEA0A1CBC94DE00BCC565 /* MIDIFlavorBehaviorArrayController.m */; }; 83EF495F17FBC96A00642E3C /* VolumeBehaviorArrayController.m in Sources */ = {isa = PBXBuildFile; fileRef = 83EF495E17FBC96A00642E3C /* VolumeBehaviorArrayController.m */; }; 83F27E6B1810DD3A00CEF538 /* appearance@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 83F27E651810DD3A00CEF538 /* appearance@2x.png */; }; 83F27E6C1810DD3A00CEF538 /* growl@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 83F27E661810DD3A00CEF538 /* growl@2x.png */; }; @@ -107,6 +108,8 @@ 83B06727180D85B8008E3612 /* MIDIPane.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MIDIPane.h; sourceTree = ""; }; 83B06728180D85B8008E3612 /* MIDIPane.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MIDIPane.m; sourceTree = ""; }; 83B0672A180D8B39008E3612 /* midi.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = midi.png; path = Icons/midi.png; sourceTree = ""; }; + 83DFEA091CBC94DE00BCC565 /* MIDIFlavorBehaviorArrayController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MIDIFlavorBehaviorArrayController.h; sourceTree = ""; }; + 83DFEA0A1CBC94DE00BCC565 /* MIDIFlavorBehaviorArrayController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MIDIFlavorBehaviorArrayController.m; sourceTree = ""; }; 83EF495D17FBC96A00642E3C /* VolumeBehaviorArrayController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = VolumeBehaviorArrayController.h; sourceTree = ""; }; 83EF495E17FBC96A00642E3C /* VolumeBehaviorArrayController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = VolumeBehaviorArrayController.m; sourceTree = ""; }; 83F27E651810DD3A00CEF538 /* appearance@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "appearance@2x.png"; path = "Icons/appearance@2x.png"; sourceTree = ""; }; @@ -233,6 +236,8 @@ 17D503410ABDB1660022D1E8 /* Custom */ = { isa = PBXGroup; children = ( + 83DFEA091CBC94DE00BCC565 /* MIDIFlavorBehaviorArrayController.h */, + 83DFEA0A1CBC94DE00BCC565 /* MIDIFlavorBehaviorArrayController.m */, 837C0D3E1C50954000CAE18F /* MIDIPluginBehaviorArrayController.h */, 837C0D3F1C50954000CAE18F /* MIDIPluginBehaviorArrayController.m */, 8372053518E3DEAF007EFAD4 /* ResamplerBehaviorArrayController.h */, @@ -417,6 +422,7 @@ 8E07AA890AAC8EA200A4B32F /* GeneralPreferencePane.m in Sources */, 8E07AA8A0AAC8EA200A4B32F /* GeneralPreferencesPlugin.m in Sources */, 8E6C13A00AACBAB500819171 /* HotKeyControl.m in Sources */, + 83DFEA0B1CBC94DE00BCC565 /* MIDIFlavorBehaviorArrayController.m in Sources */, 83EF495F17FBC96A00642E3C /* VolumeBehaviorArrayController.m in Sources */, 17C643380B8A77CC00C53518 /* OutputsArrayController.m in Sources */, 837C0D401C50954000CAE18F /* MIDIPluginBehaviorArrayController.m in Sources */, diff --git a/Preferences/General/MIDIFlavorBehaviorArrayController.h b/Preferences/General/MIDIFlavorBehaviorArrayController.h new file mode 100644 index 000000000..2511ec14c --- /dev/null +++ b/Preferences/General/MIDIFlavorBehaviorArrayController.h @@ -0,0 +1,13 @@ +// +// MIDIFlavorBehaviorArrayController.h +// General +// +// Created by Christopher Snowhill on 04/12/16. +// +// + +#import + +@interface MIDIFlavorBehaviorArrayController : NSArrayController + +@end diff --git a/Preferences/General/MIDIFlavorBehaviorArrayController.m b/Preferences/General/MIDIFlavorBehaviorArrayController.m new file mode 100644 index 000000000..2c6d99259 --- /dev/null +++ b/Preferences/General/MIDIFlavorBehaviorArrayController.m @@ -0,0 +1,45 @@ +// +// MIDIFlavorBehaviorArrayController.m +// General +// +// Created by Christopher Snowhill on 04/12/16. +// +// + +#import "MIDIFlavorBehaviorArrayController.h" + +@implementation MIDIFlavorBehaviorArrayController +- (void)awakeFromNib +{ + [self removeObjects:[self arrangedObjects]]; + + [self addObject: + [NSDictionary dictionaryWithObjectsAndKeys: + @"General MIDI", @"name", @"gm", @"preference", nil]]; + + [self addObject: + [NSDictionary dictionaryWithObjectsAndKeys: + @"General MIDI 2", @"name", @"gm2", @"preference", nil]]; + + [self addObject: + [NSDictionary dictionaryWithObjectsAndKeys: + @"Roland SC-55", @"name", @"sc55", @"preference", nil]]; + + [self addObject: + [NSDictionary dictionaryWithObjectsAndKeys: + @"Roland SC-88", @"name", @"sc88", @"preference", nil]]; + + [self addObject: + [NSDictionary dictionaryWithObjectsAndKeys: + @"Roland SC-88 Pro", @"name", @"sc88pro", @"preference", nil]]; + + [self addObject: + [NSDictionary dictionaryWithObjectsAndKeys: + @"Roland SC-8850", @"name", @"sc8850", @"preference", nil]]; + + [self addObject: + [NSDictionary dictionaryWithObjectsAndKeys: + @"Yamaha XG", @"name", @"xg", @"preference", nil]]; +} + +@end diff --git a/Preferences/General/MIDIPane.h b/Preferences/General/MIDIPane.h index f98ccb618..7ddadb338 100644 --- a/Preferences/General/MIDIPane.h +++ b/Preferences/General/MIDIPane.h @@ -10,8 +10,10 @@ #import "GeneralPreferencePane.h" @interface MIDIPane : GeneralPreferencePane { + IBOutlet NSPopUpButton *midiFlavorControl; } - (IBAction)setSoundFont:(id)sender; +- (IBAction)setMidiPlugin:(id)sender; @end diff --git a/Preferences/General/MIDIPane.m b/Preferences/General/MIDIPane.m index d93985309..ef4a3290e 100644 --- a/Preferences/General/MIDIPane.m +++ b/Preferences/General/MIDIPane.m @@ -10,6 +10,12 @@ @implementation MIDIPane +- (void)awakeFromNib +{ + if ([[[NSUserDefaults standardUserDefaults] stringForKey:@"midi.plugin"] isEqualToString:@"Sc55rolD"]) + [midiFlavorControl setEnabled:YES]; +} + - (NSString *)title { return NSLocalizedStringFromTableInBundle(@"Synthesis", nil, [NSBundle bundleForClass:[self class]], @""); @@ -39,4 +45,12 @@ } } +- (IBAction)setMidiPlugin:(id)sender +{ + if ([[[NSUserDefaults standardUserDefaults] stringForKey:@"midi.plugin"] isEqualToString:@"Sc55rolD"]) + [midiFlavorControl setEnabled:YES]; + else + [midiFlavorControl setEnabled:NO]; +} + @end diff --git a/Preferences/General/MIDIPluginBehaviorArrayController.h b/Preferences/General/MIDIPluginBehaviorArrayController.h index 7ba2a67c6..e4f825eb4 100644 --- a/Preferences/General/MIDIPluginBehaviorArrayController.h +++ b/Preferences/General/MIDIPluginBehaviorArrayController.h @@ -1,5 +1,5 @@ // -// ResamplerBehaviorArrayController.h +// MIDIPluginBehaviorArrayController.h // General // // Created by Christopher Snowhill on 03/26/14. diff --git a/Preferences/General/MIDIPluginBehaviorArrayController.m b/Preferences/General/MIDIPluginBehaviorArrayController.m index a1dcb0b82..9b0dfebe7 100644 --- a/Preferences/General/MIDIPluginBehaviorArrayController.m +++ b/Preferences/General/MIDIPluginBehaviorArrayController.m @@ -1,5 +1,5 @@ // -// ResamplerBehaviorArrayController.m +// MIDIPluginBehaviorArrayController.m // General // // Created by Christopher Snowhill on 03/26/14.