|
@@ -539,7 +539,8 @@ protected: |
|
|
fIsActive = false; |
|
|
fIsActive = false; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
void process(float**, float** const outBuffer, const uint32_t frames, const NativeMidiEvent* const midiEvents, const uint32_t midiEventCount) override |
|
|
|
|
|
|
|
|
void process(float**, float** const outBuffer, const uint32_t frames, |
|
|
|
|
|
const NativeMidiEvent* const midiEvents, const uint32_t midiEventCount) override |
|
|
{ |
|
|
{ |
|
|
if (! fMutex.tryLock()) |
|
|
if (! fMutex.tryLock()) |
|
|
{ |
|
|
{ |
|
@@ -553,55 +554,88 @@ protected: |
|
|
fMutex.lock(); |
|
|
fMutex.lock(); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
for (uint32_t i=0; i < midiEventCount; ++i) |
|
|
|
|
|
{ |
|
|
|
|
|
const NativeMidiEvent* const midiEvent(&midiEvents[i]); |
|
|
|
|
|
|
|
|
// the following code is based on ZynAddSubFX DSSI plugin code by Stephen G. Parry |
|
|
|
|
|
uint32_t fromFrame = 0; |
|
|
|
|
|
uint32_t toFrame = 0; |
|
|
|
|
|
uint32_t midiEventIndex = 0; |
|
|
|
|
|
uint32_t nextEventFrame = 0; |
|
|
|
|
|
|
|
|
const uint8_t status = MIDI_GET_STATUS_FROM_DATA(midiEvent->data); |
|
|
|
|
|
const char channel = MIDI_GET_CHANNEL_FROM_DATA(midiEvent->data); |
|
|
|
|
|
|
|
|
do { |
|
|
|
|
|
/* Find the time of the next event, if any */ |
|
|
|
|
|
if (midiEvents == nullptr || midiEventIndex >= midiEventCount) |
|
|
|
|
|
nextEventFrame = (uint32_t)-1; |
|
|
|
|
|
else |
|
|
|
|
|
nextEventFrame = midiEvents[midiEventIndex].time; |
|
|
|
|
|
|
|
|
if (MIDI_IS_STATUS_NOTE_OFF(status)) |
|
|
|
|
|
{ |
|
|
|
|
|
const char note = static_cast<char>(midiEvent->data[1]); |
|
|
|
|
|
|
|
|
/* find the end of the sub-sample to be processed this time round... */ |
|
|
|
|
|
/* if the next event falls within the desired sample interval... */ |
|
|
|
|
|
if (nextEventFrame < frames && nextEventFrame >= toFrame) |
|
|
|
|
|
/* set the end to be at that event */ |
|
|
|
|
|
toFrame = nextEventFrame; |
|
|
|
|
|
else |
|
|
|
|
|
/* ...else go for the whole remaining sample */ |
|
|
|
|
|
toFrame = frames; |
|
|
|
|
|
|
|
|
fMaster->noteOff(channel, note); |
|
|
|
|
|
} |
|
|
|
|
|
else if (MIDI_IS_STATUS_NOTE_ON(status)) |
|
|
|
|
|
|
|
|
if (fromFrame < toFrame) |
|
|
{ |
|
|
{ |
|
|
const char note = static_cast<char>(midiEvent->data[1]); |
|
|
|
|
|
const char velo = static_cast<char>(midiEvent->data[2]); |
|
|
|
|
|
|
|
|
// call master to fill from `fromFrame` to `toFrame`: |
|
|
|
|
|
fMaster->GetAudioOutSamples(toFrame - fromFrame, fSynth.samplerate, outBuffer[0], outBuffer[1]); |
|
|
|
|
|
|
|
|
fMaster->noteOn(channel, note, velo); |
|
|
|
|
|
|
|
|
// next sub-sample please... |
|
|
|
|
|
fromFrame = toFrame; |
|
|
} |
|
|
} |
|
|
else if (MIDI_IS_STATUS_POLYPHONIC_AFTERTOUCH(status)) |
|
|
|
|
|
{ |
|
|
|
|
|
const char note = static_cast<char>(midiEvent->data[1]); |
|
|
|
|
|
const char pressure = static_cast<char>(midiEvent->data[2]); |
|
|
|
|
|
|
|
|
|
|
|
fMaster->polyphonicAftertouch(channel, note, pressure); |
|
|
|
|
|
} |
|
|
|
|
|
else if (MIDI_IS_STATUS_CONTROL_CHANGE(status)) |
|
|
|
|
|
|
|
|
// Now process any event(s) at the current timing point |
|
|
|
|
|
for (; midiEventIndex < midiEventCount && midiEvents[midiEventIndex].time == toFrame;) |
|
|
{ |
|
|
{ |
|
|
// skip controls which we map to parameters |
|
|
|
|
|
if (getIndexFromZynControl(midiEvent->data[1]) != kParamCount) |
|
|
|
|
|
continue; |
|
|
|
|
|
|
|
|
|
|
|
const int control = midiEvent->data[1]; |
|
|
|
|
|
const int value = midiEvent->data[2]; |
|
|
|
|
|
|
|
|
|
|
|
fMaster->setController(channel, control, value); |
|
|
|
|
|
|
|
|
const NativeMidiEvent* const midiEvent(&midiEvents[midiEventIndex++]); |
|
|
|
|
|
|
|
|
|
|
|
const uint8_t status = MIDI_GET_STATUS_FROM_DATA(midiEvent->data); |
|
|
|
|
|
const char channel = MIDI_GET_CHANNEL_FROM_DATA(midiEvent->data); |
|
|
|
|
|
|
|
|
|
|
|
if (MIDI_IS_STATUS_NOTE_OFF(status)) |
|
|
|
|
|
{ |
|
|
|
|
|
const char note = static_cast<char>(midiEvent->data[1]); |
|
|
|
|
|
|
|
|
|
|
|
fMaster->noteOff(channel, note); |
|
|
|
|
|
} |
|
|
|
|
|
else if (MIDI_IS_STATUS_NOTE_ON(status)) |
|
|
|
|
|
{ |
|
|
|
|
|
const char note = static_cast<char>(midiEvent->data[1]); |
|
|
|
|
|
const char velo = static_cast<char>(midiEvent->data[2]); |
|
|
|
|
|
|
|
|
|
|
|
fMaster->noteOn(channel, note, velo); |
|
|
|
|
|
} |
|
|
|
|
|
else if (MIDI_IS_STATUS_POLYPHONIC_AFTERTOUCH(status)) |
|
|
|
|
|
{ |
|
|
|
|
|
const char note = static_cast<char>(midiEvent->data[1]); |
|
|
|
|
|
const char pressure = static_cast<char>(midiEvent->data[2]); |
|
|
|
|
|
|
|
|
|
|
|
fMaster->polyphonicAftertouch(channel, note, pressure); |
|
|
|
|
|
} |
|
|
|
|
|
else if (MIDI_IS_STATUS_CONTROL_CHANGE(status)) |
|
|
|
|
|
{ |
|
|
|
|
|
// skip controls which we map to parameters |
|
|
|
|
|
if (getIndexFromZynControl(midiEvent->data[1]) != kParamCount) |
|
|
|
|
|
continue; |
|
|
|
|
|
|
|
|
|
|
|
const int control = midiEvent->data[1]; |
|
|
|
|
|
const int value = midiEvent->data[2]; |
|
|
|
|
|
|
|
|
|
|
|
fMaster->setController(channel, control, value); |
|
|
|
|
|
} |
|
|
|
|
|
else if (MIDI_IS_STATUS_PITCH_WHEEL_CONTROL(status)) |
|
|
|
|
|
{ |
|
|
|
|
|
const uint8_t lsb = midiEvent->data[1]; |
|
|
|
|
|
const uint8_t msb = midiEvent->data[2]; |
|
|
|
|
|
const int value = ((msb << 7) | lsb) - 8192; |
|
|
|
|
|
|
|
|
|
|
|
fMaster->setController(channel, C_pitchwheel, value); |
|
|
|
|
|
} |
|
|
} |
|
|
} |
|
|
else if (MIDI_IS_STATUS_PITCH_WHEEL_CONTROL(status)) |
|
|
|
|
|
{ |
|
|
|
|
|
const uint8_t lsb = midiEvent->data[1]; |
|
|
|
|
|
const uint8_t msb = midiEvent->data[2]; |
|
|
|
|
|
const int value = ((msb << 7) | lsb) - 8192; |
|
|
|
|
|
|
|
|
|
|
|
fMaster->setController(channel, C_pitchwheel, value); |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
fMaster->GetAudioOutSamples(frames, fSynth.samplerate, outBuffer[0], outBuffer[1]); |
|
|
|
|
|
|
|
|
// Keep going until we have the desired total length of sample... |
|
|
|
|
|
} while (toFrame < frames); |
|
|
|
|
|
|
|
|
fMutex.unlock(); |
|
|
fMutex.unlock(); |
|
|
} |
|
|
} |
|
@@ -764,12 +798,11 @@ private: |
|
|
|
|
|
|
|
|
void run() noexcept override |
|
|
void run() noexcept override |
|
|
{ |
|
|
{ |
|
|
for (;;) |
|
|
|
|
|
|
|
|
for (; ! shouldThreadExit();) |
|
|
{ |
|
|
{ |
|
|
if (MiddleWare* const mw = fMiddleWare) |
|
|
|
|
|
mw->tick(); |
|
|
|
|
|
else |
|
|
|
|
|
break; |
|
|
|
|
|
|
|
|
try { |
|
|
|
|
|
fMiddleWare->tick(); |
|
|
|
|
|
} CARLA_SAFE_EXCEPTION("ZynAddSubFX MiddleWare tick"); |
|
|
|
|
|
|
|
|
carla_msleep(1); |
|
|
carla_msleep(1); |
|
|
} |
|
|
} |
|
@@ -825,13 +858,11 @@ private: |
|
|
|
|
|
|
|
|
void _deleteMaster() |
|
|
void _deleteMaster() |
|
|
{ |
|
|
{ |
|
|
fMaster = nullptr; |
|
|
|
|
|
|
|
|
stopThread(1000); |
|
|
|
|
|
|
|
|
MiddleWare* const tmp(fMiddleWare); |
|
|
|
|
|
|
|
|
fMaster = nullptr; |
|
|
|
|
|
delete fMiddleWare; |
|
|
fMiddleWare = nullptr; |
|
|
fMiddleWare = nullptr; |
|
|
|
|
|
|
|
|
stopThread(1000); |
|
|
|
|
|
delete tmp; |
|
|
|
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
void _masterChangedCallback(Master* m) |
|
|
void _masterChangedCallback(Master* m) |
|
|