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();
}
예제 #2
0
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();
}
void
MIDIInstrumentParameterPanel::updateBankComboBox()
{
    RG_DEBUG << "updateBankComboBox()";

    if (!getSelectedInstrument())
        return;

    MidiDevice *md =
            dynamic_cast<MidiDevice *>(getSelectedInstrument()->getDevice());
    if (!md) {
        std::cerr << "WARNING: MIDIInstrumentParameterPanel::updateBankComboBox(): No MidiDevice for Instrument " << getSelectedInstrument()->getId() << '\n';
        return;
    }

    int currentBank = -1;
    BankList banks;

    RG_DEBUG << "updateBankComboBox(): Variation type is " << md->getVariationType();

    if (md->getVariationType() == MidiDevice::NoVariations) {

        banks = md->getBanks(getSelectedInstrument()->isPercussion());

        // If there are banks to display, show the bank widgets.
        // Why not showBank(banks.size()>1)?  Because that would hide the
        // bank checkbox which would take away the user's ability to
        // enable/disable bank selects.  If we do away with the checkbox
        // in the future, we should re-evaluate this decision.
        showBank(!banks.empty());

        // Find the selected bank in the MIDI Device's bank list.
        for (unsigned int i = 0; i < banks.size(); ++i) {
            if (getSelectedInstrument()->getProgram().getBank().partialCompare(banks[i])) {
                currentBank = i;
                break;
            }
        }

    } else {

        // Usually in variation mode, the bank widgets will be hidden.
        // E.g. in GM2, the MSB for all banks is 121 with the variations
        // in the LSB numbered 0-9.  If, however, there were another
        // MSB, say 122, with some variations in the LSB, this code would
        // display the Bank combobox to allow selection of the MSB.

        // If the variations are in the LSB, then the banks are in the MSB
        // and vice versa.
        bool useMSB = (md->getVariationType() == MidiDevice::VariationFromLSB);

        MidiByteList bytes;

        if (useMSB) {
            bytes = md->getDistinctMSBs(getSelectedInstrument()->isPercussion());
        } else {
            bytes = md->getDistinctLSBs(getSelectedInstrument()->isPercussion());
        }

        // If more than one bank value is found, show the bank widgets.
        showBank(bytes.size() > 1);

        // Load "banks" with the banks and figure out currentBank.

        if (useMSB) {
            for (unsigned int i = 0; i < bytes.size(); ++i) {
                BankList bl = md->getBanksByMSB
                              (getSelectedInstrument()->isPercussion(), bytes[i]);
                RG_DEBUG << "updateBankComboBox(): Have " << bl.size() << " variations for MSB " << bytes[i];

                if (bl.size() == 0)
                    continue;
                if (getSelectedInstrument()->getMSB() == bytes[i]) {
                    currentBank = banks.size();
                }
                banks.push_back(bl[0]);
            }
        } else {
            for (unsigned int i = 0; i < bytes.size(); ++i) {
                BankList bl = md->getBanksByLSB
                              (getSelectedInstrument()->isPercussion(), bytes[i]);

                RG_DEBUG << "updateBankComboBox(): Have " << bl.size() << " variations for LSB " << bytes[i];

                if (bl.size() == 0)
                    continue;
                if (getSelectedInstrument()->getLSB() == bytes[i]) {
                    currentBank = banks.size();
                }
                banks.push_back(bl[0]);
            }
        }
    }

    // Populate the combobox with bank names.

    // If we need to repopulate m_bankComboBox
    if (banks != m_banks)
    {
        // Update the cache.
        m_banks = banks;

        // Copy from m_banks to m_bankComboBox.
        m_bankComboBox->clear();
        for (BankList::const_iterator i = m_banks.begin();
                i != m_banks.end(); ++i) {
            m_bankComboBox->addItem(QObject::tr(i->getName().c_str()));
        }
    }

    m_bankComboBox->setEnabled(getSelectedInstrument()->sendsBankSelect());

#if 0
// ??? This is a pretty nifty idea, but unfortunately, it requires
//     that we maintain a bogus combobox entry.  For now, we'll go
//     with the simpler "unselected" approach.

    // If the current bank was not found...
    if (currentBank < 0  &&  !banks.empty()) {
        // Format bank MSB:LSB and add to combobox.
        MidiBank bank = getSelectedInstrument()->getProgram().getBank();
        QString bankString = QString("%1:%2").arg(bank.getMSB()).arg(bank.getLSB());
        m_bankComboBox.addItem(bankString);
        currentBank = banks.size();
    }
#endif

    // If the bank wasn't in the Device, show the bank widgets so
    // the user can fix it if they want.
    if (currentBank == -1  &&  !banks.empty())
        showBank(true);

    // Display the current bank.
    m_bankComboBox->setCurrentIndex(currentBank);
}