1 /*************************************************************************** 2 * * 3 * LinuxSampler - modular, streaming capable sampler * 4 * * 5 * Copyright (C) 2003, 2004 by Benno Senoner and Christian Schoenebeck * 6 * Copyright (C) 2005 - 2020 Christian Schoenebeck * 7 * * 8 * This program is free software; you can redistribute it and/or modify * 9 * it under the terms of the GNU General Public License as published by * 10 * the Free Software Foundation; either version 2 of the License, or * 11 * (at your option) any later version. * 12 * * 13 * This program is distributed in the hope that it will be useful, * 14 * but WITHOUT ANY WARRANTY; without even the implied warranty of * 15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * 16 * GNU General Public License for more details. * 17 * * 18 * You should have received a copy of the GNU General Public License * 19 * along with this program; if not, write to the Free Software * 20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, * 21 * MA 02111-1307 USA * 22 ***************************************************************************/ 23 24 #include "EngineChannel.h" 25 26 #include <algorithm> 27 28 #include "../Sampler.h" 29 #include "../common/global_private.h" 30 #include "../drivers/midi/MidiInstrumentMapper.h" 31 #include "../common/atomic.h" 32 33 #define NO_MIDI_INSTRUMENT_MAP -1 34 #define DEFAULT_MIDI_INSTRUMENT_MAP -2 35 36 namespace LinuxSampler { 37 38 struct EngineChannel::private_data_t { 39 int iMute; 40 bool bSolo; 41 uint8_t uiMidiProgram; 42 uint8_t uiMidiBankMsb; 43 uint8_t uiMidiBankLsb; 44 uint8_t uiMidiRpnMsb; ///< MIDI Registered Parameter Number (upper 7 bits / coarse) 45 uint8_t uiMidiRpnLsb; ///< MIDI Registered Parameter Number (lower 7 bits / fine) 46 uint8_t uiMidiRpnDataMsb; ///< New MIDI RPN Parameter Value (upper 7 bits / coarse) 47 uint8_t uiMidiRpnDataLsb; ///< New MIDI RPN Parameter Value (lower 7 bits / fine) 48 uint8_t uiMidiNrpnMsb; ///< MIDI Non-Registered Parameter Number (upper 7 bits / coarse) 49 uint8_t uiMidiNrpnLsb; ///< MIDI Non-Registered Parameter Number (lower 7 bits / fine) 50 uint8_t uiMidiNrpnDataMsb; ///< New MIDI NRPN Parameter Value (upper 7 bits / coarse) 51 uint8_t uiMidiNrpnDataLsb; ///< New MIDI NRPN Parameter Value (lower 7 bits / fine) 52 bool bMidiBankMsbReceived; 53 bool bMidiBankLsbReceived; 54 bool bProgramChangeReceived; 55 bool bMidiRpnReceived; 56 bool bMidiNrpnReceived; 57 int iMidiInstrumentMap; 58 atomic_t voiceCount; 59 atomic_t diskStreamCount; 60 SamplerChannel* pSamplerChannel; 61 ListenerList<FxSendCountListener*> llFxSendCountListeners; 62 }; 63 EngineChannel()64 EngineChannel::EngineChannel() : p(new private_data_t) { 65 p->iMute = 0; 66 p->bSolo = false; 67 p->uiMidiBankMsb = 0; 68 p->uiMidiBankLsb = 0; 69 p->uiMidiProgram = 0; 70 p->bProgramChangeReceived = 71 p->bMidiBankMsbReceived = 72 p->bMidiBankLsbReceived = false; 73 p->iMidiInstrumentMap = NO_MIDI_INSTRUMENT_MAP; 74 SetVoiceCount(0); 75 SetDiskStreamCount(0); 76 p->pSamplerChannel = NULL; 77 ResetMidiRpnParameter(); 78 ResetMidiNrpnParameter(); 79 } 80 ~EngineChannel()81 EngineChannel::~EngineChannel() { 82 delete p; 83 } 84 85 /** 86 * Sometimes an instrument is splitted over several files. For example 87 * the GigaStudio format may split an instrument over a certain amount 88 * of files like: "Foo.gig", "Foo.gx01", "Foo.gx02", ... 89 * This method allows to retrieve the whole list of files that is used 90 * for the currently loaded instrument on this engine channel. 91 * Calling this method with index 0 is equivalent as calling the equal 92 * named method without any argument. 93 * 94 * @param index - index of sought file name (0, 1, 2, ...) 95 * @returns file name or empty string if index out of bounds 96 */ InstrumentFileName(int index)97 String EngineChannel::InstrumentFileName(int index) { 98 return (index == 0) ? InstrumentFileName() : ""; 99 } 100 SetMute(int state)101 void EngineChannel::SetMute(int state) throw (Exception) { 102 if (p->iMute == state) return; 103 if (state < -1 || state > 1) 104 throw Exception("Invalid Mute state: " + ToString(state)); 105 106 p->iMute = state; 107 108 StatusChanged(true); 109 } 110 GetMute()111 int EngineChannel::GetMute() { 112 return p->iMute; 113 } 114 SetSolo(bool solo)115 void EngineChannel::SetSolo(bool solo) { 116 if (p->bSolo == solo) return; 117 p->bSolo = solo; 118 StatusChanged(true); 119 } 120 GetSolo()121 bool EngineChannel::GetSolo() { 122 return p->bSolo; 123 } 124 125 /* 126 We use a workaround for MIDI devices (i.e. old keyboards) which either 127 only send bank select MSB or only bank select LSB messages. Bank 128 selects will be modified according to the following table: 129 130 MIDI Sequence received: -> GetMidiBankMsb()= | GetMidiBankLsb()= 131 --------------------------------------------------------------------------- 132 program change -> 0 | 0 133 bank LSB, program change -> 0 | LSB value 134 bank MSB, program change -> 0 | MSB value 135 bank LSB, bank MSB, program change -> MSB value | LSB value 136 bank MSB, bank LSB, program change -> MSB value | LSB value 137 --------------------------------------------------------------------------- 138 139 That way we ensure those limited devices always to switch between the 140 following set of MIDI instrument banks: { 0, 1, 2, ..., 127 } 141 */ 142 GetMidiProgram()143 uint8_t EngineChannel::GetMidiProgram() { 144 return p->uiMidiProgram; // AFAIK atomic on all systems 145 } 146 SetMidiProgram(uint8_t Program)147 void EngineChannel::SetMidiProgram(uint8_t Program) { 148 p->bProgramChangeReceived = true; 149 p->uiMidiProgram = Program; // AFAIK atomic on all systems 150 } 151 GetMidiBankMsb()152 uint8_t EngineChannel::GetMidiBankMsb() { 153 return (p->bMidiBankMsbReceived && p->bMidiBankLsbReceived) 154 ? p->uiMidiBankMsb : 0; 155 } 156 SetMidiBankMsb(uint8_t BankMSB)157 void EngineChannel::SetMidiBankMsb(uint8_t BankMSB) { 158 if (p->bProgramChangeReceived) { 159 p->bProgramChangeReceived = 160 p->bMidiBankLsbReceived = false; 161 } 162 p->bMidiBankMsbReceived = true; 163 p->uiMidiBankMsb = BankMSB; // AFAIK atomic on all systems 164 } 165 GetMidiBankLsb()166 uint8_t EngineChannel::GetMidiBankLsb() { 167 return (!p->bMidiBankMsbReceived && !p->bMidiBankLsbReceived) 168 ? 0 169 : (p->bMidiBankMsbReceived && !p->bMidiBankLsbReceived) 170 ? p->uiMidiBankMsb 171 : p->uiMidiBankLsb; 172 } 173 SetMidiBankLsb(uint8_t BankLSB)174 void EngineChannel::SetMidiBankLsb(uint8_t BankLSB) { 175 if (p->bProgramChangeReceived) { 176 p->bProgramChangeReceived = 177 p->bMidiBankMsbReceived = false; 178 } 179 p->bMidiBankLsbReceived = true; 180 p->uiMidiBankLsb = BankLSB; // AFAIK atomic on all systems 181 } 182 UsesNoMidiInstrumentMap()183 bool EngineChannel::UsesNoMidiInstrumentMap() { 184 return (p->iMidiInstrumentMap == NO_MIDI_INSTRUMENT_MAP); 185 } 186 UsesDefaultMidiInstrumentMap()187 bool EngineChannel::UsesDefaultMidiInstrumentMap() { 188 return (p->iMidiInstrumentMap == DEFAULT_MIDI_INSTRUMENT_MAP); 189 } 190 GetMidiInstrumentMap()191 int EngineChannel::GetMidiInstrumentMap() throw (Exception) { 192 if (UsesNoMidiInstrumentMap()) 193 throw Exception("EngineChannel is using no MIDI instrument map"); 194 if (UsesDefaultMidiInstrumentMap()) 195 throw Exception("EngineChannel is using default MIDI instrument map"); 196 // check if the stored map still exists in the MIDI instrument mapper 197 std::vector<int> maps = MidiInstrumentMapper::Maps(); 198 if (find(maps.begin(), maps.end(), p->iMidiInstrumentMap) == maps.end()) { 199 // it doesn't exist anymore, so fall back to NONE and throw an exception 200 p->iMidiInstrumentMap = NO_MIDI_INSTRUMENT_MAP; 201 throw Exception("Assigned MIDI instrument map doesn't exist anymore, falling back to NONE"); 202 } 203 return p->iMidiInstrumentMap; 204 } 205 SetMidiInstrumentMapToNone()206 void EngineChannel::SetMidiInstrumentMapToNone() { 207 if (p->iMidiInstrumentMap == NO_MIDI_INSTRUMENT_MAP) return; 208 p->iMidiInstrumentMap = NO_MIDI_INSTRUMENT_MAP; 209 StatusChanged(true); 210 } 211 SetMidiInstrumentMapToDefault()212 void EngineChannel::SetMidiInstrumentMapToDefault() { 213 if (p->iMidiInstrumentMap == DEFAULT_MIDI_INSTRUMENT_MAP) return; 214 p->iMidiInstrumentMap = DEFAULT_MIDI_INSTRUMENT_MAP; 215 StatusChanged(true); 216 } 217 SetMidiInstrumentMap(int MidiMap)218 void EngineChannel::SetMidiInstrumentMap(int MidiMap) throw (Exception) { 219 if (p->iMidiInstrumentMap == MidiMap) return; 220 221 // check if given map actually exists in the MIDI instrument mapper 222 std::vector<int> maps = MidiInstrumentMapper::Maps(); 223 if (find(maps.begin(), maps.end(), MidiMap) == maps.end()) 224 throw Exception("MIDI instrument map doesn't exist"); 225 p->iMidiInstrumentMap = MidiMap; // assign the new map ID 226 StatusChanged(true); 227 } 228 229 // RPNs ... 230 SetMidiRpnParameterMsb(uint8_t ParamMSB)231 void EngineChannel::SetMidiRpnParameterMsb(uint8_t ParamMSB) { 232 p->uiMidiRpnMsb = ParamMSB & 127; 233 p->bMidiRpnReceived = true; 234 } 235 SetMidiRpnControllerMsb(uint8_t CtrlMSB)236 void EngineChannel::SetMidiRpnControllerMsb(uint8_t CtrlMSB) { // deprecated API 237 SetMidiRpnParameterMsb(CtrlMSB); 238 } 239 SetMidiRpnParameterLsb(uint8_t ParamLSB)240 void EngineChannel::SetMidiRpnParameterLsb(uint8_t ParamLSB) { 241 p->uiMidiRpnLsb = ParamLSB & 127; 242 p->bMidiRpnReceived = true; 243 } 244 SetMidiRpnControllerLsb(uint8_t CtrlLSB)245 void EngineChannel::SetMidiRpnControllerLsb(uint8_t CtrlLSB) { // deprecated API 246 SetMidiRpnParameterLsb(CtrlLSB); 247 } 248 SetMidiRpnDataMsb(uint8_t DataMSB)249 void EngineChannel::SetMidiRpnDataMsb(uint8_t DataMSB) { 250 p->uiMidiRpnDataMsb = DataMSB & 127; 251 } 252 SetMidiRpnDataLsb(uint8_t DataLSB)253 void EngineChannel::SetMidiRpnDataLsb(uint8_t DataLSB) { 254 p->uiMidiRpnDataLsb = DataLSB & 127; 255 } 256 SetMidiRpnData(int Data)257 void EngineChannel::SetMidiRpnData(int Data) { 258 if (Data < 0) Data = 0; 259 if (Data > 16383) Data = 16383; 260 p->uiMidiRpnDataMsb = (Data >> 7) & 127; 261 p->uiMidiRpnDataLsb = Data & 127; 262 } 263 ResetMidiRpnParameter()264 void EngineChannel::ResetMidiRpnParameter() { 265 p->uiMidiRpnMsb = p->uiMidiRpnLsb = 0; 266 p->uiMidiRpnDataMsb = p->uiMidiRpnDataLsb = 0; 267 p->bMidiRpnReceived = false; 268 } 269 ResetMidiRpnController()270 void EngineChannel::ResetMidiRpnController() { // deprecated API 271 ResetMidiRpnParameter(); 272 } 273 GetMidiRpnParameter()274 int EngineChannel::GetMidiRpnParameter() { 275 return (p->bMidiRpnReceived) ? 276 (p->uiMidiRpnMsb << 7) | p->uiMidiRpnLsb : -1; 277 } 278 GetMidiRpnController()279 int EngineChannel::GetMidiRpnController() { // deprecated API 280 return (p->bMidiRpnReceived) ? 281 (p->uiMidiRpnMsb << 8) | p->uiMidiRpnLsb : -1; 282 } 283 GetMidiRpnData()284 int EngineChannel::GetMidiRpnData() { 285 return (p->bMidiRpnReceived) ? 286 (p->uiMidiRpnDataMsb << 7) | p->uiMidiRpnDataLsb : 0; 287 } 288 289 // NRPNs ... 290 SetMidiNrpnParameterMsb(uint8_t ParamMSB)291 void EngineChannel::SetMidiNrpnParameterMsb(uint8_t ParamMSB) { 292 p->uiMidiNrpnMsb = ParamMSB & 127; 293 p->bMidiNrpnReceived = true; 294 } 295 SetMidiNrpnControllerMsb(uint8_t CtrlMSB)296 void EngineChannel::SetMidiNrpnControllerMsb(uint8_t CtrlMSB) { // deprecated API 297 SetMidiNrpnParameterMsb(CtrlMSB); 298 } 299 SetMidiNrpnParameterLsb(uint8_t ParamLSB)300 void EngineChannel::SetMidiNrpnParameterLsb(uint8_t ParamLSB) { 301 p->uiMidiNrpnLsb = ParamLSB & 127; 302 p->bMidiNrpnReceived = true; 303 } 304 SetMidiNrpnControllerLsb(uint8_t CtrlLSB)305 void EngineChannel::SetMidiNrpnControllerLsb(uint8_t CtrlLSB) { // deprecated API 306 SetMidiNrpnParameterLsb(CtrlLSB); 307 } 308 SetMidiNrpnDataMsb(uint8_t DataMSB)309 void EngineChannel::SetMidiNrpnDataMsb(uint8_t DataMSB) { 310 p->uiMidiNrpnDataMsb = DataMSB & 127; 311 } 312 SetMidiNrpnDataLsb(uint8_t DataLSB)313 void EngineChannel::SetMidiNrpnDataLsb(uint8_t DataLSB) { 314 p->uiMidiNrpnDataLsb = DataLSB & 127; 315 } 316 SetMidiNrpnData(int Data)317 void EngineChannel::SetMidiNrpnData(int Data) { 318 if (Data < 0) Data = 0; 319 if (Data > 16383) Data = 16383; 320 p->uiMidiNrpnDataMsb = (Data >> 7) & 127; 321 p->uiMidiNrpnDataLsb = Data & 127; 322 } 323 ResetMidiNrpnParameter()324 void EngineChannel::ResetMidiNrpnParameter() { 325 p->uiMidiNrpnMsb = p->uiMidiNrpnLsb = 0; 326 p->uiMidiNrpnDataMsb = p->uiMidiNrpnDataLsb = 0; 327 p->bMidiNrpnReceived = false; 328 } 329 ResetMidiNrpnController()330 void EngineChannel::ResetMidiNrpnController() { // deprecated API 331 ResetMidiNrpnParameter(); 332 } 333 GetMidiNrpnParameter()334 int EngineChannel::GetMidiNrpnParameter() { 335 return (p->bMidiNrpnReceived) ? 336 (p->uiMidiNrpnMsb << 7) | p->uiMidiNrpnLsb : -1; 337 } 338 GetMidiNrpnController()339 int EngineChannel::GetMidiNrpnController() { // deprecated API 340 return (p->bMidiNrpnReceived) ? 341 (p->uiMidiNrpnMsb << 8) | p->uiMidiNrpnLsb : -1; 342 } 343 GetMidiNrpnData()344 int EngineChannel::GetMidiNrpnData() { 345 return (p->bMidiNrpnReceived) ? 346 (p->uiMidiNrpnDataMsb << 7) | p->uiMidiNrpnDataLsb : 0; 347 } 348 GetVoiceCount()349 uint EngineChannel::GetVoiceCount() { 350 return atomic_read(&p->voiceCount); 351 } 352 SetVoiceCount(uint Voices)353 void EngineChannel::SetVoiceCount(uint Voices) { 354 atomic_set(&p->voiceCount, Voices); 355 } 356 GetDiskStreamCount()357 uint EngineChannel::GetDiskStreamCount() { 358 return atomic_read(&p->diskStreamCount); 359 } 360 SetDiskStreamCount(uint Streams)361 void EngineChannel::SetDiskStreamCount(uint Streams) { 362 atomic_set(&p->diskStreamCount, Streams); 363 } 364 GetSamplerChannel()365 SamplerChannel* EngineChannel::GetSamplerChannel() { 366 if (p->pSamplerChannel == NULL) { 367 std::cerr << "EngineChannel::GetSamplerChannel(): pSamplerChannel is NULL, this is a bug!\n" << std::flush; 368 } 369 return p->pSamplerChannel; 370 } 371 SetSamplerChannel(SamplerChannel * pChannel)372 void EngineChannel::SetSamplerChannel(SamplerChannel* pChannel) { 373 p->pSamplerChannel = pChannel; 374 } 375 GetSampler()376 Sampler* EngineChannel::GetSampler() { 377 if (GetSamplerChannel() == NULL) return NULL; 378 return GetSamplerChannel()->GetSampler(); 379 } 380 AddFxSendCountListener(FxSendCountListener * l)381 void EngineChannel::AddFxSendCountListener(FxSendCountListener* l) { 382 p->llFxSendCountListeners.AddListener(l); 383 } 384 RemoveFxSendCountListener(FxSendCountListener * l)385 void EngineChannel::RemoveFxSendCountListener(FxSendCountListener* l) { 386 p->llFxSendCountListeners.RemoveListener(l); 387 } 388 RemoveAllFxSendCountListeners()389 void EngineChannel::RemoveAllFxSendCountListeners() { 390 p->llFxSendCountListeners.RemoveAllListeners(); 391 } 392 fireFxSendCountChanged(int ChannelId,int NewCount)393 void EngineChannel::fireFxSendCountChanged(int ChannelId, int NewCount) { 394 for (int i = 0; i < p->llFxSendCountListeners.GetListenerCount(); i++) { 395 p->llFxSendCountListeners.GetListener(i)->FxSendCountChanged(ChannelId, NewCount); 396 } 397 } 398 ExecuteProgramChange(uint32_t Program)399 void EngineChannel::ExecuteProgramChange(uint32_t Program) { 400 uint8_t hb = (Program >> 16) & 0xff; 401 uint8_t lb = (Program >> 8) & 0xff; 402 uint8_t pc = Program & 0x7f; 403 404 dmsg(1,("Received MIDI program change (msb=%d) (lsb=%d) (prog=%d)\n", hb ,lb, pc)); 405 std::vector<int> maps = MidiInstrumentMapper::Maps(); 406 if (maps.empty()) return; 407 408 if (UsesNoMidiInstrumentMap()) return; 409 if (MidiInstrumentMapper::GetMapCount() == 0) return; 410 // retrieve the MIDI instrument map this engine channel is assigned to 411 int iMapID = (UsesDefaultMidiInstrumentMap()) 412 ? MidiInstrumentMapper::GetDefaultMap() /*default*/ : GetMidiInstrumentMap(); 413 // is there an entry for this MIDI bank&prog pair in that map? 414 midi_prog_index_t midiIndex; 415 midiIndex.midi_bank_msb = hb; 416 midiIndex.midi_bank_lsb = lb; 417 midiIndex.midi_prog = pc; 418 optional<MidiInstrumentMapper::entry_t> mapping = 419 MidiInstrumentMapper::GetEntry(iMapID, midiIndex); 420 if (mapping) { // if mapping exists ... 421 InstrumentManager::instrument_id_t id; 422 id.FileName = mapping->InstrumentFile; 423 id.Index = mapping->InstrumentIndex; 424 //TODO: we should switch the engine type here 425 InstrumentManager::LoadInstrumentInBackground(id, this); 426 Volume(mapping->Volume); 427 } 428 } 429 430 } // namespace LinuxSampler 431