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