void MIDIInstrumentParameterPanel::slotSelectProgram(int index) { RG_DEBUG << "slotSelectProgram()"; if (!getSelectedInstrument()) return; MidiDevice *md = dynamic_cast<MidiDevice *>(getSelectedInstrument()->getDevice()); if (!md) return; const MidiProgram *prg = &m_programs[index]; // If there has been no change, bail. if (getSelectedInstrument()->getProgramChange() == prg->getProgram()) return; getSelectedInstrument()->setProgramChange(prg->getProgram()); // In Variations mode, select the 0th variation. // In Variations mode, it's very easy to select an "invalid" // program change. I.e. one for which the bank is not valid. Go // from one program/variation to a program that doesn't have that // variation. We need to handle that here by selecting the 0th // variation. That's what the user expects. if (md->getVariationType() == MidiDevice::VariationFromMSB) { MidiBank bank = getSelectedInstrument()->getProgram().getBank(); // Get the list of MSB variations. BankList bankList = md->getBanksByLSB( getSelectedInstrument()->isPercussion(), bank.getLSB()); if (!bankList.empty()) { // Pick the first MSB variation getSelectedInstrument()->setMSB(bankList.front().getMSB()); } } if (md->getVariationType() == MidiDevice::VariationFromLSB) { MidiBank bank = getSelectedInstrument()->getProgram().getBank(); // Get the list of LSB variations. BankList bankList = md->getBanksByMSB( getSelectedInstrument()->isPercussion(), bank.getMSB()); if (!bankList.empty()) { // Pick the first LSB variation getSelectedInstrument()->setLSB(bankList.front().getLSB()); } } getSelectedInstrument()->sendChannelSetup(); // Just one change notification for the two potential changes. // See comments in slotSelectBank() for further discussion. getSelectedInstrument()->changed(); }
ProgramList MidiDevice::getPrograms0thVariation(bool percussion, const MidiBank &bank) const { // If we aren't in variations mode, just use getPrograms(). if (m_variationType == NoVariations) return getPrograms(bank); // Get the variation bank list for this bank BankList bankList; if (m_variationType == VariationFromMSB) { bankList = getBanksByLSB(percussion, bank.getLSB()); } else { bankList = getBanksByMSB(percussion, bank.getMSB()); } if (!bankList.empty()) { MidiBank firstBank = bankList.front(); return getPrograms(firstBank); } return ProgramList(); }
void MIDIInstrumentParameterPanel::slotSelectBank(int index) { RG_DEBUG << "slotSelectBank() begin..."; if (!getSelectedInstrument()) return; MidiDevice *md = dynamic_cast<MidiDevice *>(getSelectedInstrument()->getDevice()); if (!md) { std::cerr << "WARNING: MIDIInstrumentParameterPanel::slotSelectBank(): No MidiDevice for Instrument " << getSelectedInstrument()->getId() << '\n'; return; } const MidiBank &bank = m_banks[index]; bool change = false; if (md->getVariationType() != MidiDevice::VariationFromLSB) { if (getSelectedInstrument()->getLSB() != bank.getLSB()) { getSelectedInstrument()->setLSB(bank.getLSB()); change = true; } } if (md->getVariationType() != MidiDevice::VariationFromMSB) { if (getSelectedInstrument()->getMSB() != bank.getMSB()) { getSelectedInstrument()->setMSB(bank.getMSB()); change = true; } } // If no change, bail. if (!change) return; // Make sure the Instrument is valid WRT the Device. // If the current bank/program is not valid for this device, fix it. if (!getSelectedInstrument()->isProgramValid()) { // If we're not in variations mode... if (md->getVariationType() == MidiDevice::NoVariations) { // ...go with the first program ProgramList programList = md->getPrograms(bank); if (!programList.empty()) { // Switch to the first program in this bank. getSelectedInstrument()->setProgram(programList.front()); } else { // No programs for this bank. Just go with 0. getSelectedInstrument()->setProgramChange(0); } } else { // We're in variations mode... // This is the three-comboboxes (bank/program/variation) case. // It's an extremely difficult case to handle, so we're just // going to punt and give them the first program/variation in // the bank they just selected. // Get the variation bank list for this bank BankList bankList; if (md->getVariationType() == MidiDevice::VariationFromMSB) { bankList = md->getBanksByLSB( getSelectedInstrument()->isPercussion(), bank.getLSB()); } else { bankList = md->getBanksByMSB( getSelectedInstrument()->isPercussion(), bank.getMSB()); } if (!bankList.empty()) { // Pick the first bank MidiBank firstBank = bankList.front(); // Get the program list ProgramList programList = md->getPrograms(firstBank); if (!programList.empty()) { // Pick the first program getSelectedInstrument()->setProgram(programList.front()); } } // To make the above more complex, we could consider the // case where the Program Change happens to be valid for // some variation bank in the newly selected bank. Then // go with the 0th variation bank that has that program // change. But I think this is complicated enough. } } getSelectedInstrument()->sendChannelSetup(); // This is why changed() isn't called within // the setters. If it were, then each of the above changes would // result in a change notification going out. Worst case, that // would be three change notifications and the first two would be // sent when the Instrument was in an inconsistent state. // Rule: Avoid sending change notifications from setters. // Why? It reduces the number of notifications which improves // performance. It avoids sending notifications when an object's // state is inconsistent. It avoids endless loops. getSelectedInstrument()->changed(); }