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 * Copyright (C) 2009-2012 Grigor Iliev * 8 * Copyright (C) 2012-2017 Andreas Persson * 9 * * 10 * This program is free software; you can redistribute it and/or modify * 11 * it under the terms of the GNU General Public License as published by * 12 * the Free Software Foundation; either version 2 of the License, or * 13 * (at your option) any later version. * 14 * * 15 * This program is distributed in the hope that it will be useful, * 16 * but WITHOUT ANY WARRANTY; without even the implied warranty of * 17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * 18 * GNU General Public License for more details. * 19 * * 20 * You should have received a copy of the GNU General Public License * 21 * along with this program; if not, write to the Free Software * 22 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, * 23 * MA 02111-1307 USA * 24 ***************************************************************************/ 25 26 #include "AbstractEngineChannel.h" 27 #include "../common/global_private.h" 28 #include "../Sampler.h" 29 30 namespace LinuxSampler { 31 AbstractEngineChannel()32 AbstractEngineChannel::AbstractEngineChannel() : 33 virtualMidiDevicesReader_AudioThread(virtualMidiDevices), 34 virtualMidiDevicesReader_MidiThread(virtualMidiDevices) 35 { 36 pEngine = NULL; 37 pEvents = NULL; // we allocate when we retrieve the right Engine object 38 delayedEvents.pList = NULL; 39 pEventQueue = new RingBuffer<Event,false>(CONFIG_MAX_EVENTS_PER_FRAGMENT, 0); 40 InstrumentIdx = -1; 41 InstrumentStat = -1; 42 pChannelLeft = NULL; 43 pChannelRight = NULL; 44 AudioDeviceChannelLeft = -1; 45 AudioDeviceChannelRight = -1; 46 midiChannel = midi_chan_all; 47 ResetControllers(); 48 PortamentoMode = false; 49 PortamentoTime = CONFIG_PORTAMENTO_TIME_DEFAULT; 50 pScript = NULL; 51 } 52 ~AbstractEngineChannel()53 AbstractEngineChannel::~AbstractEngineChannel() { 54 delete pEventQueue; 55 DeleteGroupEventLists(); 56 RemoveAllFxSends(); 57 } 58 GetEngine()59 Engine* AbstractEngineChannel::GetEngine() { 60 return pEngine; 61 } 62 Channels()63 uint AbstractEngineChannel::Channels() { 64 return 2; 65 } 66 67 /** 68 * More or less a workaround to set the instrument name, index and load 69 * status variable to zero percent immediately, that is without blocking 70 * the calling thread. It might be used in future for other preparations 71 * as well though. 72 * 73 * @param FileName - file name of the instrument file 74 * @param Instrument - index of the instrument in the file 75 * @see LoadInstrument() 76 */ PrepareLoadInstrument(const char * FileName,uint Instrument)77 void AbstractEngineChannel::PrepareLoadInstrument(const char* FileName, uint Instrument) { 78 InstrumentFile = FileName; 79 InstrumentIdx = Instrument; 80 InstrumentStat = 0; 81 } 82 InstrumentFileName()83 String AbstractEngineChannel::InstrumentFileName() { 84 return InstrumentFile; 85 } 86 InstrumentName()87 String AbstractEngineChannel::InstrumentName() { 88 return InstrumentIdxName; 89 } 90 InstrumentIndex()91 int AbstractEngineChannel::InstrumentIndex() { 92 return InstrumentIdx; 93 } 94 InstrumentStatus()95 int AbstractEngineChannel::InstrumentStatus() { 96 return InstrumentStat; 97 } 98 EngineName()99 String AbstractEngineChannel::EngineName() { 100 return AbstractEngine::GetFormatString(GetEngineFormat()); 101 } 102 Reset()103 void AbstractEngineChannel::Reset() { 104 if (pEngine) pEngine->DisableAndLock(); 105 ResetInternal(false/*don't reset engine*/); 106 ResetControllers(); 107 if (pEngine) { 108 pEngine->Enable(); 109 pEngine->Reset(); 110 } 111 } 112 ResetControllers()113 void AbstractEngineChannel::ResetControllers() { 114 Pitch = 0; 115 GlobalVolume = 1.0f; 116 MidiVolume = 1.0; 117 iLastPanRequest = 64; 118 GlobalTranspose = 0; 119 // set all MIDI controller values to zero 120 memset(ControllerTable, 0x00, 129); 121 // reset all FX Send levels 122 for ( 123 std::vector<FxSend*>::iterator iter = fxSends.begin(); 124 iter != fxSends.end(); iter++ 125 ) { 126 (*iter)->Reset(); 127 } 128 } 129 130 /** 131 * This method is not thread safe! 132 */ ResetInternal(bool bResetEngine)133 void AbstractEngineChannel::ResetInternal(bool bResetEngine) { 134 CurrentKeyDimension = 0; 135 PortamentoPos = -1.0f; // no portamento active yet 136 137 // delete all active instrument script events 138 if (pScript) pScript->resetEvents(); 139 140 // free all delayed MIDI events 141 delayedEvents.clear(); 142 143 // delete all input events 144 pEventQueue->init(); 145 146 if (bResetEngine && pEngine) pEngine->ResetInternal(); 147 148 // status of engine channel has changed, so set notify flag 149 bStatusChanged = true; 150 } 151 152 /** 153 * Implementation of virtual method from abstract EngineChannel interface. 154 * This method will periodically be polled (e.g. by the LSCP server) to 155 * check if some engine channel parameter has changed since the last 156 * StatusChanged() call. 157 * 158 * This method can also be used to mark the engine channel as changed 159 * from outside, e.g. by a MIDI input device. The optional argument 160 * \a nNewStatus can be used for this. 161 * 162 * TODO: This "poll method" is just a lazy solution and might be 163 * replaced in future. 164 * @param bNewStatus - (optional, default: false) sets the new status flag 165 * @returns true if engine channel status has changed since last 166 * StatusChanged() call 167 */ StatusChanged(bool bNewStatus)168 bool AbstractEngineChannel::StatusChanged(bool bNewStatus) { 169 bool b = bStatusChanged; 170 bStatusChanged = bNewStatus; 171 return b; 172 } 173 Volume()174 float AbstractEngineChannel::Volume() { 175 return GlobalVolume; 176 } 177 Volume(float f)178 void AbstractEngineChannel::Volume(float f) { 179 GlobalVolume = f; 180 bStatusChanged = true; // status of engine channel has changed, so set notify flag 181 } 182 Pan()183 float AbstractEngineChannel::Pan() { 184 return float(iLastPanRequest - 64) / 64.0f; 185 } 186 Pan(float f)187 void AbstractEngineChannel::Pan(float f) { 188 int iMidiPan = int(f * 64.0f) + 64; 189 if (iMidiPan > 127) iMidiPan = 127; 190 else if (iMidiPan < 0) iMidiPan = 0; 191 iLastPanRequest = iMidiPan; 192 } 193 GetAudioOutputDevice()194 AudioOutputDevice* AbstractEngineChannel::GetAudioOutputDevice() { 195 return (pEngine) ? pEngine->pAudioOutputDevice : NULL; 196 } 197 198 /** 199 * Gets thread safe access to the currently connected audio output 200 * device from other threads than the lscp thread. 201 */ GetAudioOutputDeviceSafe()202 AudioOutputDevice* AbstractEngineChannel::GetAudioOutputDeviceSafe() { 203 LockGuard lock(EngineMutex); 204 return GetAudioOutputDevice(); 205 } 206 SetOutputChannel(uint EngineAudioChannel,uint AudioDeviceChannel)207 void AbstractEngineChannel::SetOutputChannel(uint EngineAudioChannel, uint AudioDeviceChannel) { 208 if (!pEngine || !pEngine->pAudioOutputDevice) throw AudioOutputException("No audio output device connected yet."); 209 210 AudioChannel* pChannel = pEngine->pAudioOutputDevice->Channel(AudioDeviceChannel); 211 if (!pChannel) throw AudioOutputException("Invalid audio output device channel " + ToString(AudioDeviceChannel)); 212 switch (EngineAudioChannel) { 213 case 0: // left output channel 214 if (fxSends.empty()) pChannelLeft = pChannel; 215 AudioDeviceChannelLeft = AudioDeviceChannel; 216 break; 217 case 1: // right output channel 218 if (fxSends.empty()) pChannelRight = pChannel; 219 AudioDeviceChannelRight = AudioDeviceChannel; 220 break; 221 default: 222 throw AudioOutputException("Invalid engine audio channel " + ToString(EngineAudioChannel)); 223 } 224 225 bStatusChanged = true; 226 } 227 OutputChannel(uint EngineAudioChannel)228 int AbstractEngineChannel::OutputChannel(uint EngineAudioChannel) { 229 switch (EngineAudioChannel) { 230 case 0: // left channel 231 return AudioDeviceChannelLeft; 232 case 1: // right channel 233 return AudioDeviceChannelRight; 234 default: 235 throw AudioOutputException("Invalid engine audio channel " + ToString(EngineAudioChannel)); 236 } 237 } 238 Connect(MidiInputPort * pMidiPort)239 void AbstractEngineChannel::Connect(MidiInputPort* pMidiPort) { 240 if (!pMidiPort) return; 241 242 Sync< ArrayList<MidiInputPort*> > connections = midiInputs.back(); 243 244 // check if connection already exists 245 for (int i = 0; i < connections->size(); ++i) 246 if ((*connections)[i] == pMidiPort) 247 return; // to avoid endless recursion 248 249 connections->add(pMidiPort); 250 251 // inform MIDI port about this new connection 252 pMidiPort->Connect(this, MidiChannel()); 253 } 254 Disconnect(MidiInputPort * pMidiPort)255 void AbstractEngineChannel::Disconnect(MidiInputPort* pMidiPort) { 256 if (!pMidiPort) return; 257 258 Sync< ArrayList<MidiInputPort*> > connections = midiInputs.back(); 259 260 for (int i = 0; i < connections->size(); ++i) { 261 if ((*connections)[i] == pMidiPort) { 262 connections->remove(i); 263 // inform MIDI port about this disconnection 264 pMidiPort->Disconnect(this); 265 return; 266 } 267 } 268 } 269 DisconnectAllMidiInputPorts()270 void AbstractEngineChannel::DisconnectAllMidiInputPorts() { 271 Sync< ArrayList<MidiInputPort*> > connections = midiInputs.back(); 272 ArrayList<MidiInputPort*> clonedList = *connections; 273 connections->clear(); 274 for (int i = 0; i < clonedList.size(); ++i) clonedList[i]->Disconnect(this); 275 } 276 GetMidiInputPortCount()277 uint AbstractEngineChannel::GetMidiInputPortCount() { 278 Sync< ArrayList<MidiInputPort*> > connections = midiInputs.back(); 279 return (uint) connections->size(); 280 } 281 GetMidiInputPort(uint index)282 MidiInputPort* AbstractEngineChannel::GetMidiInputPort(uint index) { 283 Sync< ArrayList<MidiInputPort*> > connections = midiInputs.back(); 284 return (index < connections->size()) ? (*connections)[index] : NULL; 285 } 286 287 // deprecated (just for API backward compatibility) - may be removed in future Connect(MidiInputPort * pMidiPort,midi_chan_t MidiChannel)288 void AbstractEngineChannel::Connect(MidiInputPort* pMidiPort, midi_chan_t MidiChannel) { 289 if (!pMidiPort) return; 290 291 Sync< ArrayList<MidiInputPort*> > connections = midiInputs.back(); 292 293 // check against endless recursion 294 if (connections->size() == 1 && (*connections)[0] == pMidiPort && this->midiChannel == MidiChannel) 295 return; 296 297 if (!isValidMidiChan(MidiChannel)) 298 throw MidiInputException("Invalid MIDI channel (" + ToString(int(MidiChannel)) + ")"); 299 300 this->midiChannel = MidiChannel; 301 302 // disconnect all currently connected MIDI ports 303 ArrayList<MidiInputPort*> clonedList = *connections; 304 connections->clear(); 305 for (int i = 0; i < clonedList.size(); ++i) 306 clonedList[i]->Disconnect(this); 307 308 // connect the new port 309 connections->add(pMidiPort); 310 pMidiPort->Connect(this, MidiChannel); 311 } 312 313 // deprecated (just for API backward compatibility) - may be removed in future DisconnectMidiInputPort()314 void AbstractEngineChannel::DisconnectMidiInputPort() { 315 DisconnectAllMidiInputPorts(); 316 } 317 318 // deprecated (just for API backward compatibility) - may be removed in future GetMidiInputPort()319 MidiInputPort* AbstractEngineChannel::GetMidiInputPort() { 320 return GetMidiInputPort(0); 321 } 322 MidiChannel()323 midi_chan_t AbstractEngineChannel::MidiChannel() { 324 return midiChannel; 325 } 326 SetMidiChannel(midi_chan_t MidiChannel)327 void AbstractEngineChannel::SetMidiChannel(midi_chan_t MidiChannel) { 328 if (this->midiChannel == MidiChannel) return; 329 if (!isValidMidiChan(MidiChannel)) 330 throw MidiInputException("Invalid MIDI channel (" + ToString(int(MidiChannel)) + ")"); 331 332 this->midiChannel = MidiChannel; 333 334 Sync< ArrayList<MidiInputPort*> > connections = midiInputs.back(); 335 ArrayList<MidiInputPort*> clonedList = *connections; 336 337 DisconnectAllMidiInputPorts(); 338 339 for (int i = 0; i < clonedList.size(); ++i) Connect(clonedList[i]); 340 } 341 Connect(VirtualMidiDevice * pDevice)342 void AbstractEngineChannel::Connect(VirtualMidiDevice* pDevice) { 343 // double buffer ... double work ... 344 { 345 ArrayList<VirtualMidiDevice*>& devices = virtualMidiDevices.GetConfigForUpdate(); 346 devices.add(pDevice); 347 } 348 { 349 ArrayList<VirtualMidiDevice*>& devices = virtualMidiDevices.SwitchConfig(); 350 devices.add(pDevice); 351 } 352 } 353 Disconnect(VirtualMidiDevice * pDevice)354 void AbstractEngineChannel::Disconnect(VirtualMidiDevice* pDevice) { 355 // double buffer ... double work ... 356 { 357 ArrayList<VirtualMidiDevice*>& devices = virtualMidiDevices.GetConfigForUpdate(); 358 devices.remove(pDevice); 359 } 360 { 361 ArrayList<VirtualMidiDevice*>& devices = virtualMidiDevices.SwitchConfig(); 362 devices.remove(pDevice); 363 } 364 } 365 366 /** 367 * Will be called by the MIDIIn Thread to let the audio thread trigger a new 368 * voice for the given key. This method is meant for real time rendering, 369 * that is an event will immediately be created with the current system 370 * time as time stamp. 371 * 372 * @param Key - MIDI key number of the triggered key 373 * @param Velocity - MIDI velocity value of the triggered key 374 */ SendNoteOn(uint8_t Key,uint8_t Velocity,uint8_t MidiChannel)375 void AbstractEngineChannel::SendNoteOn(uint8_t Key, uint8_t Velocity, uint8_t MidiChannel) { 376 if (pEngine) { 377 // protection in case there are more than 1 MIDI input threads sending MIDI events to this EngineChannel 378 LockGuard g; 379 if (hasMultipleMIDIInputs()) g = LockGuard(MidiInputMutex); 380 381 Event event = pEngine->pEventGenerator->CreateEvent(); 382 event.Type = Event::type_note_on; 383 event.Param.Note.Key = Key; 384 event.Param.Note.Velocity = Velocity; 385 event.Param.Note.Channel = MidiChannel; 386 event.pEngineChannel = this; 387 if (this->pEventQueue->write_space() > 0) this->pEventQueue->push(&event); 388 else dmsg(1,("EngineChannel: Input event queue full!")); 389 // inform connected virtual MIDI devices if any ... 390 // (e.g. virtual MIDI keyboard in instrument editor(s)) 391 ArrayList<VirtualMidiDevice*>& devices = 392 const_cast<ArrayList<VirtualMidiDevice*>&>( 393 virtualMidiDevicesReader_MidiThread.Lock() 394 ); 395 for (int i = 0; i < devices.size(); i++) { 396 devices[i]->SendNoteOnToDevice(Key, Velocity); 397 } 398 virtualMidiDevicesReader_MidiThread.Unlock(); 399 } 400 } 401 402 /** 403 * Will be called by the MIDIIn Thread to let the audio thread trigger a new 404 * voice for the given key. This method is meant for offline rendering 405 * and / or for cases where the exact position of the event in the current 406 * audio fragment is already known. 407 * 408 * @param Key - MIDI key number of the triggered key 409 * @param Velocity - MIDI velocity value of the triggered key 410 * @param FragmentPos - sample point position in the current audio 411 * fragment to which this event belongs to 412 */ SendNoteOn(uint8_t Key,uint8_t Velocity,uint8_t MidiChannel,int32_t FragmentPos)413 void AbstractEngineChannel::SendNoteOn(uint8_t Key, uint8_t Velocity, uint8_t MidiChannel, int32_t FragmentPos) { 414 if (FragmentPos < 0) { 415 dmsg(1,("EngineChannel::SendNoteOn(): negative FragmentPos! Seems MIDI driver is buggy!")); 416 } 417 else if (pEngine) { 418 // protection in case there are more than 1 MIDI input threads sending MIDI events to this EngineChannel 419 LockGuard g; 420 if (hasMultipleMIDIInputs()) g = LockGuard(MidiInputMutex); 421 422 Event event = pEngine->pEventGenerator->CreateEvent(FragmentPos); 423 event.Type = Event::type_note_on; 424 event.Param.Note.Key = Key; 425 event.Param.Note.Velocity = Velocity; 426 event.Param.Note.Channel = MidiChannel; 427 event.pEngineChannel = this; 428 if (this->pEventQueue->write_space() > 0) this->pEventQueue->push(&event); 429 else dmsg(1,("EngineChannel: Input event queue full!")); 430 // inform connected virtual MIDI devices if any ... 431 // (e.g. virtual MIDI keyboard in instrument editor(s)) 432 ArrayList<VirtualMidiDevice*>& devices = 433 const_cast<ArrayList<VirtualMidiDevice*>&>( 434 virtualMidiDevicesReader_MidiThread.Lock() 435 ); 436 for (int i = 0; i < devices.size(); i++) { 437 devices[i]->SendNoteOnToDevice(Key, Velocity); 438 } 439 virtualMidiDevicesReader_MidiThread.Unlock(); 440 } 441 } 442 443 /** 444 * Will be called by the MIDIIn Thread to signal the audio thread to release 445 * voice(s) on the given key. This method is meant for real time rendering, 446 * that is an event will immediately be created with the current system 447 * time as time stamp. 448 * 449 * @param Key - MIDI key number of the released key 450 * @param Velocity - MIDI release velocity value of the released key 451 */ SendNoteOff(uint8_t Key,uint8_t Velocity,uint8_t MidiChannel)452 void AbstractEngineChannel::SendNoteOff(uint8_t Key, uint8_t Velocity, uint8_t MidiChannel) { 453 if (pEngine) { 454 // protection in case there are more than 1 MIDI input threads sending MIDI events to this EngineChannel 455 LockGuard g; 456 if (hasMultipleMIDIInputs()) g = LockGuard(MidiInputMutex); 457 458 Event event = pEngine->pEventGenerator->CreateEvent(); 459 event.Type = Event::type_note_off; 460 event.Param.Note.Key = Key; 461 event.Param.Note.Velocity = Velocity; 462 event.Param.Note.Channel = MidiChannel; 463 event.pEngineChannel = this; 464 if (this->pEventQueue->write_space() > 0) this->pEventQueue->push(&event); 465 else dmsg(1,("EngineChannel: Input event queue full!")); 466 // inform connected virtual MIDI devices if any ... 467 // (e.g. virtual MIDI keyboard in instrument editor(s)) 468 ArrayList<VirtualMidiDevice*>& devices = 469 const_cast<ArrayList<VirtualMidiDevice*>&>( 470 virtualMidiDevicesReader_MidiThread.Lock() 471 ); 472 for (int i = 0; i < devices.size(); i++) { 473 devices[i]->SendNoteOffToDevice(Key, Velocity); 474 } 475 virtualMidiDevicesReader_MidiThread.Unlock(); 476 } 477 } 478 479 /** 480 * Will be called by the MIDIIn Thread to signal the audio thread to release 481 * voice(s) on the given key. This method is meant for offline rendering 482 * and / or for cases where the exact position of the event in the current 483 * audio fragment is already known. 484 * 485 * @param Key - MIDI key number of the released key 486 * @param Velocity - MIDI release velocity value of the released key 487 * @param FragmentPos - sample point position in the current audio 488 * fragment to which this event belongs to 489 */ SendNoteOff(uint8_t Key,uint8_t Velocity,uint8_t MidiChannel,int32_t FragmentPos)490 void AbstractEngineChannel::SendNoteOff(uint8_t Key, uint8_t Velocity, uint8_t MidiChannel, int32_t FragmentPos) { 491 if (FragmentPos < 0) { 492 dmsg(1,("EngineChannel::SendNoteOff(): negative FragmentPos! Seems MIDI driver is buggy!")); 493 } 494 else if (pEngine) { 495 // protection in case there are more than 1 MIDI input threads sending MIDI events to this EngineChannel 496 LockGuard g; 497 if (hasMultipleMIDIInputs()) g = LockGuard(MidiInputMutex); 498 499 Event event = pEngine->pEventGenerator->CreateEvent(FragmentPos); 500 event.Type = Event::type_note_off; 501 event.Param.Note.Key = Key; 502 event.Param.Note.Velocity = Velocity; 503 event.Param.Note.Channel = MidiChannel; 504 event.pEngineChannel = this; 505 if (this->pEventQueue->write_space() > 0) this->pEventQueue->push(&event); 506 else dmsg(1,("EngineChannel: Input event queue full!")); 507 // inform connected virtual MIDI devices if any ... 508 // (e.g. virtual MIDI keyboard in instrument editor(s)) 509 ArrayList<VirtualMidiDevice*>& devices = 510 const_cast<ArrayList<VirtualMidiDevice*>&>( 511 virtualMidiDevicesReader_MidiThread.Lock() 512 ); 513 for (int i = 0; i < devices.size(); i++) { 514 devices[i]->SendNoteOffToDevice(Key, Velocity); 515 } 516 virtualMidiDevicesReader_MidiThread.Unlock(); 517 } 518 } 519 520 /** 521 * Will be called by the MIDIIn Thread to signal the audio thread to change 522 * the pitch value for all voices. This method is meant for real time 523 * rendering, that is an event will immediately be created with the 524 * current system time as time stamp. 525 * 526 * @param Pitch - MIDI pitch value (-8192 ... +8191) 527 */ SendPitchbend(int Pitch,uint8_t MidiChannel)528 void AbstractEngineChannel::SendPitchbend(int Pitch, uint8_t MidiChannel) { 529 if (pEngine) { 530 // protection in case there are more than 1 MIDI input threads sending MIDI events to this EngineChannel 531 LockGuard g; 532 if (hasMultipleMIDIInputs()) g = LockGuard(MidiInputMutex); 533 534 Event event = pEngine->pEventGenerator->CreateEvent(); 535 event.Type = Event::type_pitchbend; 536 event.Param.Pitch.Pitch = Pitch; 537 event.Param.Pitch.Channel = MidiChannel; 538 event.pEngineChannel = this; 539 if (this->pEventQueue->write_space() > 0) this->pEventQueue->push(&event); 540 else dmsg(1,("EngineChannel: Input event queue full!")); 541 } 542 } 543 544 /** 545 * Will be called by the MIDIIn Thread to signal the audio thread to change 546 * the pitch value for all voices. This method is meant for offline 547 * rendering and / or for cases where the exact position of the event in 548 * the current audio fragment is already known. 549 * 550 * @param Pitch - MIDI pitch value (-8192 ... +8191) 551 * @param FragmentPos - sample point position in the current audio 552 * fragment to which this event belongs to 553 */ SendPitchbend(int Pitch,uint8_t MidiChannel,int32_t FragmentPos)554 void AbstractEngineChannel::SendPitchbend(int Pitch, uint8_t MidiChannel, int32_t FragmentPos) { 555 if (FragmentPos < 0) { 556 dmsg(1,("AbstractEngineChannel::SendPitchBend(): negative FragmentPos! Seems MIDI driver is buggy!")); 557 } 558 else if (pEngine) { 559 // protection in case there are more than 1 MIDI input threads sending MIDI events to this EngineChannel 560 LockGuard g; 561 if (hasMultipleMIDIInputs()) g = LockGuard(MidiInputMutex); 562 563 Event event = pEngine->pEventGenerator->CreateEvent(FragmentPos); 564 event.Type = Event::type_pitchbend; 565 event.Param.Pitch.Pitch = Pitch; 566 event.Param.Pitch.Channel = MidiChannel; 567 event.pEngineChannel = this; 568 if (this->pEventQueue->write_space() > 0) this->pEventQueue->push(&event); 569 else dmsg(1,("AbstractEngineChannel: Input event queue full!")); 570 } 571 } 572 573 /** 574 * Will be called by the MIDIIn Thread to signal the audio thread that a 575 * continuous controller value has changed. This method is meant for real 576 * time rendering, that is an event will immediately be created with the 577 * current system time as time stamp. 578 * 579 * @param Controller - MIDI controller number of the occured control change 580 * @param Value - value of the control change 581 */ SendControlChange(uint8_t Controller,uint8_t Value,uint8_t MidiChannel)582 void AbstractEngineChannel::SendControlChange(uint8_t Controller, uint8_t Value, uint8_t MidiChannel) { 583 if (pEngine) { 584 // protection in case there are more than 1 MIDI input threads sending MIDI events to this EngineChannel 585 LockGuard g; 586 if (hasMultipleMIDIInputs()) g = LockGuard(MidiInputMutex); 587 588 Event event = pEngine->pEventGenerator->CreateEvent(); 589 event.Type = Event::type_control_change; 590 event.Param.CC.Controller = Controller; 591 event.Param.CC.Value = Value; 592 event.Param.CC.Channel = MidiChannel; 593 event.pEngineChannel = this; 594 if (this->pEventQueue->write_space() > 0) this->pEventQueue->push(&event); 595 else dmsg(1,("AbstractEngineChannel: Input event queue full!")); 596 } 597 } 598 599 /** 600 * Will be called by the MIDIIn Thread to signal the audio thread that a 601 * continuous controller value has changed. This method is meant for 602 * offline rendering and / or for cases where the exact position of the 603 * event in the current audio fragment is already known. 604 * 605 * @param Controller - MIDI controller number of the occured control change 606 * @param Value - value of the control change 607 * @param FragmentPos - sample point position in the current audio 608 * fragment to which this event belongs to 609 */ SendControlChange(uint8_t Controller,uint8_t Value,uint8_t MidiChannel,int32_t FragmentPos)610 void AbstractEngineChannel::SendControlChange(uint8_t Controller, uint8_t Value, uint8_t MidiChannel, int32_t FragmentPos) { 611 if (FragmentPos < 0) { 612 dmsg(1,("AbstractEngineChannel::SendControlChange(): negative FragmentPos! Seems MIDI driver is buggy!")); 613 } 614 else if (pEngine) { 615 // protection in case there are more than 1 MIDI input threads sending MIDI events to this EngineChannel 616 LockGuard g; 617 if (hasMultipleMIDIInputs()) g = LockGuard(MidiInputMutex); 618 619 Event event = pEngine->pEventGenerator->CreateEvent(FragmentPos); 620 event.Type = Event::type_control_change; 621 event.Param.CC.Controller = Controller; 622 event.Param.CC.Value = Value; 623 event.Param.CC.Channel = MidiChannel; 624 event.pEngineChannel = this; 625 if (this->pEventQueue->write_space() > 0) this->pEventQueue->push(&event); 626 else dmsg(1,("AbstractEngineChannel: Input event queue full!")); 627 } 628 } 629 SendChannelPressure(uint8_t Value,uint8_t MidiChannel)630 void AbstractEngineChannel::SendChannelPressure(uint8_t Value, uint8_t MidiChannel) { 631 if (pEngine) { 632 // protection in case there are more than 1 MIDI input threads sending MIDI events to this EngineChannel 633 LockGuard g; 634 if (hasMultipleMIDIInputs()) g = LockGuard(MidiInputMutex); 635 636 Event event = pEngine->pEventGenerator->CreateEvent(); 637 event.Type = Event::type_channel_pressure; 638 event.Param.ChannelPressure.Controller = CTRL_TABLE_IDX_AFTERTOUCH; // required for instrument scripts 639 event.Param.ChannelPressure.Value = Value; 640 event.Param.ChannelPressure.Channel = MidiChannel; 641 event.pEngineChannel = this; 642 if (this->pEventQueue->write_space() > 0) this->pEventQueue->push(&event); 643 else dmsg(1,("AbstractEngineChannel: Input event queue full!")); 644 } 645 } 646 SendChannelPressure(uint8_t Value,uint8_t MidiChannel,int32_t FragmentPos)647 void AbstractEngineChannel::SendChannelPressure(uint8_t Value, uint8_t MidiChannel, int32_t FragmentPos) { 648 if (pEngine) { 649 // protection in case there are more than 1 MIDI input threads sending MIDI events to this EngineChannel 650 LockGuard g; 651 if (hasMultipleMIDIInputs()) g = LockGuard(MidiInputMutex); 652 653 Event event = pEngine->pEventGenerator->CreateEvent(FragmentPos); 654 event.Type = Event::type_channel_pressure; 655 event.Param.ChannelPressure.Controller = CTRL_TABLE_IDX_AFTERTOUCH; // required for instrument scripts 656 event.Param.ChannelPressure.Value = Value; 657 event.Param.ChannelPressure.Channel = MidiChannel; 658 event.pEngineChannel = this; 659 if (this->pEventQueue->write_space() > 0) this->pEventQueue->push(&event); 660 else dmsg(1,("AbstractEngineChannel: Input event queue full!")); 661 } 662 } 663 SendPolyphonicKeyPressure(uint8_t Key,uint8_t Value,uint8_t MidiChannel)664 void AbstractEngineChannel::SendPolyphonicKeyPressure(uint8_t Key, uint8_t Value, uint8_t MidiChannel) { 665 if (pEngine) { 666 // protection in case there are more than 1 MIDI input threads sending MIDI events to this EngineChannel 667 LockGuard g; 668 if (hasMultipleMIDIInputs()) g = LockGuard(MidiInputMutex); 669 670 Event event = pEngine->pEventGenerator->CreateEvent(); 671 event.Type = Event::type_note_pressure; 672 event.Param.NotePressure.Key = Key; 673 event.Param.NotePressure.Value = Value; 674 event.Param.NotePressure.Channel = MidiChannel; 675 event.pEngineChannel = this; 676 if (this->pEventQueue->write_space() > 0) this->pEventQueue->push(&event); 677 else dmsg(1,("AbstractEngineChannel: Input event queue full!")); 678 } 679 } 680 SendPolyphonicKeyPressure(uint8_t Key,uint8_t Value,uint8_t MidiChannel,int32_t FragmentPos)681 void AbstractEngineChannel::SendPolyphonicKeyPressure(uint8_t Key, uint8_t Value, uint8_t MidiChannel, int32_t FragmentPos) { 682 if (pEngine) { 683 // protection in case there are more than 1 MIDI input threads sending MIDI events to this EngineChannel 684 LockGuard g; 685 if (hasMultipleMIDIInputs()) g = LockGuard(MidiInputMutex); 686 687 Event event = pEngine->pEventGenerator->CreateEvent(FragmentPos); 688 event.Type = Event::type_note_pressure; 689 event.Param.NotePressure.Key = Key; 690 event.Param.NotePressure.Value = Value; 691 event.Param.NotePressure.Channel = MidiChannel; 692 event.pEngineChannel = this; 693 if (this->pEventQueue->write_space() > 0) this->pEventQueue->push(&event); 694 else dmsg(1,("AbstractEngineChannel: Input event queue full!")); 695 } 696 } 697 applyTranspose(Event * event)698 bool AbstractEngineChannel::applyTranspose(Event* event) { 699 if (event->Type != Event::type_note_on && event->Type != Event::type_note_off) 700 return true; // event OK (not a note event, nothing to do with it here) 701 702 //HACK: we should better add the transpose value only to the most mandatory places (like for retrieving the region and calculating the tuning), because otherwise voices will unintendedly survive when changing transpose while playing 703 const int k = event->Param.Note.Key + GlobalTranspose; 704 if (k < 0 || k > 127) 705 return false; // bad event, drop it 706 707 event->Param.Note.Key = k; 708 709 return true; // event OK 710 } 711 712 /** 713 * Copy all events from the engine channel's input event queue buffer to 714 * the internal event list. This will be done at the beginning of each 715 * audio cycle (that is each RenderAudio() call) to distinguish all 716 * events which have to be processed in the current audio cycle. Each 717 * EngineChannel has it's own input event queue for the common channel 718 * specific events (like NoteOn, NoteOff and ControlChange events). 719 * Beside that, the engine also has a input event queue for global 720 * events (usually SysEx messages). 721 * 722 * @param Samples - number of sample points to be processed in the 723 * current audio cycle 724 */ ImportEvents(uint Samples)725 void AbstractEngineChannel::ImportEvents(uint Samples) { 726 // import events from pure software MIDI "devices" 727 // (e.g. virtual keyboard in instrument editor) 728 { 729 const uint8_t channel = MidiChannel() == midi_chan_all ? 0 : MidiChannel(); 730 const int FragmentPos = 0; // randomly chosen, we don't care about jitter for virtual MIDI devices 731 const Event event = pEngine->pEventGenerator->CreateEvent(FragmentPos); 732 VirtualMidiDevice::event_t devEvent; // the event format we get from the virtual MIDI device 733 // as we're going to (carefully) write some status to the 734 // synchronized struct, we cast away the const 735 ArrayList<VirtualMidiDevice*>& devices = 736 const_cast<ArrayList<VirtualMidiDevice*>&>(virtualMidiDevicesReader_AudioThread.Lock()); 737 // iterate through all virtual MIDI devices 738 for (int i = 0; i < devices.size(); i++) { 739 VirtualMidiDevice* pDev = devices[i]; 740 // I think we can simply flush the whole FIFO(s), the user shouldn't be so fast ;-) 741 while (pDev->GetMidiEventFromDevice(devEvent)) { 742 if (pEvents->poolIsEmpty()) { 743 dmsg(1,("Event pool emtpy!\n")); 744 goto exitVirtualDevicesLoop; 745 } 746 747 // copy event to internal event list (this is already 748 // required here, because the LaunchNewNote() call below 749 // requires the event to be from the internal event pool for 750 // being able to generate a valid event ID) 751 RTList<Event>::Iterator itEvent = pEvents->allocAppend(); 752 *itEvent = event; 753 754 itEvent->pEngineChannel = this; 755 756 switch (devEvent.Type) { 757 case VirtualMidiDevice::EVENT_TYPE_NOTEON: 758 itEvent->Type = Event::type_note_on; 759 itEvent->Param.Note.Key = devEvent.Arg1; 760 itEvent->Param.Note.Velocity = devEvent.Arg2; 761 itEvent->Param.Note.Channel = channel; 762 // apply transpose setting to (note on/off) event 763 if (!applyTranspose(&*itEvent)) { 764 // note value is out of range, so drop this event 765 pEvents->free(itEvent); 766 continue; 767 } 768 // assign a new note to this note-on event 769 if (!pEngine->LaunchNewNote(this, itEvent)) { 770 // failed launching new note, so drop this event 771 pEvents->free(itEvent); 772 continue; 773 } 774 break; 775 case VirtualMidiDevice::EVENT_TYPE_NOTEOFF: 776 itEvent->Type = Event::type_note_off; 777 itEvent->Param.Note.Key = devEvent.Arg1; 778 itEvent->Param.Note.Velocity = devEvent.Arg2; 779 itEvent->Param.Note.Channel = channel; 780 if (!applyTranspose(&*itEvent)) { 781 // note value is out of range, so drop this event 782 pEvents->free(itEvent); 783 continue; 784 } 785 break; 786 case VirtualMidiDevice::EVENT_TYPE_CC: 787 switch (devEvent.Arg1) { 788 case 0: // bank select MSB ... 789 SetMidiBankMsb(devEvent.Arg2); 790 // don't push this event into FIFO 791 pEvents->free(itEvent); 792 continue; 793 case 32: // bank select LSB ... 794 SetMidiBankLsb(devEvent.Arg2); 795 // don't push this event into FIFO 796 pEvents->free(itEvent); 797 continue; 798 default: // regular MIDI CC ... 799 itEvent->Type = Event::type_control_change; 800 itEvent->Param.CC.Controller = devEvent.Arg1; 801 itEvent->Param.CC.Value = devEvent.Arg2; 802 itEvent->Param.CC.Channel = channel; 803 } 804 break; 805 case VirtualMidiDevice::EVENT_TYPE_PITCHBEND: 806 itEvent->Type = Event::type_pitchbend; 807 itEvent->Param.Pitch.Pitch = int(devEvent.Arg2 << 7 | devEvent.Arg1) - 8192; 808 itEvent->Param.Pitch.Channel = channel; 809 break; 810 case VirtualMidiDevice::EVENT_TYPE_PROGRAM: 811 SendProgramChange(devEvent.Arg1); 812 // don't push this event into FIFO 813 pEvents->free(itEvent); 814 continue; 815 case VirtualMidiDevice::EVENT_TYPE_CHPRESSURE: 816 itEvent->Type = Event::type_channel_pressure; 817 itEvent->Param.ChannelPressure.Controller = CTRL_TABLE_IDX_AFTERTOUCH; 818 itEvent->Param.ChannelPressure.Value = devEvent.Arg2; 819 itEvent->Param.ChannelPressure.Channel = channel; 820 break; 821 default: 822 std::cerr << "AbstractEngineChannel::ImportEvents() ERROR: unknown event type (" 823 << devEvent.Type << "). This is a bug!"; 824 pEvents->free(itEvent); // drop event 825 continue; 826 } 827 } 828 } 829 } 830 exitVirtualDevicesLoop: 831 virtualMidiDevicesReader_AudioThread.Unlock(); 832 833 // import events from the regular MIDI devices 834 RingBuffer<Event,false>::NonVolatileReader eventQueueReader = pEventQueue->get_non_volatile_reader(); 835 Event* pEvent; 836 while (true) { 837 // get next event from input event queue 838 if (!(pEvent = eventQueueReader.pop())) break; 839 // if younger event reached, ignore that and all subsequent ones for now 840 if (pEvent->FragmentPos() >= Samples) { 841 eventQueueReader--; 842 dmsg(2,("Younger Event, pos=%d ,Samples=%d!\n",pEvent->FragmentPos(),Samples)); 843 pEvent->ResetFragmentPos(); 844 break; 845 } 846 if (pEvents->poolIsEmpty()) { 847 dmsg(1,("Event pool emtpy!\n")); 848 break; 849 } 850 851 // copy event to internal event list 852 // (required already because LaunchNewNote() relies on it, see 853 // comment about it above) 854 RTList<Event>::Iterator itEvent = pEvents->allocAppend(); 855 *itEvent = *pEvent; 856 857 // apply transpose setting to (note on/off) event 858 if (!applyTranspose(&*itEvent)) { 859 // it's a note event which has a note value out of range, so drop this event 860 pEvents->free(itEvent); 861 continue; 862 } 863 // assign a new note to this event (if its a note-on event) 864 if (itEvent->Type == Event::type_note_on) { 865 if (!pEngine->LaunchNewNote(this, itEvent)) { 866 // failed launching new note, so drop this event 867 pEvents->free(itEvent); 868 continue; 869 } 870 } 871 872 } 873 eventQueueReader.free(); // free all copied events from input queue 874 } 875 876 /** 877 * Called by real-time instrument script functions to schedule a new event 878 * @a delay microseconds in future. 879 * 880 * @b IMPORTANT: for the supplied @a delay to be scheduled correctly, the 881 * passed @a pEvent must be assigned a valid fragment time within the 882 * current audio fragment boundaries. That fragment time will be used by 883 * this method as basis for interpreting what "now" acutally is, and thus 884 * it will be used as basis for calculating the precise scheduling time 885 * for @a delay. The easiest way to achieve this is by copying a recent 886 * event which happened within the current audio fragment cycle: i.e. the 887 * original event which caused calling this method here. 888 * 889 * @param pEvent - event to be scheduled in future (event data will be copied) 890 * @param delay - amount of microseconds in future (from now) when event shall be processed 891 * @returns unique event ID of scheduled new event, or NULL on error 892 */ ScheduleEventMicroSec(const Event * pEvent,int64_t delay)893 event_id_t AbstractEngineChannel::ScheduleEventMicroSec(const Event* pEvent, int64_t delay) { 894 dmsg(3,("AbstractEngineChannel::ScheduleEventMicroSec(Event.Type=%d,delay=%" PRId64 ")\n", pEvent->Type, delay)); 895 RTList<Event>::Iterator itEvent = pEvents->allocAppend(); 896 if (!itEvent) { 897 dmsg(1,("AbstractEngineChannel::ScheduleEventMicroSec(): Event pool emtpy!\n")); 898 return 0; 899 } 900 RTList<ScheduledEvent>::Iterator itNode = delayedEvents.schedulerNodes.allocAppend(); 901 if (!itNode) { // scheduler node pool empty ... 902 dmsg(1,("AbstractEngineChannel::ScheduleEventMicroSec(): ScheduledEvent pool empty!\n")); 903 pEvents->free(itEvent); 904 return 0; 905 } 906 // copy passed event 907 *itEvent = *pEvent; 908 // move copied event to list of delayed events 909 itEvent = itEvent.moveToEndOf(delayedEvents.pList); 910 // connect scheduler node with the copied event 911 itNode->itEvent = itEvent; 912 // add entry to time sorted scheduler queue for copied event 913 pEngine->pEventGenerator->scheduleAheadMicroSec( 914 delayedEvents.queue, *itNode, itEvent->FragmentPos(), delay 915 ); 916 //dmsg(5,("ScheduledEvent queue size: %d\n", delayedEvents.queue.size())); 917 return pEvents->getID(itEvent); 918 } 919 920 /** 921 * Called by real-time instrument script functions to ignore the event 922 * reflected by given event ID. The event will be freed immediately to its 923 * pool and cannot be dereferenced by its old ID anymore. Even if its 924 * allocated back from the Pool later on, it will have a different ID. 925 * 926 * @param id - unique ID of event to be dropped 927 */ IgnoreEvent(event_id_t id)928 void AbstractEngineChannel::IgnoreEvent(event_id_t id) { 929 RTList<Event>::Iterator it = pEvents->fromID(id); 930 if (it) pEvents->free(it); 931 } 932 933 /** @brief Drop the requested event. 934 * 935 * Called by real-time instrument script functions to ignore the event 936 * reflected by the given event @a id. This method detects whether the 937 * passed ID is actually a @c Note ID or a regular @c Event ID and act 938 * accordingly. 939 * 940 * @param id - event id (from script scope) 941 * @see ScriptID 942 */ IgnoreEventByScriptID(const ScriptID & id)943 void AbstractEngineChannel::IgnoreEventByScriptID(const ScriptID& id) { 944 switch (id.type()) { 945 case ScriptID::EVENT: 946 IgnoreEvent( id.eventID() ); 947 break; 948 case ScriptID::NOTE: 949 IgnoreNote( id.noteID() ); 950 break; 951 } 952 } 953 954 /** @brief Order resuming of script execution instance "now". 955 * 956 * Called by real-time instrument script function stop_wait() to resume a 957 * script callback currently being suspended (i.e. due to a wait() script 958 * function call). 959 * 960 * @param itCallback - suspended script callback to be resumed 961 * @param now - current scheduler time to be "now" 962 * @param forever - whether this particulare script callback should ignore 963 * all subsequent wait*() script function calls 964 */ ScheduleResumeOfScriptCallback(RTList<ScriptEvent>::Iterator & itCallback,sched_time_t now,bool forever)965 void AbstractEngineChannel::ScheduleResumeOfScriptCallback(RTList<ScriptEvent>::Iterator& itCallback, sched_time_t now, bool forever) { 966 // ignore if invalid iterator was passed 967 if (!itCallback) return; 968 969 ScriptEvent* pCallback = &*itCallback; 970 971 // mark this callback to ignore all subsequent built-in wait*() script function calls 972 if (forever) pCallback->ignoreAllWaitCalls = true; 973 974 // ignore if callback is not in the scheduler queue 975 if (pCallback->currentSchedulerQueue() != &pScript->suspendedEvents) return; 976 977 // ignore if callback is already scheduled to be resumed "now" 978 if (pCallback->scheduleTime <= now) return; 979 980 // take it out from the scheduler queue and re-insert callback 981 // to schedule the script callback for resuming execution "now" 982 pScript->suspendedEvents.erase(*pCallback); 983 pCallback->scheduleTime = now + 1; 984 pScript->suspendedEvents.insert(*pCallback); 985 } 986 987 /** @brief Fork the given script execution instance. 988 * 989 * Called by real-time instrument script function fork() to create a new 990 * script execution instance (child) of the script execution instance 991 * (parent) that was calling fork(). This is essentially like creating a 992 * new thread for a script handler being executing. The entire execution 993 * state of parent is copied to the "forked" child. 994 * 995 * @param parent - original active script callback instance from which the 996 * new child shall be forked from 997 * @param bAutoAbort - whether the forked child shall automatically be 998 * terminated as soon as parent terminates 999 * @returns forked new child execution instance 1000 */ forkScriptCallback(ScriptEvent * parent,bool bAutoAbort)1001 RTList<ScriptEvent>::Iterator AbstractEngineChannel::forkScriptCallback(ScriptEvent* parent, bool bAutoAbort) { 1002 // check if the max. amount of child forks for this parent event handler 1003 // instance have not been exceeded yet 1004 if (parent->countChildHandlers() >= MAX_FORK_PER_SCRIPT_HANDLER) 1005 return RTList<ScriptEvent>::Iterator(); 1006 1007 // allocate a new script callback instance for child to be forked 1008 RTList<ScriptEvent>::Iterator itChild = pScript->pEvents->allocAppend(); 1009 if (!itChild) return itChild; 1010 1011 // copy entire script handler state from parent to forked child 1012 parent->forkTo(&*itChild, bAutoAbort); 1013 1014 // stick the parent ID and child ID respectively to each other 1015 itChild->parentHandlerID = GetScriptCallbackID(parent); 1016 parent->addChildHandlerID( GetScriptCallbackID(&*itChild) ); 1017 1018 // insert newly created (forked) child event handler instance to the 1019 // scheduler queue for being executed soon 1020 pEngine->pEventGenerator->scheduleAheadMicroSec( 1021 pScript->suspendedEvents, // scheduler queue 1022 *itChild, // script event 1023 parent->cause.FragmentPos(), // current time of script event (basis for its next execution) 1024 0 // "resume" new child script callback instance ASAP 1025 ); 1026 1027 return itChild; 1028 } 1029 AddFxSend(uint8_t MidiCtrl,String Name)1030 FxSend* AbstractEngineChannel::AddFxSend(uint8_t MidiCtrl, String Name) throw (Exception) { 1031 if (pEngine) pEngine->DisableAndLock(); 1032 FxSend* pFxSend = new FxSend(this, MidiCtrl, Name); 1033 if (fxSends.empty()) { 1034 if (pEngine && pEngine->pAudioOutputDevice) { 1035 AudioOutputDevice* pDevice = pEngine->pAudioOutputDevice; 1036 // create local render buffers 1037 pChannelLeft = new AudioChannel(0, pDevice->MaxSamplesPerCycle()); 1038 pChannelRight = new AudioChannel(1, pDevice->MaxSamplesPerCycle()); 1039 } else { 1040 // postpone local render buffer creation until audio device is assigned 1041 pChannelLeft = NULL; 1042 pChannelRight = NULL; 1043 } 1044 } 1045 fxSends.push_back(pFxSend); 1046 if (pEngine) pEngine->Enable(); 1047 fireFxSendCountChanged(GetSamplerChannel()->Index(), GetFxSendCount()); 1048 1049 return pFxSend; 1050 } 1051 GetFxSend(uint FxSendIndex)1052 FxSend* AbstractEngineChannel::GetFxSend(uint FxSendIndex) { 1053 return (FxSendIndex < fxSends.size()) ? fxSends[FxSendIndex] : NULL; 1054 } 1055 GetFxSendCount()1056 uint AbstractEngineChannel::GetFxSendCount() { 1057 return (uint)fxSends.size(); 1058 } 1059 RemoveFxSend(FxSend * pFxSend)1060 void AbstractEngineChannel::RemoveFxSend(FxSend* pFxSend) { 1061 if (pEngine) pEngine->DisableAndLock(); 1062 for ( 1063 std::vector<FxSend*>::iterator iter = fxSends.begin(); 1064 iter != fxSends.end(); iter++ 1065 ) { 1066 if (*iter == pFxSend) { 1067 delete pFxSend; 1068 fxSends.erase(iter); 1069 if (fxSends.empty()) { 1070 // destroy local render buffers 1071 if (pChannelLeft) delete pChannelLeft; 1072 if (pChannelRight) delete pChannelRight; 1073 // fallback to render directly into AudioOutputDevice's buffers 1074 if (pEngine && pEngine->pAudioOutputDevice) { 1075 pChannelLeft = pEngine->pAudioOutputDevice->Channel(AudioDeviceChannelLeft); 1076 pChannelRight = pEngine->pAudioOutputDevice->Channel(AudioDeviceChannelRight); 1077 } else { // we update the pointers later 1078 pChannelLeft = NULL; 1079 pChannelRight = NULL; 1080 } 1081 } 1082 break; 1083 } 1084 } 1085 if (pEngine) pEngine->Enable(); 1086 fireFxSendCountChanged(GetSamplerChannel()->Index(), GetFxSendCount()); 1087 } 1088 RemoveAllFxSends()1089 void AbstractEngineChannel::RemoveAllFxSends() { 1090 if (pEngine) pEngine->DisableAndLock(); 1091 if (!fxSends.empty()) { // free local render buffers 1092 if (pChannelLeft) { 1093 delete pChannelLeft; 1094 if (pEngine && pEngine->pAudioOutputDevice) { 1095 // fallback to render directly to the AudioOutputDevice's buffer 1096 pChannelLeft = pEngine->pAudioOutputDevice->Channel(AudioDeviceChannelLeft); 1097 } else pChannelLeft = NULL; 1098 } 1099 if (pChannelRight) { 1100 delete pChannelRight; 1101 if (pEngine && pEngine->pAudioOutputDevice) { 1102 // fallback to render directly to the AudioOutputDevice's buffer 1103 pChannelRight = pEngine->pAudioOutputDevice->Channel(AudioDeviceChannelRight); 1104 } else pChannelRight = NULL; 1105 } 1106 } 1107 for (int i = 0; i < fxSends.size(); i++) delete fxSends[i]; 1108 fxSends.clear(); 1109 if (pEngine) pEngine->Enable(); 1110 } 1111 1112 /** 1113 * Add a group number to the set of key groups. Should be called 1114 * when an instrument is loaded to make sure there are event lists 1115 * for all key groups. 1116 */ AddGroup(uint group)1117 void AbstractEngineChannel::AddGroup(uint group) { 1118 if (group) { 1119 std::pair<ActiveKeyGroupMap::iterator, bool> p = 1120 ActiveKeyGroups.insert(ActiveKeyGroupMap::value_type(group, 0)); 1121 if (p.second) { 1122 // If the engine channel is pending deletion (see bug 1123 // #113), pEngine will be null, so we can't use 1124 // pEngine->pEventPool here. Instead we're using a 1125 // specialized RTList that allows specifying the pool 1126 // later. 1127 (*p.first).second = new LazyList<Event>; 1128 } 1129 } 1130 } 1131 1132 /** 1133 * Handle key group (a.k.a. exclusive group) conflicts. 1134 */ HandleKeyGroupConflicts(uint KeyGroup,Pool<Event>::Iterator & itNoteOnEvent)1135 void AbstractEngineChannel::HandleKeyGroupConflicts(uint KeyGroup, Pool<Event>::Iterator& itNoteOnEvent) { 1136 dmsg(4,("HandelKeyGroupConflicts KeyGroup=%d\n", KeyGroup)); 1137 // when editing key groups with an instrument editor while sound was 1138 // already loaded, ActiveKeyGroups may not have the KeyGroup in question 1139 // so check for that to prevent a crash while editing instruments 1140 if (KeyGroup && ActiveKeyGroups.count(KeyGroup)) { 1141 // send a release event to all active voices in the group 1142 RTList<Event>::Iterator itEvent = ActiveKeyGroups[KeyGroup]->allocAppend(pEngine->pEventPool); 1143 *itEvent = *itNoteOnEvent; 1144 } 1145 } 1146 1147 /** 1148 * Empty the lists of group events. Should be called from the 1149 * audio thread, after all voices have been rendered. 1150 */ ClearGroupEventLists()1151 void AbstractEngineChannel::ClearGroupEventLists() { 1152 for (ActiveKeyGroupMap::iterator iter = ActiveKeyGroups.begin(); 1153 iter != ActiveKeyGroups.end(); iter++) { 1154 if (iter->second) { 1155 iter->second->clear(); 1156 } else { 1157 dmsg(1,("EngineChannel: group event list was NULL")); 1158 } 1159 } 1160 } 1161 1162 /** 1163 * Remove all lists with group events. 1164 */ DeleteGroupEventLists()1165 void AbstractEngineChannel::DeleteGroupEventLists() { 1166 for (ActiveKeyGroupMap::iterator iter = ActiveKeyGroups.begin(); 1167 iter != ActiveKeyGroups.end(); iter++) { 1168 delete iter->second; 1169 } 1170 ActiveKeyGroups.clear(); 1171 } 1172 1173 } // namespace LinuxSampler 1174