/*************************************************************************** * * * LinuxSampler - modular, streaming capable sampler * * * * Copyright (C) 2003, 2004 by Benno Senoner and Christian Schoenebeck * * Copyright (C) 2005 - 2020 Christian Schoenebeck * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the Free Software * * Foundation, Inc., 59 Temple Place, Suite 330, Boston, * * MA 02111-1307 USA * ***************************************************************************/ #include "EngineChannel.h" #include #include "../Sampler.h" #include "../common/global_private.h" #include "../drivers/midi/MidiInstrumentMapper.h" #include "../common/atomic.h" #define NO_MIDI_INSTRUMENT_MAP -1 #define DEFAULT_MIDI_INSTRUMENT_MAP -2 namespace LinuxSampler { struct EngineChannel::private_data_t { int iMute; bool bSolo; uint8_t uiMidiProgram; uint8_t uiMidiBankMsb; uint8_t uiMidiBankLsb; uint8_t uiMidiRpnMsb; ///< MIDI Registered Parameter Number (upper 7 bits / coarse) uint8_t uiMidiRpnLsb; ///< MIDI Registered Parameter Number (lower 7 bits / fine) uint8_t uiMidiRpnDataMsb; ///< New MIDI RPN Parameter Value (upper 7 bits / coarse) uint8_t uiMidiRpnDataLsb; ///< New MIDI RPN Parameter Value (lower 7 bits / fine) uint8_t uiMidiNrpnMsb; ///< MIDI Non-Registered Parameter Number (upper 7 bits / coarse) uint8_t uiMidiNrpnLsb; ///< MIDI Non-Registered Parameter Number (lower 7 bits / fine) uint8_t uiMidiNrpnDataMsb; ///< New MIDI NRPN Parameter Value (upper 7 bits / coarse) uint8_t uiMidiNrpnDataLsb; ///< New MIDI NRPN Parameter Value (lower 7 bits / fine) bool bMidiBankMsbReceived; bool bMidiBankLsbReceived; bool bProgramChangeReceived; bool bMidiRpnReceived; bool bMidiNrpnReceived; int iMidiInstrumentMap; atomic_t voiceCount; atomic_t diskStreamCount; SamplerChannel* pSamplerChannel; ListenerList llFxSendCountListeners; }; EngineChannel::EngineChannel() : p(new private_data_t) { p->iMute = 0; p->bSolo = false; p->uiMidiBankMsb = 0; p->uiMidiBankLsb = 0; p->uiMidiProgram = 0; p->bProgramChangeReceived = p->bMidiBankMsbReceived = p->bMidiBankLsbReceived = false; p->iMidiInstrumentMap = NO_MIDI_INSTRUMENT_MAP; SetVoiceCount(0); SetDiskStreamCount(0); p->pSamplerChannel = NULL; ResetMidiRpnParameter(); ResetMidiNrpnParameter(); } EngineChannel::~EngineChannel() { delete p; } /** * Sometimes an instrument is splitted over several files. For example * the GigaStudio format may split an instrument over a certain amount * of files like: "Foo.gig", "Foo.gx01", "Foo.gx02", ... * This method allows to retrieve the whole list of files that is used * for the currently loaded instrument on this engine channel. * Calling this method with index 0 is equivalent as calling the equal * named method without any argument. * * @param index - index of sought file name (0, 1, 2, ...) * @returns file name or empty string if index out of bounds */ String EngineChannel::InstrumentFileName(int index) { return (index == 0) ? InstrumentFileName() : ""; } void EngineChannel::SetMute(int state) throw (Exception) { if (p->iMute == state) return; if (state < -1 || state > 1) throw Exception("Invalid Mute state: " + ToString(state)); p->iMute = state; StatusChanged(true); } int EngineChannel::GetMute() { return p->iMute; } void EngineChannel::SetSolo(bool solo) { if (p->bSolo == solo) return; p->bSolo = solo; StatusChanged(true); } bool EngineChannel::GetSolo() { return p->bSolo; } /* We use a workaround for MIDI devices (i.e. old keyboards) which either only send bank select MSB or only bank select LSB messages. Bank selects will be modified according to the following table: MIDI Sequence received: -> GetMidiBankMsb()= | GetMidiBankLsb()= --------------------------------------------------------------------------- program change -> 0 | 0 bank LSB, program change -> 0 | LSB value bank MSB, program change -> 0 | MSB value bank LSB, bank MSB, program change -> MSB value | LSB value bank MSB, bank LSB, program change -> MSB value | LSB value --------------------------------------------------------------------------- That way we ensure those limited devices always to switch between the following set of MIDI instrument banks: { 0, 1, 2, ..., 127 } */ uint8_t EngineChannel::GetMidiProgram() { return p->uiMidiProgram; // AFAIK atomic on all systems } void EngineChannel::SetMidiProgram(uint8_t Program) { p->bProgramChangeReceived = true; p->uiMidiProgram = Program; // AFAIK atomic on all systems } uint8_t EngineChannel::GetMidiBankMsb() { return (p->bMidiBankMsbReceived && p->bMidiBankLsbReceived) ? p->uiMidiBankMsb : 0; } void EngineChannel::SetMidiBankMsb(uint8_t BankMSB) { if (p->bProgramChangeReceived) { p->bProgramChangeReceived = p->bMidiBankLsbReceived = false; } p->bMidiBankMsbReceived = true; p->uiMidiBankMsb = BankMSB; // AFAIK atomic on all systems } uint8_t EngineChannel::GetMidiBankLsb() { return (!p->bMidiBankMsbReceived && !p->bMidiBankLsbReceived) ? 0 : (p->bMidiBankMsbReceived && !p->bMidiBankLsbReceived) ? p->uiMidiBankMsb : p->uiMidiBankLsb; } void EngineChannel::SetMidiBankLsb(uint8_t BankLSB) { if (p->bProgramChangeReceived) { p->bProgramChangeReceived = p->bMidiBankMsbReceived = false; } p->bMidiBankLsbReceived = true; p->uiMidiBankLsb = BankLSB; // AFAIK atomic on all systems } bool EngineChannel::UsesNoMidiInstrumentMap() { return (p->iMidiInstrumentMap == NO_MIDI_INSTRUMENT_MAP); } bool EngineChannel::UsesDefaultMidiInstrumentMap() { return (p->iMidiInstrumentMap == DEFAULT_MIDI_INSTRUMENT_MAP); } int EngineChannel::GetMidiInstrumentMap() throw (Exception) { if (UsesNoMidiInstrumentMap()) throw Exception("EngineChannel is using no MIDI instrument map"); if (UsesDefaultMidiInstrumentMap()) throw Exception("EngineChannel is using default MIDI instrument map"); // check if the stored map still exists in the MIDI instrument mapper std::vector maps = MidiInstrumentMapper::Maps(); if (find(maps.begin(), maps.end(), p->iMidiInstrumentMap) == maps.end()) { // it doesn't exist anymore, so fall back to NONE and throw an exception p->iMidiInstrumentMap = NO_MIDI_INSTRUMENT_MAP; throw Exception("Assigned MIDI instrument map doesn't exist anymore, falling back to NONE"); } return p->iMidiInstrumentMap; } void EngineChannel::SetMidiInstrumentMapToNone() { if (p->iMidiInstrumentMap == NO_MIDI_INSTRUMENT_MAP) return; p->iMidiInstrumentMap = NO_MIDI_INSTRUMENT_MAP; StatusChanged(true); } void EngineChannel::SetMidiInstrumentMapToDefault() { if (p->iMidiInstrumentMap == DEFAULT_MIDI_INSTRUMENT_MAP) return; p->iMidiInstrumentMap = DEFAULT_MIDI_INSTRUMENT_MAP; StatusChanged(true); } void EngineChannel::SetMidiInstrumentMap(int MidiMap) throw (Exception) { if (p->iMidiInstrumentMap == MidiMap) return; // check if given map actually exists in the MIDI instrument mapper std::vector maps = MidiInstrumentMapper::Maps(); if (find(maps.begin(), maps.end(), MidiMap) == maps.end()) throw Exception("MIDI instrument map doesn't exist"); p->iMidiInstrumentMap = MidiMap; // assign the new map ID StatusChanged(true); } // RPNs ... void EngineChannel::SetMidiRpnParameterMsb(uint8_t ParamMSB) { p->uiMidiRpnMsb = ParamMSB & 127; p->bMidiRpnReceived = true; } void EngineChannel::SetMidiRpnControllerMsb(uint8_t CtrlMSB) { // deprecated API SetMidiRpnParameterMsb(CtrlMSB); } void EngineChannel::SetMidiRpnParameterLsb(uint8_t ParamLSB) { p->uiMidiRpnLsb = ParamLSB & 127; p->bMidiRpnReceived = true; } void EngineChannel::SetMidiRpnControllerLsb(uint8_t CtrlLSB) { // deprecated API SetMidiRpnParameterLsb(CtrlLSB); } void EngineChannel::SetMidiRpnDataMsb(uint8_t DataMSB) { p->uiMidiRpnDataMsb = DataMSB & 127; } void EngineChannel::SetMidiRpnDataLsb(uint8_t DataLSB) { p->uiMidiRpnDataLsb = DataLSB & 127; } void EngineChannel::SetMidiRpnData(int Data) { if (Data < 0) Data = 0; if (Data > 16383) Data = 16383; p->uiMidiRpnDataMsb = (Data >> 7) & 127; p->uiMidiRpnDataLsb = Data & 127; } void EngineChannel::ResetMidiRpnParameter() { p->uiMidiRpnMsb = p->uiMidiRpnLsb = 0; p->uiMidiRpnDataMsb = p->uiMidiRpnDataLsb = 0; p->bMidiRpnReceived = false; } void EngineChannel::ResetMidiRpnController() { // deprecated API ResetMidiRpnParameter(); } int EngineChannel::GetMidiRpnParameter() { return (p->bMidiRpnReceived) ? (p->uiMidiRpnMsb << 7) | p->uiMidiRpnLsb : -1; } int EngineChannel::GetMidiRpnController() { // deprecated API return (p->bMidiRpnReceived) ? (p->uiMidiRpnMsb << 8) | p->uiMidiRpnLsb : -1; } int EngineChannel::GetMidiRpnData() { return (p->bMidiRpnReceived) ? (p->uiMidiRpnDataMsb << 7) | p->uiMidiRpnDataLsb : 0; } // NRPNs ... void EngineChannel::SetMidiNrpnParameterMsb(uint8_t ParamMSB) { p->uiMidiNrpnMsb = ParamMSB & 127; p->bMidiNrpnReceived = true; } void EngineChannel::SetMidiNrpnControllerMsb(uint8_t CtrlMSB) { // deprecated API SetMidiNrpnParameterMsb(CtrlMSB); } void EngineChannel::SetMidiNrpnParameterLsb(uint8_t ParamLSB) { p->uiMidiNrpnLsb = ParamLSB & 127; p->bMidiNrpnReceived = true; } void EngineChannel::SetMidiNrpnControllerLsb(uint8_t CtrlLSB) { // deprecated API SetMidiNrpnParameterLsb(CtrlLSB); } void EngineChannel::SetMidiNrpnDataMsb(uint8_t DataMSB) { p->uiMidiNrpnDataMsb = DataMSB & 127; } void EngineChannel::SetMidiNrpnDataLsb(uint8_t DataLSB) { p->uiMidiNrpnDataLsb = DataLSB & 127; } void EngineChannel::SetMidiNrpnData(int Data) { if (Data < 0) Data = 0; if (Data > 16383) Data = 16383; p->uiMidiNrpnDataMsb = (Data >> 7) & 127; p->uiMidiNrpnDataLsb = Data & 127; } void EngineChannel::ResetMidiNrpnParameter() { p->uiMidiNrpnMsb = p->uiMidiNrpnLsb = 0; p->uiMidiNrpnDataMsb = p->uiMidiNrpnDataLsb = 0; p->bMidiNrpnReceived = false; } void EngineChannel::ResetMidiNrpnController() { // deprecated API ResetMidiNrpnParameter(); } int EngineChannel::GetMidiNrpnParameter() { return (p->bMidiNrpnReceived) ? (p->uiMidiNrpnMsb << 7) | p->uiMidiNrpnLsb : -1; } int EngineChannel::GetMidiNrpnController() { // deprecated API return (p->bMidiNrpnReceived) ? (p->uiMidiNrpnMsb << 8) | p->uiMidiNrpnLsb : -1; } int EngineChannel::GetMidiNrpnData() { return (p->bMidiNrpnReceived) ? (p->uiMidiNrpnDataMsb << 7) | p->uiMidiNrpnDataLsb : 0; } uint EngineChannel::GetVoiceCount() { return atomic_read(&p->voiceCount); } void EngineChannel::SetVoiceCount(uint Voices) { atomic_set(&p->voiceCount, Voices); } uint EngineChannel::GetDiskStreamCount() { return atomic_read(&p->diskStreamCount); } void EngineChannel::SetDiskStreamCount(uint Streams) { atomic_set(&p->diskStreamCount, Streams); } SamplerChannel* EngineChannel::GetSamplerChannel() { if (p->pSamplerChannel == NULL) { std::cerr << "EngineChannel::GetSamplerChannel(): pSamplerChannel is NULL, this is a bug!\n" << std::flush; } return p->pSamplerChannel; } void EngineChannel::SetSamplerChannel(SamplerChannel* pChannel) { p->pSamplerChannel = pChannel; } Sampler* EngineChannel::GetSampler() { if (GetSamplerChannel() == NULL) return NULL; return GetSamplerChannel()->GetSampler(); } void EngineChannel::AddFxSendCountListener(FxSendCountListener* l) { p->llFxSendCountListeners.AddListener(l); } void EngineChannel::RemoveFxSendCountListener(FxSendCountListener* l) { p->llFxSendCountListeners.RemoveListener(l); } void EngineChannel::RemoveAllFxSendCountListeners() { p->llFxSendCountListeners.RemoveAllListeners(); } void EngineChannel::fireFxSendCountChanged(int ChannelId, int NewCount) { for (int i = 0; i < p->llFxSendCountListeners.GetListenerCount(); i++) { p->llFxSendCountListeners.GetListener(i)->FxSendCountChanged(ChannelId, NewCount); } } void EngineChannel::ExecuteProgramChange(uint32_t Program) { uint8_t hb = (Program >> 16) & 0xff; uint8_t lb = (Program >> 8) & 0xff; uint8_t pc = Program & 0x7f; dmsg(1,("Received MIDI program change (msb=%d) (lsb=%d) (prog=%d)\n", hb ,lb, pc)); std::vector maps = MidiInstrumentMapper::Maps(); if (maps.empty()) return; if (UsesNoMidiInstrumentMap()) return; if (MidiInstrumentMapper::GetMapCount() == 0) return; // retrieve the MIDI instrument map this engine channel is assigned to int iMapID = (UsesDefaultMidiInstrumentMap()) ? MidiInstrumentMapper::GetDefaultMap() /*default*/ : GetMidiInstrumentMap(); // is there an entry for this MIDI bank&prog pair in that map? midi_prog_index_t midiIndex; midiIndex.midi_bank_msb = hb; midiIndex.midi_bank_lsb = lb; midiIndex.midi_prog = pc; optional mapping = MidiInstrumentMapper::GetEntry(iMapID, midiIndex); if (mapping) { // if mapping exists ... InstrumentManager::instrument_id_t id; id.FileName = mapping->InstrumentFile; id.Index = mapping->InstrumentIndex; //TODO: we should switch the engine type here InstrumentManager::LoadInstrumentInBackground(id, this); Volume(mapping->Volume); } } } // namespace LinuxSampler