165 lines
5.2 KiB
C++
165 lines
5.2 KiB
C++
/* ✔ */
|
|
|
|
#pragma once
|
|
|
|
#include "Bank.hpp"
|
|
#include "BankConfig.hpp"
|
|
#include <Def/MIDIAddress.hpp>
|
|
|
|
BEGIN_CS_NAMESPACE
|
|
|
|
/// Class for keeping track of the active bank, and allows locking of the
|
|
/// bank setting.
|
|
///
|
|
/// @see @ref OutputBankableMIDIAddress
|
|
class OutputBankableMIDIAddress_Base {
|
|
protected:
|
|
/**
|
|
* @brief Constructor.
|
|
*
|
|
* @param bank
|
|
* The bank to add this element to.
|
|
*/
|
|
OutputBankableMIDIAddress_Base(const OutputBank &bank) : bank(bank) {}
|
|
|
|
public:
|
|
/**
|
|
* @brief Get the actual bank setting (no matter whether the element is
|
|
* locked or not).
|
|
*/
|
|
setting_t getRawBankSetting() const { return bank.getSelection(); }
|
|
|
|
/**
|
|
* @brief Get the bank setting.
|
|
*
|
|
* If the element is locked, the bank setting from the moment it was locked
|
|
* is returned.
|
|
*/
|
|
setting_t getSelection() const {
|
|
return lockedSetting == Unlocked ? getRawBankSetting() : lockedSetting;
|
|
}
|
|
|
|
/**
|
|
* @brief Lock the bank setting.
|
|
*
|
|
* As long as it's locked, `getSelection` will return the current setting,
|
|
* independent from the actual bank setting.
|
|
*/
|
|
void lock() {
|
|
if (lockedSetting == Unlocked)
|
|
lockedSetting = getRawBankSetting();
|
|
}
|
|
|
|
/**
|
|
* @brief Unlock the bank setting.
|
|
*
|
|
* After unlocking, @ref getSelection will return the actual bank setting
|
|
* again.
|
|
*/
|
|
void unlock() { lockedSetting = Unlocked; }
|
|
|
|
protected:
|
|
const OutputBank &bank;
|
|
|
|
private:
|
|
constexpr static setting_t Unlocked = NoSetting;
|
|
setting_t lockedSetting = Unlocked;
|
|
};
|
|
|
|
/**
|
|
* @brief A base class for all MIDIOutputElement%s that can be banked.
|
|
*
|
|
* @note These elements don't have to be updated when the bank setting is
|
|
* changed, because they poll the bank setting each time they send a
|
|
* MIDI event.
|
|
* They are not added to the bank, they just keep a reference to the
|
|
* bank they're a part of.
|
|
*
|
|
* @note To prevent 'sticky' notes (i.e. a button is pressed, a note on is
|
|
* sent, the bank is changed, the button is released, and the note off
|
|
* is sent to a different address, causing the first note to keep on
|
|
* playing indefinitely), there must be a way to lock the bank setting
|
|
* while a note is playing. Then when it is no longer playing, the
|
|
* bank setting is unlocked.
|
|
*/
|
|
class OutputBankableMIDIAddress : public OutputBankableMIDIAddress_Base {
|
|
public:
|
|
/**
|
|
* @brief Create a new OutputBankableMIDIAddress object.
|
|
*
|
|
* @param bank
|
|
* The bank to add this element to.
|
|
* @param type
|
|
* What address type to change (address, channel or cable number).
|
|
*/
|
|
OutputBankableMIDIAddress(const OutputBank &bank, BankType type)
|
|
: OutputBankableMIDIAddress_Base(bank), type(type) {}
|
|
|
|
/**
|
|
* @brief Create a new OutputBankableMIDIAddress object.
|
|
*
|
|
* @param config
|
|
* The bank and address type to change.
|
|
*
|
|
* @see OutputBankableMIDIAddress::OutputBankableMIDIAddress(Bank<N> &, BankType)
|
|
*/
|
|
OutputBankableMIDIAddress(BaseOutputBankConfig config)
|
|
: OutputBankableMIDIAddress(config.bank, config.type) {}
|
|
|
|
/**
|
|
* @brief Get the offset relative to the base address.
|
|
*/
|
|
RelativeMIDIAddress getAddressOffset(setting_t bankindex) const {
|
|
int8_t offset = bank.getOffsetOfSetting(bankindex);
|
|
switch (type) {
|
|
case ChangeAddress: return {offset, 0, 0};
|
|
case ChangeChannel: return {0, offset, 0};
|
|
case ChangeCable: return {0, 0, offset};
|
|
default: return {};
|
|
}
|
|
}
|
|
/**
|
|
* @brief Get the offset relative to the base address for the active bank.
|
|
*/
|
|
RelativeMIDIAddress getAddressOffset() const {
|
|
return getAddressOffset(getSelection());
|
|
}
|
|
|
|
private:
|
|
const BankType type;
|
|
};
|
|
|
|
/**
|
|
* @brief A base class for all MIDIOutputElement%s that can have one of many
|
|
* addresses.
|
|
*
|
|
* The bank setting determines the address that's being used.
|
|
*
|
|
* @note To prevent 'sticky' notes (i.e. a button is pressed, a note on is
|
|
* sent, the bank is changed, the button is released, and the note off
|
|
* is sent to a different address, causing the first note to keep on
|
|
* playing indefinitely), there must be a way to lock the bank setting
|
|
* while a note is playing. Then when it is no longer playing, the
|
|
* bank setting is unlocked.
|
|
*
|
|
* @invariant `getSelection` and `getRawBankSetting` always return a number in
|
|
* the half-open interval
|
|
* @f$ \left[0, \mathrm{bank.getNumberOfBanks()}\right) \cap
|
|
* \mathbb{N} @f$.
|
|
*/
|
|
class ManyAddresses_Base : public OutputBankableMIDIAddress_Base {
|
|
public:
|
|
/**
|
|
* @brief Constructor.
|
|
*
|
|
* @param bank
|
|
* The bank to add this element to.
|
|
* @tparam NumBanks
|
|
* The number of bank settings @p bank has.
|
|
*/
|
|
template <uint8_t NumBanks>
|
|
ManyAddresses_Base(const Bank<NumBanks> &bank)
|
|
: OutputBankableMIDIAddress_Base(bank) {}
|
|
};
|
|
|
|
END_CS_NAMESPACE |