- MIDI player now loops non-looping files internally if Repeat One is enabled
- MIDI player now supports installed Audio Unit plug-ins, and defaults to the DLS MIDI synthesizer
This commit is contained in:
parent
fed76e9a49
commit
039788226d
13 changed files with 499 additions and 142 deletions
|
@ -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 numberWithInteger:-1] forKey:@"lastTrackPlaying"];
|
||||||
[userDefaultsValuesDict setObject:[NSNumber numberWithDouble:0] forKey:@"lastTrackPosition"];
|
[userDefaultsValuesDict setObject:[NSNumber numberWithDouble:0] forKey:@"lastTrackPosition"];
|
||||||
|
|
||||||
|
[userDefaultsValuesDict setObject:@"dls appl" forKey:@"midi.plugin"];
|
||||||
|
|
||||||
//Register and sync defaults
|
//Register and sync defaults
|
||||||
[[NSUserDefaults standardUserDefaults] registerDefaults:userDefaultsValuesDict];
|
[[NSUserDefaults standardUserDefaults] registerDefaults:userDefaultsValuesDict];
|
||||||
[[NSUserDefaults standardUserDefaults] synchronize];
|
[[NSUserDefaults standardUserDefaults] synchronize];
|
||||||
|
|
|
@ -3,6 +3,8 @@
|
||||||
|
|
||||||
#include "MIDIPlayer.h"
|
#include "MIDIPlayer.h"
|
||||||
|
|
||||||
|
//#include <string>
|
||||||
|
|
||||||
#import <AudioToolbox/AudioToolbox.h>
|
#import <AudioToolbox/AudioToolbox.h>
|
||||||
#import <AVFoundation/AVFoundation.h>
|
#import <AVFoundation/AVFoundation.h>
|
||||||
#import <CoreAudio/CoreAudioTypes.h>
|
#import <CoreAudio/CoreAudioTypes.h>
|
||||||
|
@ -17,21 +19,28 @@ public:
|
||||||
virtual ~AUPlayer();
|
virtual ~AUPlayer();
|
||||||
|
|
||||||
// configuration
|
// configuration
|
||||||
void showDialog();
|
/*void setSoundFont( const char * in );
|
||||||
|
void setFileSoundFont( const char * in );*/
|
||||||
|
//void showDialog();
|
||||||
|
|
||||||
typedef void (*callback)(const char * name);
|
typedef void (*callback)(OSType uSubType, OSType uManufacturer, const char * name);
|
||||||
void enumComponents(callback cbEnum);
|
static void enumComponents(callback cbEnum);
|
||||||
|
|
||||||
void setComponent(const char * name);
|
void setComponent(OSType uSubType, OSType uManufacturer);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual void send_event(uint32_t b);
|
virtual void send_event(uint32_t b, uint32_t sample_offset);
|
||||||
virtual void render(float * out, unsigned long count);
|
virtual void render_512(float * out);
|
||||||
|
|
||||||
virtual void shutdown();
|
virtual void shutdown();
|
||||||
virtual bool startup();
|
virtual bool startup();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
/*void loadSoundFont(const char * name);
|
||||||
|
|
||||||
|
std::string sSoundFontName;
|
||||||
|
std::string sFileSoundFontName;*/
|
||||||
|
|
||||||
AudioTimeStamp mTimeStamp;
|
AudioTimeStamp mTimeStamp;
|
||||||
|
|
||||||
AudioUnit samplerUnit[3];
|
AudioUnit samplerUnit[3];
|
||||||
|
@ -40,7 +49,7 @@ private:
|
||||||
|
|
||||||
float *audioBuffer;
|
float *audioBuffer;
|
||||||
|
|
||||||
char *mComponentName;
|
OSType componentSubType, componentManufacturer;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -14,7 +14,8 @@ AUPlayer::AUPlayer() : MIDIPlayer()
|
||||||
bufferList = NULL;
|
bufferList = NULL;
|
||||||
audioBuffer = NULL;
|
audioBuffer = NULL;
|
||||||
|
|
||||||
mComponentName = NULL;
|
componentSubType = kAudioUnitSubType_DLSSynth;
|
||||||
|
componentManufacturer = kAudioUnitManufacturer_Apple;
|
||||||
}
|
}
|
||||||
|
|
||||||
AUPlayer::~AUPlayer()
|
AUPlayer::~AUPlayer()
|
||||||
|
@ -22,7 +23,7 @@ AUPlayer::~AUPlayer()
|
||||||
shutdown();
|
shutdown();
|
||||||
}
|
}
|
||||||
|
|
||||||
void AUPlayer::send_event(uint32_t b)
|
void AUPlayer::send_event(uint32_t b, uint32_t sample_offset)
|
||||||
{
|
{
|
||||||
if (!(b & 0x80000000))
|
if (!(b & 0x80000000))
|
||||||
{
|
{
|
||||||
|
@ -32,7 +33,7 @@ void AUPlayer::send_event(uint32_t b)
|
||||||
event[ 2 ] = (unsigned char)( b >> 16 );
|
event[ 2 ] = (unsigned char)( b >> 16 );
|
||||||
unsigned port = (b >> 24) & 0x7F;
|
unsigned port = (b >> 24) & 0x7F;
|
||||||
if ( port > 2 ) port = 2;
|
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
|
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;
|
float *ptrL, *ptrR;
|
||||||
while (count)
|
memset(out, 0, 512 * sizeof(float) * 2);
|
||||||
{
|
|
||||||
unsigned long todo = count;
|
|
||||||
if (todo > 512)
|
|
||||||
todo = 512;
|
|
||||||
memset(out, 0, todo * sizeof(float) * 2);
|
|
||||||
for (unsigned long i = 0; i < 3; ++i)
|
for (unsigned long i = 0; i < 3; ++i)
|
||||||
{
|
{
|
||||||
AudioUnitRenderActionFlags ioActionFlags = 0;
|
AudioUnitRenderActionFlags ioActionFlags = 0;
|
||||||
UInt32 numberFrames = (UInt32) todo;
|
UInt32 numberFrames = 512;
|
||||||
|
|
||||||
for (unsigned long j = 0; j < 2; j++)
|
for (unsigned long j = 0; j < 2; j++)
|
||||||
{
|
{
|
||||||
bufferList->mBuffers[j].mNumberChannels = 1;
|
bufferList->mBuffers[j].mNumberChannels = 1;
|
||||||
bufferList->mBuffers[j].mDataByteSize = (UInt32) (todo * sizeof(float));
|
bufferList->mBuffers[j].mDataByteSize = (UInt32) (512 * sizeof(float));
|
||||||
bufferList->mBuffers[j].mData = audioBuffer + j * 512;
|
bufferList->mBuffers[j].mData = audioBuffer + j * 512;
|
||||||
memset(bufferList->mBuffers[j].mData, 0, todo * sizeof(float));
|
memset(bufferList->mBuffers[j].mData, 0, 512 * sizeof(float));
|
||||||
}
|
}
|
||||||
|
|
||||||
AudioUnitRender(samplerUnit[i], &ioActionFlags, &mTimeStamp, 0, numberFrames, bufferList);
|
AudioUnitRender(samplerUnit[i], &ioActionFlags, &mTimeStamp, 0, numberFrames, bufferList);
|
||||||
|
|
||||||
ptrL = (float *) bufferList->mBuffers[0].mData;
|
ptrL = (float *) bufferList->mBuffers[0].mData;
|
||||||
ptrR = (float *) bufferList->mBuffers[1].mData;
|
ptrR = (float *) bufferList->mBuffers[1].mData;
|
||||||
for (unsigned long j = 0; j < todo; ++j)
|
for (unsigned long j = 0; j < 512; ++j)
|
||||||
{
|
{
|
||||||
out[j * 2 + 0] += ptrL[j];
|
out[j * 2 + 0] += ptrL[j];
|
||||||
out[j * 2 + 1] += ptrR[j];
|
out[j * 2 + 1] += ptrR[j];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mTimeStamp.mSampleTime += (Float64) todo;
|
mTimeStamp.mSampleTime += 512.0;
|
||||||
|
|
||||||
out += todo * 2;
|
|
||||||
count -= todo;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void AUPlayer::shutdown()
|
void AUPlayer::shutdown()
|
||||||
|
@ -144,23 +136,46 @@ void AUPlayer::enumComponents(callback cbEnum)
|
||||||
CFStringGetCString(cfName, bytesBuffer, sizeof(bytesBuffer) - 1, kCFStringEncodingUTF8);
|
CFStringGetCString(cfName, bytesBuffer, sizeof(bytesBuffer) - 1, kCFStringEncodingUTF8);
|
||||||
bytes = bytesBuffer;
|
bytes = bytesBuffer;
|
||||||
}
|
}
|
||||||
cbEnum(bytes);
|
AudioComponentGetDescription(comp, &cd);
|
||||||
|
cbEnum(cd.componentSubType, cd.componentManufacturer, bytes);
|
||||||
CFRelease(cfName);
|
CFRelease(cfName);
|
||||||
comp = AudioComponentFindNext(comp, &cd);
|
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);
|
for ( int i = 0, j = ioData->mNumberBuffers; i < j; ++i )
|
||||||
mComponentName = NULL;
|
{
|
||||||
|
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;
|
return noErr;
|
||||||
mComponentName = (char *) malloc(size);
|
|
||||||
memcpy(mComponentName, name, size);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool AUPlayer::startup()
|
bool AUPlayer::startup()
|
||||||
|
@ -169,41 +184,13 @@ bool AUPlayer::startup()
|
||||||
|
|
||||||
AudioComponentDescription cd = {0};
|
AudioComponentDescription cd = {0};
|
||||||
cd.componentType = kAudioUnitType_MusicDevice;
|
cd.componentType = kAudioUnitType_MusicDevice;
|
||||||
|
cd.componentSubType = componentSubType;
|
||||||
|
cd.componentManufacturer = componentManufacturer;
|
||||||
|
|
||||||
AudioComponent comp = NULL;
|
AudioComponent comp = NULL;
|
||||||
|
|
||||||
const char * pComponentName = mComponentName;
|
|
||||||
|
|
||||||
const char * bytes;
|
|
||||||
char bytesBuffer[512];
|
|
||||||
|
|
||||||
comp = AudioComponentFindNext(comp, &cd);
|
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)
|
if (!comp)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
@ -211,30 +198,14 @@ bool AUPlayer::startup()
|
||||||
|
|
||||||
for (int i = 0; i < 3; i++)
|
for (int i = 0; i < 3; i++)
|
||||||
{
|
{
|
||||||
|
UInt32 value = 1;
|
||||||
|
UInt32 size = sizeof(value);
|
||||||
|
|
||||||
error = AudioComponentInstanceNew(comp, &samplerUnit[i]);
|
error = AudioComponentInstanceNew(comp, &samplerUnit[i]);
|
||||||
|
|
||||||
if (error != noErr)
|
if (error != noErr)
|
||||||
return false;
|
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 };
|
AudioStreamBasicDescription stream = { 0 };
|
||||||
stream.mSampleRate = uSampleRate;
|
stream.mSampleRate = uSampleRate;
|
||||||
|
@ -253,12 +224,61 @@ bool AUPlayer::startup()
|
||||||
kAudioUnitScope_Output, 0, &stream, sizeof (stream));
|
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]);
|
error = AudioUnitInitialize(samplerUnit[i]);
|
||||||
|
|
||||||
if (error != noErr)
|
if (error != noErr)
|
||||||
return false;
|
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));
|
bufferList = (AudioBufferList *) calloc(1, sizeof(AudioBufferList) + sizeof(AudioBuffer));
|
||||||
if (!bufferList)
|
if (!bufferList)
|
||||||
return false;
|
return false;
|
||||||
|
@ -274,3 +294,21 @@ bool AUPlayer::startup()
|
||||||
|
|
||||||
return true;
|
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);
|
||||||
|
}
|
||||||
|
}*/
|
||||||
|
|
|
@ -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)
|
void BMPlayer::render(float * out, unsigned long count)
|
||||||
{
|
{
|
||||||
float buffer[1024];
|
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 )
|
void BMPlayer::setSoundFont( const char * in )
|
||||||
{
|
{
|
||||||
sSoundFontName = in;
|
sSoundFontName = in;
|
||||||
|
|
|
@ -20,8 +20,11 @@ public:
|
||||||
void setSincInterpolation(bool enable = true);
|
void setSincInterpolation(bool enable = true);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
virtual void send_event(uint32_t b);
|
void send_event(uint32_t b);
|
||||||
virtual void render(float * out, unsigned long count);
|
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 void shutdown();
|
||||||
virtual bool startup();
|
virtual bool startup();
|
||||||
|
@ -34,6 +37,15 @@ private:
|
||||||
std::string sSoundFontName;
|
std::string sSoundFontName;
|
||||||
std::string sFileSoundFontName;
|
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<BASS_event> _eventQueue;
|
||||||
|
|
||||||
HSTREAM _stream[3];
|
HSTREAM _stream[3];
|
||||||
|
|
||||||
bool bSincInterpolation;
|
bool bSincInterpolation;
|
||||||
|
|
|
@ -12,7 +12,10 @@
|
||||||
|
|
||||||
#import "Plugin.h"
|
#import "Plugin.h"
|
||||||
|
|
||||||
|
class BMPlayer;
|
||||||
|
|
||||||
@interface MIDIDecoder : NSObject <CogDecoder> {
|
@interface MIDIDecoder : NSObject <CogDecoder> {
|
||||||
|
BMPlayer* bmplayer;
|
||||||
MIDIPlayer* player;
|
MIDIPlayer* player;
|
||||||
midi_container midi_file;
|
midi_container midi_file;
|
||||||
|
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
|
|
||||||
#import "MIDIDecoder.h"
|
#import "MIDIDecoder.h"
|
||||||
|
|
||||||
|
#import "AUPlayer.h"
|
||||||
#import "BMPlayer.h"
|
#import "BMPlayer.h"
|
||||||
|
|
||||||
#import "Logging.h"
|
#import "Logging.h"
|
||||||
|
@ -16,6 +17,13 @@
|
||||||
|
|
||||||
#import "PlaylistController.h"
|
#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
|
@implementation MIDIDecoder
|
||||||
|
|
||||||
+ (NSInteger)testExtensions:(NSString *)pathMinusExtension extensions:(NSArray *)extensionsToTest
|
+ (NSInteger)testExtensions:(NSString *)pathMinusExtension extensions:(NSArray *)extensionsToTest
|
||||||
|
@ -113,8 +121,12 @@
|
||||||
|
|
||||||
DLog(@"Track num: %i", track_num);
|
DLog(@"Track num: %i", track_num);
|
||||||
|
|
||||||
BMPlayer * bmplayer = new BMPlayer;
|
AUPlayer * auplayer = NULL;
|
||||||
player = bmplayer;
|
|
||||||
|
NSString * plugin = [[NSUserDefaults standardUserDefaults] stringForKey:@"midi.plugin"];
|
||||||
|
if (!plugin || [plugin isEqualToString:@"BASSMIDI"])
|
||||||
|
{
|
||||||
|
bmplayer = new BMPlayer;
|
||||||
|
|
||||||
bool resampling_sinc = false;
|
bool resampling_sinc = false;
|
||||||
NSString * resampling = [[NSUserDefaults standardUserDefaults] stringForKey:@"resampling"];
|
NSString * resampling = [[NSUserDefaults standardUserDefaults] stringForKey:@"resampling"];
|
||||||
|
@ -127,10 +139,29 @@
|
||||||
if ( [soundFontPath length] )
|
if ( [soundFontPath length] )
|
||||||
bmplayer->setFileSoundFont( [soundFontPath UTF8String] );
|
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 loop_mode = framesFade ? MIDIPlayer::loop_mode_enable | MIDIPlayer::loop_mode_force : 0;
|
||||||
unsigned int clean_flags = midi_container::clean_flag_emidi;
|
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;
|
return NO;
|
||||||
|
|
||||||
framesRead = 0;
|
framesRead = 0;
|
||||||
|
@ -161,22 +192,17 @@
|
||||||
long localFramesLength = framesLength;
|
long localFramesLength = framesLength;
|
||||||
long localTotalFrames = totalFrames;
|
long localTotalFrames = totalFrames;
|
||||||
|
|
||||||
if ( repeatone && !isLooped )
|
player->SetLoopMode((repeatone || isLooped) ? (MIDIPlayer::loop_mode_enable | MIDIPlayer::loop_mode_force) : 0);
|
||||||
{
|
|
||||||
localFramesLength -= 44100;
|
|
||||||
localTotalFrames -= 44100;
|
|
||||||
repeatone = NO;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( !repeatone && framesRead >= localTotalFrames )
|
if ( !repeatone && framesRead >= localTotalFrames )
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
if ( !soundFontsAssigned ) {
|
if ( bmplayer && !soundFontsAssigned ) {
|
||||||
NSString * soundFontPath = [[NSUserDefaults standardUserDefaults] stringForKey:@"soundFontPath"];
|
NSString * soundFontPath = [[NSUserDefaults standardUserDefaults] stringForKey:@"soundFontPath"];
|
||||||
if (soundFontPath == nil)
|
if (soundFontPath == nil)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
((BMPlayer *)player)->setSoundFont( [soundFontPath UTF8String] );
|
bmplayer->setSoundFont( [soundFontPath UTF8String] );
|
||||||
|
|
||||||
soundFontsAssigned = YES;
|
soundFontsAssigned = YES;
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,6 +9,7 @@ MIDIPlayer::MIDIPlayer()
|
||||||
uTimeCurrent = 0;
|
uTimeCurrent = 0;
|
||||||
uTimeEnd = 0;
|
uTimeEnd = 0;
|
||||||
uTimeLoopStart = 0;
|
uTimeLoopStart = 0;
|
||||||
|
uSamplesInBuffer = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void MIDIPlayer::setSampleRate(unsigned long rate)
|
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];
|
midi_stream_event * me = &mStream[uStreamPosition];
|
||||||
|
|
||||||
unsigned long samples_todo = me->m_timestamp - uTimeCurrent;
|
unsigned long samples_todo = me->m_timestamp - uTimeCurrent;
|
||||||
if ( samples_todo )
|
while ( samples_todo >= 512 )
|
||||||
{
|
{
|
||||||
if ( samples_todo > count - done )
|
if ( samples_todo > count - done )
|
||||||
{
|
{
|
||||||
uSamplesRemaining = samples_todo - ( count - done );
|
uSamplesRemaining = samples_todo - ( count - done );
|
||||||
samples_todo = count - done;
|
samples_todo = count - done;
|
||||||
}
|
}
|
||||||
render( out + done * 2, 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;
|
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 )
|
if ( uSamplesRemaining )
|
||||||
{
|
{
|
||||||
uTimeCurrent = me->m_timestamp;
|
|
||||||
return done;
|
return done;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
send_event( me->m_event );
|
send_event( me->m_event, (uint32_t) samples_todo );
|
||||||
|
|
||||||
uTimeCurrent = me->m_timestamp;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -188,7 +215,7 @@ unsigned long MIDIPlayer::Play(float * out, unsigned long count)
|
||||||
else samples_todo = uTimeEnd;
|
else samples_todo = uTimeEnd;
|
||||||
samples_todo -= uTimeCurrent;
|
samples_todo -= uTimeCurrent;
|
||||||
if ( samples_todo > count - done ) samples_todo = count - done;
|
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;
|
done += samples_todo;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -200,7 +227,7 @@ unsigned long MIDIPlayer::Play(float * out, unsigned long count)
|
||||||
{
|
{
|
||||||
for (; uStreamPosition < mStream.size(); uStreamPosition++)
|
for (; uStreamPosition < mStream.size(); uStreamPosition++)
|
||||||
{
|
{
|
||||||
send_event( mStream[ uStreamPosition ].m_event );
|
send_event( mStream[ uStreamPosition ].m_event, 0 );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -248,6 +275,8 @@ void MIDIPlayer::Seek(unsigned long sample)
|
||||||
|
|
||||||
if (!startup()) return;
|
if (!startup()) return;
|
||||||
|
|
||||||
|
uSamplesInBuffer = 0;
|
||||||
|
|
||||||
uTimeCurrent = sample;
|
uTimeCurrent = sample;
|
||||||
|
|
||||||
std::vector<midi_stream_event> filler;
|
std::vector<midi_stream_event> filler;
|
||||||
|
@ -295,8 +324,57 @@ void MIDIPlayer::Seek(unsigned long sample)
|
||||||
for (i = 0; i < stream_start; i++)
|
for (i = 0; i < stream_start; i++)
|
||||||
{
|
{
|
||||||
if (me[i].m_event)
|
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;
|
||||||
|
}
|
||||||
|
|
|
@ -25,9 +25,11 @@ public:
|
||||||
unsigned long Play(float * out, unsigned long count);
|
unsigned long Play(float * out, unsigned long count);
|
||||||
void Seek(unsigned long sample);
|
void Seek(unsigned long sample);
|
||||||
|
|
||||||
|
void SetLoopMode(unsigned mode);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual void send_event(uint32_t b) {}
|
virtual void send_event(uint32_t b, uint32_t sample_offset) {}
|
||||||
virtual void render(float * out, unsigned long count) {}
|
virtual void render_512(float * out) {}
|
||||||
|
|
||||||
virtual void shutdown() {};
|
virtual void shutdown() {};
|
||||||
virtual bool startup() {return false;}
|
virtual bool startup() {return false;}
|
||||||
|
@ -36,6 +38,8 @@ protected:
|
||||||
system_exclusive_table mSysexMap;
|
system_exclusive_table mSysexMap;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
void render(float * out, uint32_t count);
|
||||||
|
|
||||||
unsigned long uSamplesRemaining;
|
unsigned long uSamplesRemaining;
|
||||||
|
|
||||||
unsigned uLoopMode;
|
unsigned uLoopMode;
|
||||||
|
@ -49,6 +53,9 @@ private:
|
||||||
unsigned long uStreamLoopStart;
|
unsigned long uStreamLoopStart;
|
||||||
unsigned long uTimeLoopStart;
|
unsigned long uTimeLoopStart;
|
||||||
unsigned long uStreamEnd;
|
unsigned long uStreamEnd;
|
||||||
|
|
||||||
|
unsigned int uSamplesInBuffer;
|
||||||
|
float fSampleBuffer[512 * 2];
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="6254" systemVersion="14B25" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none">
|
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="9531" systemVersion="15C50" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none">
|
||||||
<dependencies>
|
<dependencies>
|
||||||
<deployment version="1050" identifier="macosx"/>
|
<deployment version="1050" identifier="macosx"/>
|
||||||
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="6254"/>
|
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="9531"/>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
<objects>
|
<objects>
|
||||||
<customObject id="-2" userLabel="File's Owner" customClass="GeneralPreferencesPlugin">
|
<customObject id="-2" userLabel="File's Owner" customClass="GeneralPreferencesPlugin">
|
||||||
|
@ -483,11 +483,11 @@
|
||||||
</subviews>
|
</subviews>
|
||||||
</customView>
|
</customView>
|
||||||
<customView id="JXu-ar-J3Y" userLabel="MIDIView">
|
<customView id="JXu-ar-J3Y" userLabel="MIDIView">
|
||||||
<rect key="frame" x="0.0" y="0.0" width="500" height="107"/>
|
<rect key="frame" x="0.0" y="0.0" width="500" height="111"/>
|
||||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||||
<subviews>
|
<subviews>
|
||||||
<button verticalHuggingPriority="750" id="SBO-WF-DVS" userLabel="Push Button - Select a SoundFont">
|
<button verticalHuggingPriority="750" id="SBO-WF-DVS" userLabel="Push Button - Select a SoundFont">
|
||||||
<rect key="frame" x="14" y="63" width="160" height="32"/>
|
<rect key="frame" x="14" y="67" width="160" height="32"/>
|
||||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||||
<buttonCell key="cell" type="push" title="Select a SoundFont" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="yeL-9c-lFq" userLabel="Button Cell - Select a SoundFont">
|
<buttonCell key="cell" type="push" title="Select a SoundFont" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="yeL-9c-lFq" userLabel="Button Cell - Select a SoundFont">
|
||||||
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
|
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
|
||||||
|
@ -498,7 +498,7 @@
|
||||||
</connections>
|
</connections>
|
||||||
</button>
|
</button>
|
||||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" id="FRC-Dh-BS2" userLabel="Text Field - Selected:">
|
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" id="FRC-Dh-BS2" userLabel="Text Field - Selected:">
|
||||||
<rect key="frame" x="173" y="72" width="61" height="17"/>
|
<rect key="frame" x="173" y="76" width="61" height="17"/>
|
||||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Selected:" id="KJz-qS-IcO" userLabel="Text Field Cell - Selected">
|
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Selected:" id="KJz-qS-IcO" userLabel="Text Field Cell - Selected">
|
||||||
<font key="font" metaFont="system"/>
|
<font key="font" metaFont="system"/>
|
||||||
|
@ -507,7 +507,7 @@
|
||||||
</textFieldCell>
|
</textFieldCell>
|
||||||
</textField>
|
</textField>
|
||||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" id="cZr-2d-6cv">
|
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" id="cZr-2d-6cv">
|
||||||
<rect key="frame" x="236" y="72" width="220" height="17"/>
|
<rect key="frame" x="236" y="76" width="220" height="17"/>
|
||||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Label" id="ECB-P0-pve">
|
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Label" id="ECB-P0-pve">
|
||||||
<font key="font" metaFont="system"/>
|
<font key="font" metaFont="system"/>
|
||||||
|
@ -524,7 +524,7 @@
|
||||||
</textFieldCell>
|
</textFieldCell>
|
||||||
</textField>
|
</textField>
|
||||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" id="G86-18-hiK">
|
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" id="G86-18-hiK">
|
||||||
<rect key="frame" x="32" y="40" width="138" height="17"/>
|
<rect key="frame" x="32" y="22" width="138" height="17"/>
|
||||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" alignment="right" title="Resampling quality:" id="eX0-PC-iVo">
|
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" alignment="right" title="Resampling quality:" id="eX0-PC-iVo">
|
||||||
<font key="font" metaFont="system"/>
|
<font key="font" metaFont="system"/>
|
||||||
|
@ -532,8 +532,38 @@
|
||||||
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
|
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
|
||||||
</textFieldCell>
|
</textFieldCell>
|
||||||
</textField>
|
</textField>
|
||||||
|
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" id="4uG-vW-B64">
|
||||||
|
<rect key="frame" x="79" y="49" width="91" height="17"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||||
|
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" alignment="right" title="MIDI Plugin:" id="n5F-dq-NZh">
|
||||||
|
<font key="font" metaFont="system"/>
|
||||||
|
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
</textFieldCell>
|
||||||
|
</textField>
|
||||||
|
<popUpButton verticalHuggingPriority="750" id="8Nj-G4-5ag">
|
||||||
|
<rect key="frame" x="173" y="44" width="164" height="26"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||||
|
<popUpButtonCell key="cell" type="push" title="Item 1" bezelStyle="rounded" alignment="left" lineBreakMode="truncatingTail" state="on" borderStyle="borderAndBezel" imageScaling="proportionallyDown" inset="2" selectedItem="vaQ-pZ-jXy" id="xcv-1b-kTI">
|
||||||
|
<behavior key="behavior" lightByBackground="YES" lightByGray="YES"/>
|
||||||
|
<font key="font" metaFont="menu"/>
|
||||||
|
<menu key="menu" id="pRc-b6-sMR">
|
||||||
|
<items>
|
||||||
|
<menuItem title="Item 1" state="on" id="vaQ-pZ-jXy"/>
|
||||||
|
<menuItem title="Item 2" id="YLi-QX-EgD"/>
|
||||||
|
<menuItem title="Item 3" id="1Cb-TU-E0q"/>
|
||||||
|
</items>
|
||||||
|
</menu>
|
||||||
|
</popUpButtonCell>
|
||||||
|
<connections>
|
||||||
|
<binding destination="czk-eG-6QG" name="content" keyPath="arrangedObjects" id="Mzg-yr-6mv"/>
|
||||||
|
<binding destination="czk-eG-6QG" name="contentObjects" keyPath="arrangedObjects.preference" previousBinding="Mzg-yr-6mv" id="NFp-di-hMf"/>
|
||||||
|
<binding destination="czk-eG-6QG" name="contentValues" keyPath="arrangedObjects.name" previousBinding="NFp-di-hMf" id="6JE-ba-47Z"/>
|
||||||
|
<binding destination="52" name="selectedObject" keyPath="values.midi.plugin" previousBinding="6JE-ba-47Z" id="n1u-ie-870"/>
|
||||||
|
</connections>
|
||||||
|
</popUpButton>
|
||||||
<popUpButton verticalHuggingPriority="750" id="E1D-Bo-ZVf">
|
<popUpButton verticalHuggingPriority="750" id="E1D-Bo-ZVf">
|
||||||
<rect key="frame" x="173" y="35" width="164" height="26"/>
|
<rect key="frame" x="173" y="17" width="164" height="26"/>
|
||||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||||
<popUpButtonCell key="cell" type="push" title="Item 1" bezelStyle="rounded" alignment="left" lineBreakMode="truncatingTail" state="on" borderStyle="borderAndBezel" imageScaling="proportionallyDown" inset="2" selectedItem="3Gx-cs-3B0" id="5q7-83-7V6">
|
<popUpButtonCell key="cell" type="push" title="Item 1" bezelStyle="rounded" alignment="left" lineBreakMode="truncatingTail" state="on" borderStyle="borderAndBezel" imageScaling="proportionallyDown" inset="2" selectedItem="3Gx-cs-3B0" id="5q7-83-7V6">
|
||||||
<behavior key="behavior" lightByBackground="YES" lightByGray="YES"/>
|
<behavior key="behavior" lightByBackground="YES" lightByGray="YES"/>
|
||||||
|
@ -554,6 +584,7 @@
|
||||||
</connections>
|
</connections>
|
||||||
</popUpButton>
|
</popUpButton>
|
||||||
</subviews>
|
</subviews>
|
||||||
|
<point key="canvasLocation" x="340" y="554.5"/>
|
||||||
</customView>
|
</customView>
|
||||||
<arrayController objectClassName="NSDictionary" editable="NO" id="JB6-r9-XpG" userLabel="ResamplerBehavior" customClass="ResamplerBehaviorArrayController">
|
<arrayController objectClassName="NSDictionary" editable="NO" id="JB6-r9-XpG" userLabel="ResamplerBehavior" customClass="ResamplerBehaviorArrayController">
|
||||||
<declaredKeys>
|
<declaredKeys>
|
||||||
|
@ -562,5 +593,12 @@
|
||||||
<string>preference</string>
|
<string>preference</string>
|
||||||
</declaredKeys>
|
</declaredKeys>
|
||||||
</arrayController>
|
</arrayController>
|
||||||
|
<arrayController objectClassName="NSDictionary" editable="NO" id="czk-eG-6QG" userLabel="MIDIPluginBehavior" customClass="MIDIPluginBehaviorArrayController">
|
||||||
|
<declaredKeys>
|
||||||
|
<string>name</string>
|
||||||
|
<string>slug</string>
|
||||||
|
<string>preference</string>
|
||||||
|
</declaredKeys>
|
||||||
|
</arrayController>
|
||||||
</objects>
|
</objects>
|
||||||
</document>
|
</document>
|
||||||
|
|
|
@ -20,6 +20,7 @@
|
||||||
17E78A7E0D68BE3C005C5A59 /* file_tree.png in Resources */ = {isa = PBXBuildFile; fileRef = 17E78A7D0D68BE3C005C5A59 /* file_tree.png */; };
|
17E78A7E0D68BE3C005C5A59 /* file_tree.png in Resources */ = {isa = PBXBuildFile; fileRef = 17E78A7D0D68BE3C005C5A59 /* file_tree.png */; };
|
||||||
17E78B6A0D68C1E3005C5A59 /* Preferences.xib in Resources */ = {isa = PBXBuildFile; fileRef = 17E78B680D68C1E3005C5A59 /* Preferences.xib */; };
|
17E78B6A0D68C1E3005C5A59 /* Preferences.xib in Resources */ = {isa = PBXBuildFile; fileRef = 17E78B680D68C1E3005C5A59 /* Preferences.xib */; };
|
||||||
8372053718E3DEAF007EFAD4 /* ResamplerBehaviorArrayController.m in Sources */ = {isa = PBXBuildFile; fileRef = 8372053618E3DEAF007EFAD4 /* ResamplerBehaviorArrayController.m */; };
|
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 */; };
|
8384917718084D9F00E7332D /* appearance.png in Resources */ = {isa = PBXBuildFile; fileRef = 8384917518084D9F00E7332D /* appearance.png */; };
|
||||||
8384917818084D9F00E7332D /* growl.png in Resources */ = {isa = PBXBuildFile; fileRef = 8384917618084D9F00E7332D /* growl.png */; };
|
8384917818084D9F00E7332D /* growl.png in Resources */ = {isa = PBXBuildFile; fileRef = 8384917618084D9F00E7332D /* growl.png */; };
|
||||||
8384918C1808596A00E7332D /* NDHotKey.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = 838491841808588D00E7332D /* NDHotKey.framework */; };
|
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 = "<group>"; };
|
32DBCF630370AF2F00C91783 /* General_Prefix.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = General_Prefix.pch; sourceTree = "<group>"; };
|
||||||
8372053518E3DEAF007EFAD4 /* ResamplerBehaviorArrayController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ResamplerBehaviorArrayController.h; sourceTree = "<group>"; };
|
8372053518E3DEAF007EFAD4 /* ResamplerBehaviorArrayController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ResamplerBehaviorArrayController.h; sourceTree = "<group>"; };
|
||||||
8372053618E3DEAF007EFAD4 /* ResamplerBehaviorArrayController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ResamplerBehaviorArrayController.m; sourceTree = "<group>"; };
|
8372053618E3DEAF007EFAD4 /* ResamplerBehaviorArrayController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ResamplerBehaviorArrayController.m; sourceTree = "<group>"; };
|
||||||
|
837C0D3E1C50954000CAE18F /* MIDIPluginBehaviorArrayController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MIDIPluginBehaviorArrayController.h; sourceTree = "<group>"; };
|
||||||
|
837C0D3F1C50954000CAE18F /* MIDIPluginBehaviorArrayController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MIDIPluginBehaviorArrayController.m; sourceTree = "<group>"; };
|
||||||
8384913618081ECB00E7332D /* Logging.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Logging.h; path = ../../Utils/Logging.h; sourceTree = "<group>"; };
|
8384913618081ECB00E7332D /* Logging.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Logging.h; path = ../../Utils/Logging.h; sourceTree = "<group>"; };
|
||||||
8384917518084D9F00E7332D /* appearance.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = appearance.png; path = Icons/appearance.png; sourceTree = "<group>"; };
|
8384917518084D9F00E7332D /* appearance.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = appearance.png; path = Icons/appearance.png; sourceTree = "<group>"; };
|
||||||
8384917618084D9F00E7332D /* growl.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = growl.png; path = Icons/growl.png; sourceTree = "<group>"; };
|
8384917618084D9F00E7332D /* growl.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = growl.png; path = Icons/growl.png; sourceTree = "<group>"; };
|
||||||
|
@ -230,6 +233,8 @@
|
||||||
17D503410ABDB1660022D1E8 /* Custom */ = {
|
17D503410ABDB1660022D1E8 /* Custom */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
|
837C0D3E1C50954000CAE18F /* MIDIPluginBehaviorArrayController.h */,
|
||||||
|
837C0D3F1C50954000CAE18F /* MIDIPluginBehaviorArrayController.m */,
|
||||||
8372053518E3DEAF007EFAD4 /* ResamplerBehaviorArrayController.h */,
|
8372053518E3DEAF007EFAD4 /* ResamplerBehaviorArrayController.h */,
|
||||||
8372053618E3DEAF007EFAD4 /* ResamplerBehaviorArrayController.m */,
|
8372053618E3DEAF007EFAD4 /* ResamplerBehaviorArrayController.m */,
|
||||||
170744AB0BFF3938002475C9 /* AppcastArrayController.h */,
|
170744AB0BFF3938002475C9 /* AppcastArrayController.h */,
|
||||||
|
@ -414,6 +419,7 @@
|
||||||
8E6C13A00AACBAB500819171 /* HotKeyControl.m in Sources */,
|
8E6C13A00AACBAB500819171 /* HotKeyControl.m in Sources */,
|
||||||
83EF495F17FBC96A00642E3C /* VolumeBehaviorArrayController.m in Sources */,
|
83EF495F17FBC96A00642E3C /* VolumeBehaviorArrayController.m in Sources */,
|
||||||
17C643380B8A77CC00C53518 /* OutputsArrayController.m in Sources */,
|
17C643380B8A77CC00C53518 /* OutputsArrayController.m in Sources */,
|
||||||
|
837C0D401C50954000CAE18F /* MIDIPluginBehaviorArrayController.m in Sources */,
|
||||||
17C6433F0B8A783F00C53518 /* OutputPane.m in Sources */,
|
17C6433F0B8A783F00C53518 /* OutputPane.m in Sources */,
|
||||||
170744AD0BFF3938002475C9 /* AppcastArrayController.m in Sources */,
|
170744AD0BFF3938002475C9 /* AppcastArrayController.m in Sources */,
|
||||||
99F1813F0DE01D7A00FD5FFB /* PlaylistBehaviorArrayController.m in Sources */,
|
99F1813F0DE01D7A00FD5FFB /* PlaylistBehaviorArrayController.m in Sources */,
|
||||||
|
|
13
Preferences/General/MIDIPluginBehaviorArrayController.h
Normal file
13
Preferences/General/MIDIPluginBehaviorArrayController.h
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
//
|
||||||
|
// ResamplerBehaviorArrayController.h
|
||||||
|
// General
|
||||||
|
//
|
||||||
|
// Created by Christopher Snowhill on 03/26/14.
|
||||||
|
//
|
||||||
|
//
|
||||||
|
|
||||||
|
#import <Cocoa/Cocoa.h>
|
||||||
|
|
||||||
|
@interface MIDIPluginBehaviorArrayController : NSArrayController
|
||||||
|
|
||||||
|
@end
|
82
Preferences/General/MIDIPluginBehaviorArrayController.m
Normal file
82
Preferences/General/MIDIPluginBehaviorArrayController.m
Normal file
|
@ -0,0 +1,82 @@
|
||||||
|
//
|
||||||
|
// ResamplerBehaviorArrayController.m
|
||||||
|
// General
|
||||||
|
//
|
||||||
|
// Created by Christopher Snowhill on 03/26/14.
|
||||||
|
//
|
||||||
|
//
|
||||||
|
|
||||||
|
#import <AudioToolbox/AudioToolbox.h>
|
||||||
|
#import <AVFoundation/AVFoundation.h>
|
||||||
|
#import <CoreAudio/CoreAudioTypes.h>
|
||||||
|
|
||||||
|
#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
|
Loading…
Reference in a new issue