diff --git a/Application/AppController.m b/Application/AppController.m index c735c8943..3fb2daa6f 100644 --- a/Application/AppController.m +++ b/Application/AppController.m @@ -499,6 +499,8 @@ increase/decrease as long as the user holds the left/right, plus/minus button */ [userDefaultsValuesDict setObject:[NSNumber numberWithInteger:-1] forKey:@"lastTrackPlaying"]; [userDefaultsValuesDict setObject:[NSNumber numberWithDouble:0] forKey:@"lastTrackPosition"]; + [userDefaultsValuesDict setObject:@"dls appl" forKey:@"midi.plugin"]; + //Register and sync defaults [[NSUserDefaults standardUserDefaults] registerDefaults:userDefaultsValuesDict]; [[NSUserDefaults standardUserDefaults] synchronize]; diff --git a/Plugins/MIDI/MIDI/AUPlayer.h b/Plugins/MIDI/MIDI/AUPlayer.h index e4cf7803f..5eefe25bb 100644 --- a/Plugins/MIDI/MIDI/AUPlayer.h +++ b/Plugins/MIDI/MIDI/AUPlayer.h @@ -3,6 +3,8 @@ #include "MIDIPlayer.h" +//#include + #import #import #import @@ -17,21 +19,28 @@ public: virtual ~AUPlayer(); // configuration - void showDialog(); + /*void setSoundFont( const char * in ); + void setFileSoundFont( const char * in );*/ + //void showDialog(); - typedef void (*callback)(const char * name); - void enumComponents(callback cbEnum); + typedef void (*callback)(OSType uSubType, OSType uManufacturer, const char * name); + static void enumComponents(callback cbEnum); - void setComponent(const char * name); + void setComponent(OSType uSubType, OSType uManufacturer); protected: - virtual void send_event(uint32_t b); - virtual 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 shutdown(); virtual bool startup(); private: + /*void loadSoundFont(const char * name); + + std::string sSoundFontName; + std::string sFileSoundFontName;*/ + AudioTimeStamp mTimeStamp; AudioUnit samplerUnit[3]; @@ -40,7 +49,7 @@ private: float *audioBuffer; - char *mComponentName; + OSType componentSubType, componentManufacturer; }; #endif diff --git a/Plugins/MIDI/MIDI/AUPlayer.mm b/Plugins/MIDI/MIDI/AUPlayer.mm index b37321419..41ded9582 100644 --- a/Plugins/MIDI/MIDI/AUPlayer.mm +++ b/Plugins/MIDI/MIDI/AUPlayer.mm @@ -14,7 +14,8 @@ AUPlayer::AUPlayer() : MIDIPlayer() bufferList = NULL; audioBuffer = NULL; - mComponentName = NULL; + componentSubType = kAudioUnitSubType_DLSSynth; + componentManufacturer = kAudioUnitManufacturer_Apple; } AUPlayer::~AUPlayer() @@ -22,7 +23,7 @@ AUPlayer::~AUPlayer() shutdown(); } -void AUPlayer::send_event(uint32_t b) +void AUPlayer::send_event(uint32_t b, uint32_t sample_offset) { if (!(b & 0x80000000)) { @@ -32,7 +33,7 @@ void AUPlayer::send_event(uint32_t b) 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); + MusicDeviceMIDIEvent(samplerUnit[port], event[0], event[1], event[2], sample_offset); } else { @@ -50,44 +51,35 @@ void AUPlayer::send_event(uint32_t b) } } -void AUPlayer::render(float * out, unsigned long count) +void AUPlayer::render_512(float * out) { 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) + 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++) { - 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]; - } + 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)); + } + + 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 < 512; ++j) + { + out[j * 2 + 0] += ptrL[j]; + out[j * 2 + 1] += ptrR[j]; } - - mTimeStamp.mSampleTime += (Float64) todo; - - out += todo * 2; - count -= todo; } + + mTimeStamp.mSampleTime += 512.0; } void AUPlayer::shutdown() @@ -144,23 +136,46 @@ void AUPlayer::enumComponents(callback cbEnum) CFStringGetCString(cfName, bytesBuffer, sizeof(bytesBuffer) - 1, kCFStringEncodingUTF8); bytes = bytesBuffer; } - cbEnum(bytes); + AudioComponentGetDescription(comp, &cd); + cbEnum(cd.componentSubType, cd.componentManufacturer, bytes); CFRelease(cfName); comp = AudioComponentFindNext(comp, &cd); } } -void AUPlayer::setComponent(const char *name) +void AUPlayer::setComponent(OSType uSubType, OSType uManufacturer) { - if (mComponentName) + componentSubType = uSubType; + componentManufacturer = uManufacturer; + shutdown(); +} + +/*void AUPlayer::setSoundFont( const char * in ) +{ + sSoundFontName = in; + shutdown(); +} + +void AUPlayer::setFileSoundFont( const char * in ) +{ + sFileSoundFontName = in; + shutdown(); +}*/ + +static OSStatus renderCallback( void *inRefCon, AudioUnitRenderActionFlags *ioActionFlags, const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber, UInt32 inNumberFrames, AudioBufferList *ioData ) +{ + if ( inNumberFrames && ioData ) { - free(mComponentName); - mComponentName = NULL; + for ( int i = 0, j = ioData->mNumberBuffers; i < j; ++i ) + { + int k = inNumberFrames * sizeof(float); + if (k > ioData->mBuffers[i].mDataByteSize) + k = ioData->mBuffers[i].mDataByteSize; + memset( ioData->mBuffers[i].mData, 0, k); + } } - size_t size = strlen(name) + 1; - mComponentName = (char *) malloc(size); - memcpy(mComponentName, name, size); + return noErr; } bool AUPlayer::startup() @@ -169,41 +184,13 @@ bool AUPlayer::startup() AudioComponentDescription cd = {0}; cd.componentType = kAudioUnitType_MusicDevice; + cd.componentSubType = componentSubType; + cd.componentManufacturer = componentManufacturer; 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; @@ -211,30 +198,14 @@ bool AUPlayer::startup() for (int i = 0; i < 3; i++) { + UInt32 value = 1; + UInt32 size = sizeof(value); + 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; @@ -252,6 +223,44 @@ bool AUPlayer::startup() AudioUnitSetProperty (samplerUnit[i], kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, 0, &stream, sizeof (stream)); } + + value = 512; + AudioUnitSetProperty (samplerUnit[i], kAudioUnitProperty_MaximumFramesPerSlice, + kAudioUnitScope_Global, 0, &value, size); + + value = 127; + AudioUnitSetProperty (samplerUnit[i], kAudioUnitProperty_RenderQuality, + kAudioUnitScope_Global, 0, &value, size); + + AURenderCallbackStruct callbackStruct; + callbackStruct.inputProc = renderCallback; + callbackStruct.inputProcRefCon = 0; + AudioUnitSetProperty (samplerUnit[i], kAudioUnitProperty_SetRenderCallback, + kAudioUnitScope_Input, 0, &callbackStruct, sizeof(callbackStruct)); + + /*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); + + /* + value = 1; + AudioUnitSetProperty(samplerUnit[i], kMusicDeviceProperty_StreamFromDisk, kAudioUnitScope_Global, 0, &value, size); + */ error = AudioUnitInitialize(samplerUnit[i]); @@ -259,6 +268,17 @@ bool AUPlayer::startup() return false; } + // Now load instruments + /*if (sSoundFontName.length()) + { + loadSoundFont( sSoundFontName.c_str() ); + } + + if ( sFileSoundFontName.length() ) + { + loadSoundFont( sFileSoundFontName.c_str() ); + }*/ + bufferList = (AudioBufferList *) calloc(1, sizeof(AudioBufferList) + sizeof(AudioBuffer)); if (!bufferList) return false; @@ -274,3 +294,21 @@ bool AUPlayer::startup() return true; } + +/*void AUPlayer::loadSoundFont(const char *name) +{ + // kMusicDeviceProperty_SoundBankURL was added in 10.5 as a replacement + // In addition, the File Manager API became deprecated starting in 10.8 + CFURLRef url = CFURLCreateFromFileSystemRepresentation(kCFAllocatorDefault, (const UInt8 *)name, strlen(name), false); + + if (url) { + for (int i = 0; i < 3; i++) + AudioUnitSetProperty(samplerUnit[i], + kMusicDeviceProperty_SoundBankURL, kAudioUnitScope_Global, + 0, + &url, sizeof(url) + ); + + CFRelease(url); + } +}*/ diff --git a/Plugins/MIDI/MIDI/BMPlayer.cpp b/Plugins/MIDI/MIDI/BMPlayer.cpp index a74aa5132..499ddc887 100644 --- a/Plugins/MIDI/MIDI/BMPlayer.cpp +++ b/Plugins/MIDI/MIDI/BMPlayer.cpp @@ -248,6 +248,11 @@ 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]; @@ -270,6 +275,44 @@ 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 b270450ed..7a8267099 100644 --- a/Plugins/MIDI/MIDI/BMPlayer.h +++ b/Plugins/MIDI/MIDI/BMPlayer.h @@ -20,8 +20,11 @@ public: void setSincInterpolation(bool enable = true); private: - virtual void send_event(uint32_t b); - virtual void render(float * out, unsigned long count); + 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 shutdown(); virtual bool startup(); @@ -33,6 +36,15 @@ private: std::vector _soundFonts; 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]; diff --git a/Plugins/MIDI/MIDI/MIDIDecoder.h b/Plugins/MIDI/MIDI/MIDIDecoder.h index 88822c7f2..4cc06d9fa 100755 --- a/Plugins/MIDI/MIDI/MIDIDecoder.h +++ b/Plugins/MIDI/MIDI/MIDIDecoder.h @@ -12,7 +12,10 @@ #import "Plugin.h" +class BMPlayer; + @interface MIDIDecoder : NSObject { + BMPlayer* bmplayer; MIDIPlayer* player; midi_container midi_file; diff --git a/Plugins/MIDI/MIDI/MIDIDecoder.mm b/Plugins/MIDI/MIDI/MIDIDecoder.mm index ecea95c80..c71519e48 100755 --- a/Plugins/MIDI/MIDI/MIDIDecoder.mm +++ b/Plugins/MIDI/MIDI/MIDIDecoder.mm @@ -8,6 +8,7 @@ #import "MIDIDecoder.h" +#import "AUPlayer.h" #import "BMPlayer.h" #import "Logging.h" @@ -16,6 +17,13 @@ #import "PlaylistController.h" +static OSType getOSType(const char * in_) +{ + const unsigned char * in = (const unsigned char *) in_; + OSType v = (in[0] << 24) + (in[1] << 16) + (in[2] << 8) + in[3]; + return v; +} + @implementation MIDIDecoder + (NSInteger)testExtensions:(NSString *)pathMinusExtension extensions:(NSArray *)extensionsToTest @@ -113,24 +121,47 @@ DLog(@"Track num: %i", track_num); - BMPlayer * bmplayer = new BMPlayer; - player = bmplayer; - - bool resampling_sinc = false; - NSString * resampling = [[NSUserDefaults standardUserDefaults] stringForKey:@"resampling"]; - if ([resampling isEqualToString:@"sinc"]) - resampling_sinc = true; + AUPlayer * auplayer = NULL; - bmplayer->setSincInterpolation( resampling_sinc ); - bmplayer->setSampleRate( 44100 ); + NSString * plugin = [[NSUserDefaults standardUserDefaults] stringForKey:@"midi.plugin"]; + if (!plugin || [plugin isEqualToString:@"BASSMIDI"]) + { + bmplayer = new BMPlayer; - if ( [soundFontPath length] ) - bmplayer->setFileSoundFont( [soundFontPath UTF8String] ); + bool resampling_sinc = false; + NSString * resampling = [[NSUserDefaults standardUserDefaults] stringForKey:@"resampling"]; + if ([resampling isEqualToString:@"sinc"]) + resampling_sinc = true; + + bmplayer->setSincInterpolation( resampling_sinc ); + bmplayer->setSampleRate( 44100 ); + + if ( [soundFontPath length] ) + bmplayer->setFileSoundFont( [soundFontPath UTF8String] ); + + player = bmplayer; + } + else + { + const char * cplugin = [plugin UTF8String]; + OSType componentSubType; + OSType componentManufacturer; + + componentSubType = getOSType(cplugin); + componentManufacturer = getOSType(cplugin + 4); + + auplayer = new AUPlayer; + + auplayer->setComponent(componentSubType, componentManufacturer); + auplayer->setSampleRate( 44100 ); + + player = auplayer; + } unsigned int loop_mode = framesFade ? MIDIPlayer::loop_mode_enable | MIDIPlayer::loop_mode_force : 0; unsigned int clean_flags = midi_container::clean_flag_emidi; - if ( !bmplayer->Load( midi_file, track_num, loop_mode, clean_flags) ) + if ( !player->Load( midi_file, track_num, loop_mode, clean_flags) ) return NO; framesRead = 0; @@ -161,22 +192,17 @@ long localFramesLength = framesLength; long localTotalFrames = totalFrames; - if ( repeatone && !isLooped ) - { - localFramesLength -= 44100; - localTotalFrames -= 44100; - repeatone = NO; - } + player->SetLoopMode((repeatone || isLooped) ? (MIDIPlayer::loop_mode_enable | MIDIPlayer::loop_mode_force) : 0); if ( !repeatone && framesRead >= localTotalFrames ) return 0; - if ( !soundFontsAssigned ) { + if ( bmplayer && !soundFontsAssigned ) { NSString * soundFontPath = [[NSUserDefaults standardUserDefaults] stringForKey:@"soundFontPath"]; if (soundFontPath == nil) return 0; - ((BMPlayer *)player)->setSoundFont( [soundFontPath UTF8String] ); + bmplayer->setSoundFont( [soundFontPath UTF8String] ); soundFontsAssigned = YES; } diff --git a/Plugins/MIDI/MIDI/MIDIPlayer.cpp b/Plugins/MIDI/MIDI/MIDIPlayer.cpp index 506ee7ec8..2c55c3f23 100644 --- a/Plugins/MIDI/MIDI/MIDIPlayer.cpp +++ b/Plugins/MIDI/MIDI/MIDIPlayer.cpp @@ -9,6 +9,7 @@ MIDIPlayer::MIDIPlayer() uTimeCurrent = 0; uTimeEnd = 0; uTimeLoopStart = 0; + uSamplesInBuffer = 0; } void MIDIPlayer::setSampleRate(unsigned long rate) @@ -158,26 +159,52 @@ unsigned long MIDIPlayer::Play(float * out, unsigned long count) midi_stream_event * me = &mStream[uStreamPosition]; unsigned long samples_todo = me->m_timestamp - uTimeCurrent; - if ( samples_todo ) + while ( samples_todo >= 512 ) { if ( samples_todo > count - done ) { uSamplesRemaining = samples_todo - ( count - done ); samples_todo = count - done; } - render( out + done * 2, samples_todo ); - done += samples_todo; + 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; + } if ( uSamplesRemaining ) { - uTimeCurrent = me->m_timestamp; return done; } } - send_event( me->m_event ); - - uTimeCurrent = me->m_timestamp; + send_event( me->m_event, (uint32_t) samples_todo ); } } @@ -188,7 +215,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, samples_todo ); + render( out + done * 2, (uint32_t) samples_todo ); done += samples_todo; } @@ -200,7 +227,7 @@ unsigned long MIDIPlayer::Play(float * out, unsigned long count) { for (; uStreamPosition < mStream.size(); uStreamPosition++) { - send_event( mStream[ uStreamPosition ].m_event ); + send_event( mStream[ uStreamPosition ].m_event, 0 ); } } @@ -247,6 +274,8 @@ void MIDIPlayer::Seek(unsigned long sample) } if (!startup()) return; + + uSamplesInBuffer = 0; uTimeCurrent = sample; @@ -295,8 +324,57 @@ void MIDIPlayer::Seek(unsigned long sample) for (i = 0; i < stream_start; i++) { if (me[i].m_event) - send_event(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; + } + } +} + +void MIDIPlayer::SetLoopMode(unsigned int mode) +{ + if (uLoopMode != mode) + { + if (mode & loop_mode_enable) + uTimeEnd -= uSampleRate; + else + uTimeEnd += uSampleRate; + } + uLoopMode = mode; +} diff --git a/Plugins/MIDI/MIDI/MIDIPlayer.h b/Plugins/MIDI/MIDI/MIDIPlayer.h index 3ecabfeb4..8ce57a895 100644 --- a/Plugins/MIDI/MIDI/MIDIPlayer.h +++ b/Plugins/MIDI/MIDI/MIDIPlayer.h @@ -24,10 +24,12 @@ public: 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) {} - virtual 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 shutdown() {}; virtual bool startup() {return false;} @@ -36,6 +38,8 @@ protected: system_exclusive_table mSysexMap; private: + void render(float * out, uint32_t count); + unsigned long uSamplesRemaining; unsigned uLoopMode; @@ -49,6 +53,9 @@ private: unsigned long uStreamLoopStart; unsigned long uTimeLoopStart; unsigned long uStreamEnd; + + unsigned int uSamplesInBuffer; + float fSampleBuffer[512 * 2]; }; #endif diff --git a/Preferences/General/English.lproj/Preferences.xib b/Preferences/General/English.lproj/Preferences.xib index 20cccba9b..8009ad086 100644 --- a/Preferences/General/English.lproj/Preferences.xib +++ b/Preferences/General/English.lproj/Preferences.xib @@ -1,8 +1,8 @@ - + - + @@ -483,11 +483,11 @@ - + - + @@ -507,7 +507,7 @@ - + @@ -524,7 +524,7 @@ - + @@ -532,8 +532,38 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + @@ -554,6 +584,7 @@ + @@ -562,5 +593,12 @@ preference + + + name + slug + preference + + diff --git a/Preferences/General/General.xcodeproj/project.pbxproj b/Preferences/General/General.xcodeproj/project.pbxproj index f8762ef0e..31dfb3971 100644 --- a/Preferences/General/General.xcodeproj/project.pbxproj +++ b/Preferences/General/General.xcodeproj/project.pbxproj @@ -20,6 +20,7 @@ 17E78A7E0D68BE3C005C5A59 /* file_tree.png in Resources */ = {isa = PBXBuildFile; fileRef = 17E78A7D0D68BE3C005C5A59 /* file_tree.png */; }; 17E78B6A0D68C1E3005C5A59 /* Preferences.xib in Resources */ = {isa = PBXBuildFile; fileRef = 17E78B680D68C1E3005C5A59 /* Preferences.xib */; }; 8372053718E3DEAF007EFAD4 /* ResamplerBehaviorArrayController.m in Sources */ = {isa = PBXBuildFile; fileRef = 8372053618E3DEAF007EFAD4 /* ResamplerBehaviorArrayController.m */; }; + 837C0D401C50954000CAE18F /* MIDIPluginBehaviorArrayController.m in Sources */ = {isa = PBXBuildFile; fileRef = 837C0D3F1C50954000CAE18F /* MIDIPluginBehaviorArrayController.m */; }; 8384917718084D9F00E7332D /* appearance.png in Resources */ = {isa = PBXBuildFile; fileRef = 8384917518084D9F00E7332D /* appearance.png */; }; 8384917818084D9F00E7332D /* growl.png in Resources */ = {isa = PBXBuildFile; fileRef = 8384917618084D9F00E7332D /* growl.png */; }; 8384918C1808596A00E7332D /* NDHotKey.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = 838491841808588D00E7332D /* NDHotKey.framework */; }; @@ -97,6 +98,8 @@ 32DBCF630370AF2F00C91783 /* General_Prefix.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = General_Prefix.pch; sourceTree = ""; }; 8372053518E3DEAF007EFAD4 /* ResamplerBehaviorArrayController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ResamplerBehaviorArrayController.h; sourceTree = ""; }; 8372053618E3DEAF007EFAD4 /* ResamplerBehaviorArrayController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ResamplerBehaviorArrayController.m; sourceTree = ""; }; + 837C0D3E1C50954000CAE18F /* MIDIPluginBehaviorArrayController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MIDIPluginBehaviorArrayController.h; sourceTree = ""; }; + 837C0D3F1C50954000CAE18F /* MIDIPluginBehaviorArrayController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MIDIPluginBehaviorArrayController.m; sourceTree = ""; }; 8384913618081ECB00E7332D /* Logging.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Logging.h; path = ../../Utils/Logging.h; sourceTree = ""; }; 8384917518084D9F00E7332D /* appearance.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = appearance.png; path = Icons/appearance.png; sourceTree = ""; }; 8384917618084D9F00E7332D /* growl.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = growl.png; path = Icons/growl.png; sourceTree = ""; }; @@ -230,6 +233,8 @@ 17D503410ABDB1660022D1E8 /* Custom */ = { isa = PBXGroup; children = ( + 837C0D3E1C50954000CAE18F /* MIDIPluginBehaviorArrayController.h */, + 837C0D3F1C50954000CAE18F /* MIDIPluginBehaviorArrayController.m */, 8372053518E3DEAF007EFAD4 /* ResamplerBehaviorArrayController.h */, 8372053618E3DEAF007EFAD4 /* ResamplerBehaviorArrayController.m */, 170744AB0BFF3938002475C9 /* AppcastArrayController.h */, @@ -414,6 +419,7 @@ 8E6C13A00AACBAB500819171 /* HotKeyControl.m in Sources */, 83EF495F17FBC96A00642E3C /* VolumeBehaviorArrayController.m in Sources */, 17C643380B8A77CC00C53518 /* OutputsArrayController.m in Sources */, + 837C0D401C50954000CAE18F /* MIDIPluginBehaviorArrayController.m in Sources */, 17C6433F0B8A783F00C53518 /* OutputPane.m in Sources */, 170744AD0BFF3938002475C9 /* AppcastArrayController.m in Sources */, 99F1813F0DE01D7A00FD5FFB /* PlaylistBehaviorArrayController.m in Sources */, diff --git a/Preferences/General/MIDIPluginBehaviorArrayController.h b/Preferences/General/MIDIPluginBehaviorArrayController.h new file mode 100644 index 000000000..7ba2a67c6 --- /dev/null +++ b/Preferences/General/MIDIPluginBehaviorArrayController.h @@ -0,0 +1,13 @@ +// +// ResamplerBehaviorArrayController.h +// General +// +// Created by Christopher Snowhill on 03/26/14. +// +// + +#import + +@interface MIDIPluginBehaviorArrayController : NSArrayController + +@end diff --git a/Preferences/General/MIDIPluginBehaviorArrayController.m b/Preferences/General/MIDIPluginBehaviorArrayController.m new file mode 100644 index 000000000..a1dcb0b82 --- /dev/null +++ b/Preferences/General/MIDIPluginBehaviorArrayController.m @@ -0,0 +1,82 @@ +// +// ResamplerBehaviorArrayController.m +// General +// +// Created by Christopher Snowhill on 03/26/14. +// +// + +#import +#import +#import + +#import "MIDIPluginBehaviorArrayController.h" + +typedef void (*callback)(void *context, OSType uSubType, OSType uManufacturer, const char * name); +static void enumComponents(callback cbEnum, void *context) +{ + AudioComponentDescription cd = {0}; + cd.componentType = kAudioUnitType_MusicDevice; + + AudioComponent comp = NULL; + + const char * bytes; + char bytesBuffer[512]; + + comp = AudioComponentFindNext(comp, &cd); + + while (comp != NULL) + { + AudioComponentDescription tcd; + CFStringRef cfName; + AudioComponentCopyName(comp, &cfName); + bytes = CFStringGetCStringPtr(cfName, kCFStringEncodingUTF8); + if (!bytes) + { + CFStringGetCString(cfName, bytesBuffer, sizeof(bytesBuffer) - 1, kCFStringEncodingUTF8); + bytes = bytesBuffer; + } + AudioComponentGetDescription(comp, &tcd); + cbEnum(context, tcd.componentSubType, tcd.componentManufacturer, bytes); + CFRelease(cfName); + comp = AudioComponentFindNext(comp, &cd); + } +} + +static void copyOSType(char * out, OSType in) +{ + out[0] = (in >> 24) & 0xFF; + out[1] = (in >> 16) & 0xFF; + out[2] = (in >> 8) & 0xFF; + out[3] = in & 0xFF; +} + +static void enumCallback(void *context, OSType uSubType, OSType uManufacturer, const char * name) +{ + id pself = (id) context; + + char pref[9]; + + copyOSType(pref, uSubType); + copyOSType(pref + 4, uManufacturer); + pref[8] = '\0'; + + [pself addObject: + [NSDictionary dictionaryWithObjectsAndKeys: + [NSString stringWithUTF8String:name], @"name", + [NSString stringWithUTF8String:pref], @"preference", nil]]; +} + +@implementation MIDIPluginBehaviorArrayController +- (void)awakeFromNib +{ + [self removeObjects:[self arrangedObjects]]; + + [self addObject: + [NSDictionary dictionaryWithObjectsAndKeys: + @"BASSMIDI", @"name", @"BASSMIDI", @"preference", nil]]; + + enumComponents(enumCallback, self); +} + +@end