@@ -104,11 +104,14 @@ public: | |||
: PluginDescriptorClass(host), | |||
kMaster(new Master()), | |||
kSampleRate(getSampleRate()), | |||
fIsActive(false), | |||
fThread(kMaster, host) | |||
{ | |||
fThread.start(); | |||
maybeInitPrograms(kMaster); | |||
//fThread.waitForStarted(); | |||
for (int i = 0; i < NUM_MIDI_PARTS; ++i) | |||
kMaster->partonoff(i, 1); | |||
} | |||
~ZynAddSubFxPlugin() override | |||
@@ -229,13 +232,13 @@ protected: | |||
bool isOffline = false; | |||
if (isOffline) | |||
if (isOffline || ! fIsActive) | |||
loadProgram(kMaster, channel, bank, program); | |||
else | |||
fThread.loadLater(channel, bank, program); | |||
} | |||
void setCustomData(const char* const key, const char* const value) | |||
void setCustomData(const char* const key, const char* const value) override | |||
{ | |||
CARLA_ASSERT(key != nullptr); | |||
CARLA_ASSERT(value != nullptr); | |||
@@ -254,6 +257,13 @@ protected: | |||
// broken | |||
//for (int i=0; i < NUM_MIDI_PARTS; i++) | |||
// kMaster->setController(0, MIDI_CONTROL_ALL_SOUND_OFF, 0); | |||
fIsActive = true; | |||
} | |||
void deactivate() override | |||
{ | |||
fIsActive = false; | |||
} | |||
void process(float**, float** const outBuffer, const uint32_t frames, const uint32_t midiEventCount, const MidiEvent* const midiEvents) override | |||
@@ -371,6 +381,7 @@ private: | |||
#endif | |||
fQuit(false), | |||
fChangeProgram(false), | |||
fNextChannel(0), | |||
fNextBank(0), | |||
fNextProgram(0) | |||
{ | |||
@@ -387,7 +398,7 @@ private: | |||
void loadLater(const uint8_t channel, const uint32_t bank, const uint32_t program) | |||
{ | |||
// TODO | |||
fNextChannel = channel; | |||
fNextBank = bank; | |||
fNextProgram = program; | |||
fChangeProgram = true; | |||
@@ -396,6 +407,7 @@ private: | |||
void stopLoadLater() | |||
{ | |||
fChangeProgram = false; | |||
fNextChannel = 0; | |||
fNextBank = 0; | |||
fNextProgram = 0; | |||
} | |||
@@ -489,12 +501,17 @@ private: | |||
if (fChangeProgram) | |||
{ | |||
fChangeProgram = false; | |||
loadProgram(kMaster, 0, fNextBank, fNextProgram); // TODO | |||
loadProgram(kMaster, fNextChannel, fNextBank, fNextProgram); | |||
fNextChannel = 0; | |||
fNextBank = 0; | |||
fNextProgram = 0; | |||
} | |||
carla_msleep(15); | |||
carla_msleep(15); | |||
} | |||
else | |||
{ | |||
carla_msleep(30); | |||
} | |||
} | |||
#ifdef WANT_ZYNADDSUBFX_UI | |||
@@ -533,12 +550,14 @@ private: | |||
bool fQuit; | |||
bool fChangeProgram; | |||
uint8_t fNextChannel; | |||
uint32_t fNextBank; | |||
uint32_t fNextProgram; | |||
}; | |||
Master* const kMaster; | |||
const unsigned kSampleRate; | |||
bool fIsActive; | |||
ZynThread fThread; | |||
@@ -601,26 +620,28 @@ public: | |||
return; | |||
doSearch = false; | |||
sPrograms.append(new ProgramInfo(0, 0, "default")); | |||
pthread_mutex_lock(&master->mutex); | |||
// refresh banks | |||
master->bank.rescanforbanks(); | |||
for (uint32_t i=0, size = master->bank.banks.size(); i < size; i++) | |||
for (uint32_t i=0, size = master->bank.banks.size(); i < size; ++i) | |||
{ | |||
if (master->bank.banks[i].dir.empty()) | |||
continue; | |||
master->bank.loadbank(master->bank.banks[i].dir); | |||
for (unsigned int instrument = 0; instrument < BANK_SIZE; instrument++) | |||
for (unsigned int instrument = 0; instrument < BANK_SIZE; ++instrument) | |||
{ | |||
const std::string insName(master->bank.getname(instrument)); | |||
if (insName.empty() || insName[0] == '\0' || insName[0] == ' ') | |||
continue; | |||
sPrograms.append(new ProgramInfo(i, instrument, insName.c_str())); | |||
sPrograms.append(new ProgramInfo(i+1, instrument, insName.c_str())); | |||
} | |||
} | |||
@@ -629,16 +650,31 @@ public: | |||
static void loadProgram(Master* const master, const uint8_t channel, const uint32_t bank, const uint32_t program) | |||
{ | |||
const std::string& bankdir(master->bank.banks[bank].dir); | |||
if (bank == 0) | |||
{ | |||
pthread_mutex_lock(&master->mutex); | |||
master->part[channel]->defaults(); | |||
master->part[channel]->applyparameters(false); | |||
master->partonoff(channel, 1); | |||
pthread_mutex_unlock(&master->mutex); | |||
return; | |||
} | |||
const std::string& bankdir(master->bank.banks[bank-1].dir); | |||
if (! bankdir.empty()) | |||
{ | |||
pthread_mutex_lock(&master->mutex); | |||
master->partonoff(channel, 1); | |||
master->bank.loadbank(bankdir); | |||
master->bank.loadfromslot(program, master->part[channel]); | |||
master->applyparameters(false); | |||
master->part[channel]->applyparameters(false); | |||
pthread_mutex_unlock(&master->mutex); | |||
} | |||
@@ -804,7 +804,9 @@ void CarlaPlugin::loadSaveState(const SaveState& saveState) | |||
// --------------------------------------------------------------------- | |||
// Part 3 - set midi program | |||
if (saveState.currentMidiBank >= 0 && saveState.currentMidiProgram >= 0 && type() != PLUGIN_SF2) | |||
const bool usesMultiProgs(type() == PLUGIN_SF2 || (type() == PLUGIN_INTERNAL && (fHints & PLUGIN_IS_SYNTH) != 0)); | |||
if (saveState.currentMidiBank >= 0 && saveState.currentMidiProgram >= 0 && ! usesMultiProgs) | |||
setMidiProgramById(saveState.currentMidiBank, saveState.currentMidiProgram, true, true, true); | |||
// --------------------------------------------------------------------- | |||
@@ -451,7 +451,10 @@ public: | |||
fCurMidiProgs[i] = index; | |||
if (kData->ctrlChannel == static_cast<int32_t>(i)) | |||
{ | |||
kData->midiprog.current = index; | |||
kData->engine->callback(CALLBACK_MIDI_PROGRAM_CHANGED, fId, index, 0, 0.0f, nullptr); | |||
} | |||
} | |||
++i; | |||
@@ -977,7 +980,7 @@ public: | |||
fCurMidiProgs[9] = 0; | |||
} | |||
setMidiProgram(0, false, false, false); | |||
kData->midiprog.current = 0; | |||
} | |||
else | |||
{ | |||
@@ -172,7 +172,8 @@ public: | |||
fIsUiVisible(false), | |||
fAudioInBuffers(nullptr), | |||
fAudioOutBuffers(nullptr), | |||
fMidiEventCount(0) | |||
fMidiEventCount(0), | |||
fCurMidiProgs{0} | |||
{ | |||
carla_debug("NativePlugin::NativePlugin(%p, %i)", engine, id); | |||
@@ -483,9 +484,19 @@ public: | |||
CARLA_ASSERT(fDescriptor != nullptr); | |||
CARLA_ASSERT(fHandle != nullptr); | |||
if (fDescriptor->get_state == nullptr) | |||
return; | |||
if ((fDescriptor->hints & ::PLUGIN_USES_STATE) == 0) | |||
if (kData->midiprog.count > 0 && (fHints & PLUGIN_IS_SYNTH) != 0) | |||
{ | |||
char strBuf[STR_MAX+1]; | |||
std::snprintf(strBuf, STR_MAX, "%i:%i:%i:%i:%i:%i:%i:%i:%i:%i:%i:%i:%i:%i:%i:%i", | |||
fCurMidiProgs[0], fCurMidiProgs[1], fCurMidiProgs[2], fCurMidiProgs[3], | |||
fCurMidiProgs[4], fCurMidiProgs[5], fCurMidiProgs[6], fCurMidiProgs[7], | |||
fCurMidiProgs[8], fCurMidiProgs[9], fCurMidiProgs[10], fCurMidiProgs[11], | |||
fCurMidiProgs[12], fCurMidiProgs[13], fCurMidiProgs[14], fCurMidiProgs[15]); | |||
CarlaPlugin::setCustomData(CUSTOM_DATA_STRING, "midiPrograms", strBuf, false); | |||
} | |||
if (fDescriptor->get_state == nullptr || (fDescriptor->hints & ::PLUGIN_USES_STATE) == 0) | |||
return; | |||
if (char* data = fDescriptor->get_state(fHandle)) | |||
@@ -514,6 +525,14 @@ public: | |||
CarlaPlugin::setName(newName); | |||
} | |||
void setCtrlChannel(const int8_t channel, const bool sendOsc, const bool sendCallback) override | |||
{ | |||
if (channel < MAX_MIDI_CHANNELS) | |||
kData->midiprog.current = fCurMidiProgs[channel]; | |||
CarlaPlugin::setCtrlChannel(channel, sendOsc, sendCallback); | |||
} | |||
// ------------------------------------------------------------------- | |||
// Set data (plugin-specific stuff) | |||
@@ -569,6 +588,41 @@ public: | |||
fDescriptor->set_state(fHandle2, value); | |||
} | |||
} | |||
else if (std::strcmp(key, "midiPrograms") == 0 && fDescriptor->set_midi_program != nullptr) | |||
{ | |||
QStringList midiProgramList(QString(value).split(":", QString::SkipEmptyParts)); | |||
if (midiProgramList.count() == MAX_MIDI_CHANNELS) | |||
{ | |||
uint i = 0; | |||
foreach (const QString& midiProg, midiProgramList) | |||
{ | |||
bool ok; | |||
uint index = midiProg.toUInt(&ok); | |||
if (ok && index < kData->midiprog.count) | |||
{ | |||
const uint32_t bank = kData->midiprog.data[index].bank; | |||
const uint32_t program = kData->midiprog.data[index].program; | |||
fDescriptor->set_midi_program(fHandle, i, bank, program); | |||
if (fHandle2 != nullptr) | |||
fDescriptor->set_midi_program(fHandle2, i, bank, program); | |||
fCurMidiProgs[i] = index; | |||
if (kData->ctrlChannel == static_cast<int32_t>(i)) | |||
{ | |||
kData->midiprog.current = index; | |||
kData->engine->callback(CALLBACK_MIDI_PROGRAM_CHANGED, fId, index, 0, 0.0f, nullptr); | |||
} | |||
} | |||
++i; | |||
} | |||
} | |||
} | |||
else | |||
{ | |||
if (fDescriptor->set_custom_data != nullptr) | |||
@@ -603,17 +657,23 @@ public: | |||
else if (index > static_cast<int32_t>(kData->midiprog.count)) | |||
return; | |||
if ((fHints & PLUGIN_IS_SYNTH) != 0 && (kData->ctrlChannel < 0 || kData->ctrlChannel >= MAX_MIDI_CHANNELS)) | |||
return; | |||
if (index >= 0) | |||
{ | |||
const uint8_t channel = (kData->ctrlChannel >= 0 || kData->ctrlChannel < MAX_MIDI_CHANNELS) ? kData->ctrlChannel : 0; | |||
const uint32_t bank = kData->midiprog.data[index].bank; | |||
const uint32_t program = kData->midiprog.data[index].program; | |||
const ScopedSingleProcessLocker spl(this, (sendGui || sendOsc || sendCallback)); | |||
fDescriptor->set_midi_program(fHandle, 0, bank, program); // TODO | |||
fDescriptor->set_midi_program(fHandle, channel, bank, program); | |||
if (fHandle2 != nullptr) | |||
fDescriptor->set_midi_program(fHandle2, 0, bank, program); // TODO | |||
fDescriptor->set_midi_program(fHandle2, channel, bank, program); | |||
fCurMidiProgs[channel] = index; | |||
} | |||
CarlaPlugin::setMidiProgram(index, sendGui, sendOsc, sendCallback); | |||
@@ -1072,7 +1132,7 @@ public: | |||
// Query new programs | |||
uint32_t count = 0; | |||
if (fDescriptor->get_midi_program_count != nullptr && fDescriptor->get_midi_program_info != nullptr) | |||
if (fDescriptor->get_midi_program_count != nullptr && fDescriptor->get_midi_program_info != nullptr && fDescriptor->set_midi_program != nullptr) | |||
count = fDescriptor->get_midi_program_count(fHandle); | |||
if (count > 0) | |||
@@ -1452,16 +1512,24 @@ public: | |||
break; | |||
case kEngineControlEventTypeMidiProgram: | |||
if (event.channel == kData->ctrlChannel && (fOptions & PLUGIN_OPTION_MAP_PROGRAM_CHANGES) != 0) | |||
if (event.channel < MAX_MIDI_CHANNELS && (fOptions & PLUGIN_OPTION_MAP_PROGRAM_CHANGES) != 0) | |||
{ | |||
const uint32_t nextProgramId = ctrlEvent.param; | |||
const uint32_t nextProgramId(ctrlEvent.param); | |||
for (k=0; k < kData->midiprog.count; ++k) | |||
{ | |||
if (kData->midiprog.data[k].bank == nextBankId && kData->midiprog.data[k].program == nextProgramId) | |||
{ | |||
setMidiProgram(k, false, false, false); | |||
postponeRtEvent(kPluginPostRtEventMidiProgramChange, k, 0, 0.0f); | |||
fDescriptor->set_midi_program(fHandle, event.channel, nextBankId, nextProgramId); | |||
if (fHandle2 != nullptr) | |||
fDescriptor->set_midi_program(fHandle2, event.channel, nextBankId, nextProgramId); | |||
fCurMidiProgs[event.channel] = k; | |||
if (event.channel == kData->ctrlChannel) | |||
postponeRtEvent(kPluginPostRtEventMidiProgramChange, k, 0, 0.0f); | |||
break; | |||
} | |||
} | |||
@@ -2188,6 +2256,8 @@ private: | |||
uint32_t fMidiEventCount; | |||
::MidiEvent fMidiEvents[MAX_MIDI_EVENTS*2]; | |||
int32_t fCurMidiProgs[MAX_MIDI_CHANNELS]; | |||
NativePluginMidiData fMidiIn; | |||
NativePluginMidiData fMidiOut; | |||
@@ -1900,7 +1900,9 @@ class PluginEdit(QDialog): | |||
self.fTabIconTimers.append(ICON_STATE_NULL) | |||
def _updateCtrlMidiProgram(self): | |||
if self.fPluginInfo['type'] != PLUGIN_SF2: | |||
if self.fPluginInfo['type'] not in (PLUGIN_INTERNAL, PLUGIN_SF2): | |||
return | |||
elif not self.fPluginInfo['hints'] & PLUGIN_IS_SYNTH: | |||
return | |||
if self.fControlChannel == -1: | |||