/[svn]/linuxsampler/trunk/src/engines/AbstractEngineChannel.cpp
ViewVC logotype

Annotation of /linuxsampler/trunk/src/engines/AbstractEngineChannel.cpp

Parent Directory Parent Directory | Revision Log Revision Log


Revision 2600 - (hide annotations) (download)
Sat Jun 7 00:16:03 2014 UTC (9 years, 10 months ago) by schoenebeck
File size: 48302 byte(s)
* Implemented built-in instrument script function "set_controller()".
* Fixed built-in script functions "ignore_event()" and
  "ignore_controller()".
* Added extended instrument script VM for the Gigasampler/GigaStudio format
  sampler engine, which extends the general instrument script VM with Giga
  format specific variables and functions.
* Giga format instrument scripts: added built-in script int constant
  variables $GIG_DIM_CHANNEL, $GIG_DIM_LAYER, $GIG_DIM_VELOCITY,
  $GIG_DIM_AFTERTOUCH, $GIG_DIM_RELEASE, $GIG_DIM_KEYBOARD,
  $GIG_DIM_ROUNDROBIN, $GIG_DIM_RANDOM, $GIG_DIM_SMARTMIDI,
  $GIG_DIM_ROUNDROBINKEY, $GIG_DIM_MODWHEEL, $GIG_DIM_BREATH,
  $GIG_DIM_FOOT, $GIG_DIM_PORTAMENTOTIME, $GIG_DIM_EFFECT1,
  $GIG_DIM_EFFECT2, $GIG_DIM_GENPURPOSE1, $GIG_DIM_GENPURPOSE2,
  $GIG_DIM_GENPURPOSE3, $GIG_DIM_GENPURPOSE4, $GIG_DIM_SUSTAIN,
  $GIG_DIM_PORTAMENTO, $GIG_DIM_SOSTENUTO, $GIG_DIM_SOFT,
  $GIG_DIM_GENPURPOSE5, $GIG_DIM_GENPURPOSE6, $GIG_DIM_GENPURPOSE7,
  $GIG_DIM_GENPURPOSE8, $GIG_DIM_EFFECT1DEPTH, $GIG_DIM_EFFECT2DEPTH,
  $GIG_DIM_EFFECT3DEPTH, $GIG_DIM_EFFECT4DEPTH, $GIG_DIM_EFFECT5DEPTH.
* Giga format instrument scripts: Implemented built-in script function
  "gig_set_dim_zone(event_id, dimension, zone)", which allows to override
  dimension zone(s) for new voices.
* Bumped version (1.0.0.svn45).

1 iliev 2012 /***************************************************************************
2     * *
3     * LinuxSampler - modular, streaming capable sampler *
4     * *
5     * Copyright (C) 2003,2004 by Benno Senoner and Christian Schoenebeck *
6 persson 2114 * Copyright (C) 2005-2008 Christian Schoenebeck *
7 schoenebeck 2500 * Copyright (C) 2009-2012 Christian Schoenebeck and Grigor Iliev *
8     * Copyright (C) 2013-2014 Christian Schoenebeck and Andreas Persson *
9 iliev 2012 * *
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    
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     pEventQueue = new RingBuffer<Event,false>(CONFIG_MAX_EVENTS_PER_FRAGMENT, 0);
39     InstrumentIdx = -1;
40     InstrumentStat = -1;
41     pChannelLeft = NULL;
42     pChannelRight = NULL;
43     AudioDeviceChannelLeft = -1;
44     AudioDeviceChannelRight = -1;
45     midiChannel = midi_chan_all;
46     ResetControllers();
47     PortamentoMode = false;
48     PortamentoTime = CONFIG_PORTAMENTO_TIME_DEFAULT;
49 schoenebeck 2594 pScriptEvents = NULL;
50 iliev 2012 }
51    
52     AbstractEngineChannel::~AbstractEngineChannel() {
53 schoenebeck 2594 unloadCurrentInstrumentScript();
54     if (pScriptEvents) delete pScriptEvents;
55 persson 2114 delete pEventQueue;
56     DeleteGroupEventLists();
57 iliev 2012 RemoveAllFxSends();
58     }
59    
60     Engine* AbstractEngineChannel::GetEngine() {
61     return pEngine;
62     }
63    
64     uint AbstractEngineChannel::Channels() {
65     return 2;
66     }
67    
68     /**
69     * More or less a workaround to set the instrument name, index and load
70     * status variable to zero percent immediately, that is without blocking
71     * the calling thread. It might be used in future for other preparations
72     * as well though.
73     *
74     * @param FileName - file name of the instrument file
75     * @param Instrument - index of the instrument in the file
76     * @see LoadInstrument()
77     */
78     void AbstractEngineChannel::PrepareLoadInstrument(const char* FileName, uint Instrument) {
79     InstrumentFile = FileName;
80     InstrumentIdx = Instrument;
81     InstrumentStat = 0;
82     }
83    
84     String AbstractEngineChannel::InstrumentFileName() {
85     return InstrumentFile;
86     }
87    
88     String AbstractEngineChannel::InstrumentName() {
89     return InstrumentIdxName;
90     }
91    
92     int AbstractEngineChannel::InstrumentIndex() {
93     return InstrumentIdx;
94     }
95    
96     int AbstractEngineChannel::InstrumentStatus() {
97     return InstrumentStat;
98     }
99    
100     String AbstractEngineChannel::EngineName() {
101     return AbstractEngine::GetFormatString(GetEngineFormat());
102     }
103    
104     void AbstractEngineChannel::Reset() {
105     if (pEngine) pEngine->DisableAndLock();
106     ResetInternal();
107     ResetControllers();
108     if (pEngine) {
109     pEngine->Enable();
110     pEngine->Reset();
111     }
112     }
113    
114     void AbstractEngineChannel::ResetControllers() {
115     Pitch = 0;
116     GlobalVolume = 1.0f;
117     MidiVolume = 1.0;
118     iLastPanRequest = 64;
119     GlobalTranspose = 0;
120     // set all MIDI controller values to zero
121     memset(ControllerTable, 0x00, 129);
122     // reset all FX Send levels
123     for (
124     std::vector<FxSend*>::iterator iter = fxSends.begin();
125     iter != fxSends.end(); iter++
126     ) {
127     (*iter)->Reset();
128     }
129     }
130    
131     /**
132     * This method is not thread safe!
133     */
134     void AbstractEngineChannel::ResetInternal() {
135     CurrentKeyDimension = 0;
136     PortamentoPos = -1.0f; // no portamento active yet
137    
138     // delete all input events
139     pEventQueue->init();
140    
141     if (pEngine) pEngine->ResetInternal();
142    
143     // status of engine channel has changed, so set notify flag
144     bStatusChanged = true;
145     }
146    
147     /**
148 schoenebeck 2594 * Loads the real-time instrument script given by @a text on this engine
149     * channel. A resource manager is used to allocate and share equivalent
150     * scripts on multiple engine channels.
151     *
152     * @param text - source code of script
153     */
154     void AbstractEngineChannel::loadInstrumentScript(const String& text) {
155     dmsg(1,("Loading real-time instrument script ... "));
156    
157     // hand back old script reference and VM execution contexts
158     // (if not done already)
159     unloadCurrentInstrumentScript();
160    
161     // get new script reference
162     script.parserContext = pEngine->scripts.Borrow(text, this);
163     if (!script.parserContext->errors().empty()) {
164     std::vector<ParserIssue> errors = script.parserContext->errors();
165     std::cerr << "[ScriptVM] Could not load instrument script, there were "
166     << errors.size() << " parser errors:\n";
167     for (int i = 0; i < errors.size(); ++i)
168     errors[i].dump();
169     return; // stop here if there were any parser errors
170     }
171    
172     script.handlerInit = script.parserContext->eventHandlerByName("init");
173     script.handlerNote = script.parserContext->eventHandlerByName("note");
174 schoenebeck 2596 script.handlerRelease = script.parserContext->eventHandlerByName("release");
175 schoenebeck 2594 script.handlerController = script.parserContext->eventHandlerByName("controller");
176     script.bHasValidScript =
177 schoenebeck 2596 script.handlerInit || script.handlerNote || script.handlerRelease ||
178     script.handlerController;
179 schoenebeck 2594
180     // amount of script handlers each script event has to execute
181     int handlerExecCount = 0;
182 schoenebeck 2596 if (script.handlerInit) handlerExecCount++; // "init" handler is always executed before the actual event handler
183     if (script.handlerNote || script.handlerRelease || script.handlerController) // only one of these are executed after "init" handler
184     handlerExecCount++;
185 schoenebeck 2594
186     // create script event pool (if it doesn't exist already)
187     if (!pScriptEvents)
188     pScriptEvents = new Pool<ScriptEvent>(CONFIG_MAX_EVENTS_PER_FRAGMENT);
189    
190     // create new VM execution contexts for new script
191     while (!pScriptEvents->poolIsEmpty()) {
192     RTList<ScriptEvent>::Iterator it = pScriptEvents->allocAppend();
193     it->execCtx = pEngine->pScriptVM->createExecContext(
194     script.parserContext
195     );
196     it->handlers = new VMEventHandler*[handlerExecCount+1];
197     }
198     pScriptEvents->clear();
199    
200     dmsg(1,("Done\n"));
201     }
202    
203     /**
204     * Unloads the currently used real-time instrument script on this sampler
205     * channel. A resource manager is used to share equivalent scripts among
206     * multiple sampler channels, and to deallocate the parsed script once not
207     * used on any engine channel anymore.
208     */
209     void AbstractEngineChannel::unloadCurrentInstrumentScript() {
210     if (script.parserContext)
211     dmsg(1,("Unloading current instrument script."));
212    
213     // free allocated VM execution contexts
214     if (pScriptEvents) {
215     pScriptEvents->clear();
216     while (!pScriptEvents->poolIsEmpty()) {
217     RTList<ScriptEvent>::Iterator it = pScriptEvents->allocAppend();
218     if (it->execCtx) {
219     // free VM execution context object
220     delete it->execCtx;
221     it->execCtx = NULL;
222     // free C array of handler pointers
223     delete [] it->handlers;
224     }
225     }
226     pScriptEvents->clear();
227     }
228     // hand back VM representation of script
229     if (script.parserContext) {
230     pEngine->scripts.HandBack(script.parserContext, this);
231     script.parserContext = NULL;
232     script.handlerInit = NULL;
233     script.handlerNote = NULL;
234 schoenebeck 2596 script.handlerRelease = NULL;
235 schoenebeck 2594 script.handlerController = NULL;
236     }
237     script.bHasValidScript = false;
238     }
239    
240     /**
241 iliev 2012 * Implementation of virtual method from abstract EngineChannel interface.
242     * This method will periodically be polled (e.g. by the LSCP server) to
243     * check if some engine channel parameter has changed since the last
244     * StatusChanged() call.
245     *
246     * This method can also be used to mark the engine channel as changed
247     * from outside, e.g. by a MIDI input device. The optional argument
248     * \a nNewStatus can be used for this.
249     *
250     * TODO: This "poll method" is just a lazy solution and might be
251     * replaced in future.
252     * @param bNewStatus - (optional, default: false) sets the new status flag
253     * @returns true if engine channel status has changed since last
254     * StatusChanged() call
255     */
256     bool AbstractEngineChannel::StatusChanged(bool bNewStatus) {
257     bool b = bStatusChanged;
258     bStatusChanged = bNewStatus;
259     return b;
260     }
261    
262     float AbstractEngineChannel::Volume() {
263     return GlobalVolume;
264     }
265    
266     void AbstractEngineChannel::Volume(float f) {
267     GlobalVolume = f;
268     bStatusChanged = true; // status of engine channel has changed, so set notify flag
269     }
270    
271     float AbstractEngineChannel::Pan() {
272     return float(iLastPanRequest - 64) / 64.0f;
273     }
274    
275     void AbstractEngineChannel::Pan(float f) {
276     int iMidiPan = int(f * 64.0f) + 64;
277     if (iMidiPan > 127) iMidiPan = 127;
278     else if (iMidiPan < 0) iMidiPan = 0;
279     iLastPanRequest = iMidiPan;
280     }
281    
282     AudioOutputDevice* AbstractEngineChannel::GetAudioOutputDevice() {
283     return (pEngine) ? pEngine->pAudioOutputDevice : NULL;
284     }
285    
286 persson 2326 /**
287     * Gets thread safe access to the currently connected audio output
288     * device from other threads than the lscp thread.
289     */
290     AudioOutputDevice* AbstractEngineChannel::GetAudioOutputDeviceSafe() {
291 persson 2427 LockGuard lock(EngineMutex);
292     return GetAudioOutputDevice();
293 persson 2326 }
294    
295 iliev 2012 void AbstractEngineChannel::SetOutputChannel(uint EngineAudioChannel, uint AudioDeviceChannel) {
296     if (!pEngine || !pEngine->pAudioOutputDevice) throw AudioOutputException("No audio output device connected yet.");
297    
298     AudioChannel* pChannel = pEngine->pAudioOutputDevice->Channel(AudioDeviceChannel);
299     if (!pChannel) throw AudioOutputException("Invalid audio output device channel " + ToString(AudioDeviceChannel));
300     switch (EngineAudioChannel) {
301     case 0: // left output channel
302     if (fxSends.empty()) pChannelLeft = pChannel;
303     AudioDeviceChannelLeft = AudioDeviceChannel;
304     break;
305     case 1: // right output channel
306     if (fxSends.empty()) pChannelRight = pChannel;
307     AudioDeviceChannelRight = AudioDeviceChannel;
308     break;
309     default:
310     throw AudioOutputException("Invalid engine audio channel " + ToString(EngineAudioChannel));
311     }
312    
313     bStatusChanged = true;
314     }
315    
316     int AbstractEngineChannel::OutputChannel(uint EngineAudioChannel) {
317     switch (EngineAudioChannel) {
318     case 0: // left channel
319     return AudioDeviceChannelLeft;
320     case 1: // right channel
321     return AudioDeviceChannelRight;
322     default:
323     throw AudioOutputException("Invalid engine audio channel " + ToString(EngineAudioChannel));
324     }
325     }
326    
327 schoenebeck 2500 void AbstractEngineChannel::Connect(MidiInputPort* pMidiPort) {
328     if (!pMidiPort) return;
329    
330     Sync< ArrayList<MidiInputPort*> > connections = midiInputs.back();
331    
332     // check if connection already exists
333     for (int i = 0; i < connections->size(); ++i)
334     if ((*connections)[i] == pMidiPort)
335     return; // to avoid endless recursion
336    
337     connections->add(pMidiPort);
338    
339     // inform MIDI port about this new connection
340     pMidiPort->Connect(this, MidiChannel());
341     }
342    
343     void AbstractEngineChannel::Disconnect(MidiInputPort* pMidiPort) {
344     if (!pMidiPort) return;
345    
346     Sync< ArrayList<MidiInputPort*> > connections = midiInputs.back();
347    
348     for (int i = 0; i < connections->size(); ++i) {
349     if ((*connections)[i] == pMidiPort) {
350     connections->remove(i);
351     // inform MIDI port about this disconnection
352     pMidiPort->Disconnect(this);
353     return;
354     }
355     }
356     }
357    
358     void AbstractEngineChannel::DisconnectAllMidiInputPorts() {
359     Sync< ArrayList<MidiInputPort*> > connections = midiInputs.back();
360     ArrayList<MidiInputPort*> clonedList = *connections;
361     connections->clear();
362     for (int i = 0; i < clonedList.size(); ++i) clonedList[i]->Disconnect(this);
363     }
364    
365     uint AbstractEngineChannel::GetMidiInputPortCount() {
366     Sync< ArrayList<MidiInputPort*> > connections = midiInputs.back();
367     return connections->size();
368     }
369    
370     MidiInputPort* AbstractEngineChannel::GetMidiInputPort(uint index) {
371     Sync< ArrayList<MidiInputPort*> > connections = midiInputs.back();
372     return (index < connections->size()) ? (*connections)[index] : NULL;
373     }
374    
375     // deprecated (just for API backward compatibility) - may be removed in future
376 iliev 2012 void AbstractEngineChannel::Connect(MidiInputPort* pMidiPort, midi_chan_t MidiChannel) {
377 schoenebeck 2500 if (!pMidiPort) return;
378    
379     Sync< ArrayList<MidiInputPort*> > connections = midiInputs.back();
380    
381     // check against endless recursion
382     if (connections->size() == 1 && (*connections)[0] == pMidiPort && this->midiChannel == MidiChannel)
383     return;
384    
385     if (!isValidMidiChan(MidiChannel))
386     throw MidiInputException("Invalid MIDI channel (" + ToString(int(MidiChannel)) + ")");
387    
388     this->midiChannel = MidiChannel;
389    
390     // disconnect all currently connected MIDI ports
391     ArrayList<MidiInputPort*> clonedList = *connections;
392     connections->clear();
393     for (int i = 0; i < clonedList.size(); ++i)
394     clonedList[i]->Disconnect(this);
395    
396     // connect the new port
397     connections->add(pMidiPort);
398 iliev 2012 pMidiPort->Connect(this, MidiChannel);
399     }
400    
401 schoenebeck 2500 // deprecated (just for API backward compatibility) - may be removed in future
402 iliev 2012 void AbstractEngineChannel::DisconnectMidiInputPort() {
403 schoenebeck 2500 DisconnectAllMidiInputPorts();
404 iliev 2012 }
405    
406 schoenebeck 2500 // deprecated (just for API backward compatibility) - may be removed in future
407 iliev 2012 MidiInputPort* AbstractEngineChannel::GetMidiInputPort() {
408 schoenebeck 2500 return GetMidiInputPort(0);
409 iliev 2012 }
410    
411     midi_chan_t AbstractEngineChannel::MidiChannel() {
412     return midiChannel;
413     }
414    
415 schoenebeck 2500 void AbstractEngineChannel::SetMidiChannel(midi_chan_t MidiChannel) {
416     if (this->midiChannel == MidiChannel) return;
417     if (!isValidMidiChan(MidiChannel))
418     throw MidiInputException("Invalid MIDI channel (" + ToString(int(MidiChannel)) + ")");
419    
420     this->midiChannel = MidiChannel;
421    
422     Sync< ArrayList<MidiInputPort*> > connections = midiInputs.back();
423     ArrayList<MidiInputPort*> clonedList = *connections;
424    
425     DisconnectAllMidiInputPorts();
426    
427     for (int i = 0; i < clonedList.size(); ++i) Connect(clonedList[i]);
428     }
429    
430 iliev 2012 void AbstractEngineChannel::Connect(VirtualMidiDevice* pDevice) {
431     // double buffer ... double work ...
432     {
433     ArrayList<VirtualMidiDevice*>& devices = virtualMidiDevices.GetConfigForUpdate();
434     devices.add(pDevice);
435     }
436     {
437     ArrayList<VirtualMidiDevice*>& devices = virtualMidiDevices.SwitchConfig();
438     devices.add(pDevice);
439     }
440     }
441    
442     void AbstractEngineChannel::Disconnect(VirtualMidiDevice* pDevice) {
443     // double buffer ... double work ...
444     {
445     ArrayList<VirtualMidiDevice*>& devices = virtualMidiDevices.GetConfigForUpdate();
446     devices.remove(pDevice);
447     }
448     {
449     ArrayList<VirtualMidiDevice*>& devices = virtualMidiDevices.SwitchConfig();
450     devices.remove(pDevice);
451     }
452     }
453    
454     /**
455     * Will be called by the MIDIIn Thread to let the audio thread trigger a new
456     * voice for the given key. This method is meant for real time rendering,
457     * that is an event will immediately be created with the current system
458     * time as time stamp.
459     *
460     * @param Key - MIDI key number of the triggered key
461     * @param Velocity - MIDI velocity value of the triggered key
462     */
463 persson 2317 void AbstractEngineChannel::SendNoteOn(uint8_t Key, uint8_t Velocity, uint8_t MidiChannel) {
464 iliev 2012 if (pEngine) {
465 schoenebeck 2500 // protection in case there are more than 1 MIDI input threads sending MIDI events to this EngineChannel
466     LockGuard g;
467     if (hasMultipleMIDIInputs()) g = LockGuard(MidiInputMutex);
468    
469 iliev 2012 Event event = pEngine->pEventGenerator->CreateEvent();
470     event.Type = Event::type_note_on;
471     event.Param.Note.Key = Key;
472     event.Param.Note.Velocity = Velocity;
473 persson 2317 event.Param.Note.Channel = MidiChannel;
474 schoenebeck 2600 event.Format = {}; // init format specific stuff with zeroes
475 iliev 2012 event.pEngineChannel = this;
476     if (this->pEventQueue->write_space() > 0) this->pEventQueue->push(&event);
477     else dmsg(1,("EngineChannel: Input event queue full!"));
478     // inform connected virtual MIDI devices if any ...
479     // (e.g. virtual MIDI keyboard in instrument editor(s))
480     ArrayList<VirtualMidiDevice*>& devices =
481     const_cast<ArrayList<VirtualMidiDevice*>&>(
482     virtualMidiDevicesReader_MidiThread.Lock()
483     );
484     for (int i = 0; i < devices.size(); i++) {
485     devices[i]->SendNoteOnToDevice(Key, Velocity);
486     }
487     virtualMidiDevicesReader_MidiThread.Unlock();
488     }
489     }
490    
491     /**
492     * Will be called by the MIDIIn Thread to let the audio thread trigger a new
493     * voice for the given key. This method is meant for offline rendering
494     * and / or for cases where the exact position of the event in the current
495     * audio fragment is already known.
496     *
497     * @param Key - MIDI key number of the triggered key
498     * @param Velocity - MIDI velocity value of the triggered key
499     * @param FragmentPos - sample point position in the current audio
500     * fragment to which this event belongs to
501     */
502 persson 2317 void AbstractEngineChannel::SendNoteOn(uint8_t Key, uint8_t Velocity, uint8_t MidiChannel, int32_t FragmentPos) {
503 iliev 2012 if (FragmentPos < 0) {
504     dmsg(1,("EngineChannel::SendNoteOn(): negative FragmentPos! Seems MIDI driver is buggy!"));
505     }
506     else if (pEngine) {
507 schoenebeck 2500 // protection in case there are more than 1 MIDI input threads sending MIDI events to this EngineChannel
508     LockGuard g;
509     if (hasMultipleMIDIInputs()) g = LockGuard(MidiInputMutex);
510    
511 iliev 2012 Event event = pEngine->pEventGenerator->CreateEvent(FragmentPos);
512     event.Type = Event::type_note_on;
513     event.Param.Note.Key = Key;
514     event.Param.Note.Velocity = Velocity;
515 persson 2317 event.Param.Note.Channel = MidiChannel;
516 schoenebeck 2600 event.Format = {}; // init format specific stuff with zeroes
517 iliev 2012 event.pEngineChannel = this;
518     if (this->pEventQueue->write_space() > 0) this->pEventQueue->push(&event);
519     else dmsg(1,("EngineChannel: Input event queue full!"));
520     // inform connected virtual MIDI devices if any ...
521     // (e.g. virtual MIDI keyboard in instrument editor(s))
522     ArrayList<VirtualMidiDevice*>& devices =
523     const_cast<ArrayList<VirtualMidiDevice*>&>(
524     virtualMidiDevicesReader_MidiThread.Lock()
525     );
526     for (int i = 0; i < devices.size(); i++) {
527     devices[i]->SendNoteOnToDevice(Key, Velocity);
528     }
529     virtualMidiDevicesReader_MidiThread.Unlock();
530     }
531     }
532    
533     /**
534     * Will be called by the MIDIIn Thread to signal the audio thread to release
535     * voice(s) on the given key. This method is meant for real time rendering,
536     * that is an event will immediately be created with the current system
537     * time as time stamp.
538     *
539     * @param Key - MIDI key number of the released key
540     * @param Velocity - MIDI release velocity value of the released key
541     */
542 persson 2317 void AbstractEngineChannel::SendNoteOff(uint8_t Key, uint8_t Velocity, uint8_t MidiChannel) {
543 iliev 2012 if (pEngine) {
544 schoenebeck 2500 // protection in case there are more than 1 MIDI input threads sending MIDI events to this EngineChannel
545     LockGuard g;
546     if (hasMultipleMIDIInputs()) g = LockGuard(MidiInputMutex);
547    
548 iliev 2012 Event event = pEngine->pEventGenerator->CreateEvent();
549     event.Type = Event::type_note_off;
550     event.Param.Note.Key = Key;
551     event.Param.Note.Velocity = Velocity;
552 persson 2317 event.Param.Note.Channel = MidiChannel;
553 schoenebeck 2600 event.Format = {}; // init format specific stuff with zeroes
554 iliev 2012 event.pEngineChannel = this;
555     if (this->pEventQueue->write_space() > 0) this->pEventQueue->push(&event);
556     else dmsg(1,("EngineChannel: Input event queue full!"));
557     // inform connected virtual MIDI devices if any ...
558     // (e.g. virtual MIDI keyboard in instrument editor(s))
559     ArrayList<VirtualMidiDevice*>& devices =
560     const_cast<ArrayList<VirtualMidiDevice*>&>(
561     virtualMidiDevicesReader_MidiThread.Lock()
562     );
563     for (int i = 0; i < devices.size(); i++) {
564     devices[i]->SendNoteOffToDevice(Key, Velocity);
565     }
566     virtualMidiDevicesReader_MidiThread.Unlock();
567     }
568     }
569    
570     /**
571     * Will be called by the MIDIIn Thread to signal the audio thread to release
572     * voice(s) on the given key. This method is meant for offline rendering
573     * and / or for cases where the exact position of the event in the current
574     * audio fragment is already known.
575     *
576     * @param Key - MIDI key number of the released key
577     * @param Velocity - MIDI release velocity value of the released key
578     * @param FragmentPos - sample point position in the current audio
579     * fragment to which this event belongs to
580     */
581 persson 2317 void AbstractEngineChannel::SendNoteOff(uint8_t Key, uint8_t Velocity, uint8_t MidiChannel, int32_t FragmentPos) {
582 iliev 2012 if (FragmentPos < 0) {
583     dmsg(1,("EngineChannel::SendNoteOff(): negative FragmentPos! Seems MIDI driver is buggy!"));
584     }
585     else if (pEngine) {
586 schoenebeck 2500 // protection in case there are more than 1 MIDI input threads sending MIDI events to this EngineChannel
587     LockGuard g;
588     if (hasMultipleMIDIInputs()) g = LockGuard(MidiInputMutex);
589    
590 iliev 2012 Event event = pEngine->pEventGenerator->CreateEvent(FragmentPos);
591     event.Type = Event::type_note_off;
592     event.Param.Note.Key = Key;
593     event.Param.Note.Velocity = Velocity;
594 persson 2317 event.Param.Note.Channel = MidiChannel;
595 schoenebeck 2600 event.Format = {}; // init format specific stuff with zeroes
596 iliev 2012 event.pEngineChannel = this;
597     if (this->pEventQueue->write_space() > 0) this->pEventQueue->push(&event);
598     else dmsg(1,("EngineChannel: Input event queue full!"));
599     // inform connected virtual MIDI devices if any ...
600     // (e.g. virtual MIDI keyboard in instrument editor(s))
601     ArrayList<VirtualMidiDevice*>& devices =
602     const_cast<ArrayList<VirtualMidiDevice*>&>(
603     virtualMidiDevicesReader_MidiThread.Lock()
604     );
605     for (int i = 0; i < devices.size(); i++) {
606     devices[i]->SendNoteOffToDevice(Key, Velocity);
607     }
608     virtualMidiDevicesReader_MidiThread.Unlock();
609     }
610     }
611    
612     /**
613     * Will be called by the MIDIIn Thread to signal the audio thread to change
614     * the pitch value for all voices. This method is meant for real time
615     * rendering, that is an event will immediately be created with the
616     * current system time as time stamp.
617     *
618     * @param Pitch - MIDI pitch value (-8192 ... +8191)
619     */
620 persson 2317 void AbstractEngineChannel::SendPitchbend(int Pitch, uint8_t MidiChannel) {
621 iliev 2012 if (pEngine) {
622 schoenebeck 2500 // protection in case there are more than 1 MIDI input threads sending MIDI events to this EngineChannel
623     LockGuard g;
624     if (hasMultipleMIDIInputs()) g = LockGuard(MidiInputMutex);
625    
626 iliev 2012 Event event = pEngine->pEventGenerator->CreateEvent();
627     event.Type = Event::type_pitchbend;
628     event.Param.Pitch.Pitch = Pitch;
629 persson 2317 event.Param.Pitch.Channel = MidiChannel;
630 schoenebeck 2600 event.Format = {}; // init format specific stuff with zeroes
631 iliev 2012 event.pEngineChannel = this;
632     if (this->pEventQueue->write_space() > 0) this->pEventQueue->push(&event);
633     else dmsg(1,("EngineChannel: Input event queue full!"));
634     }
635     }
636    
637     /**
638     * Will be called by the MIDIIn Thread to signal the audio thread to change
639     * the pitch value for all voices. This method is meant for offline
640     * rendering and / or for cases where the exact position of the event in
641     * the current audio fragment is already known.
642     *
643     * @param Pitch - MIDI pitch value (-8192 ... +8191)
644     * @param FragmentPos - sample point position in the current audio
645     * fragment to which this event belongs to
646     */
647 persson 2317 void AbstractEngineChannel::SendPitchbend(int Pitch, uint8_t MidiChannel, int32_t FragmentPos) {
648 iliev 2012 if (FragmentPos < 0) {
649     dmsg(1,("AbstractEngineChannel::SendPitchBend(): negative FragmentPos! Seems MIDI driver is buggy!"));
650     }
651     else if (pEngine) {
652 schoenebeck 2500 // protection in case there are more than 1 MIDI input threads sending MIDI events to this EngineChannel
653     LockGuard g;
654     if (hasMultipleMIDIInputs()) g = LockGuard(MidiInputMutex);
655    
656 iliev 2012 Event event = pEngine->pEventGenerator->CreateEvent(FragmentPos);
657     event.Type = Event::type_pitchbend;
658     event.Param.Pitch.Pitch = Pitch;
659 persson 2317 event.Param.Pitch.Channel = MidiChannel;
660 schoenebeck 2600 event.Format = {}; // init format specific stuff with zeroes
661 iliev 2012 event.pEngineChannel = this;
662     if (this->pEventQueue->write_space() > 0) this->pEventQueue->push(&event);
663     else dmsg(1,("AbstractEngineChannel: Input event queue full!"));
664     }
665     }
666    
667     /**
668     * Will be called by the MIDIIn Thread to signal the audio thread that a
669     * continuous controller value has changed. This method is meant for real
670     * time rendering, that is an event will immediately be created with the
671     * current system time as time stamp.
672     *
673     * @param Controller - MIDI controller number of the occured control change
674     * @param Value - value of the control change
675     */
676 persson 2317 void AbstractEngineChannel::SendControlChange(uint8_t Controller, uint8_t Value, uint8_t MidiChannel) {
677 iliev 2012 if (pEngine) {
678 schoenebeck 2500 // protection in case there are more than 1 MIDI input threads sending MIDI events to this EngineChannel
679     LockGuard g;
680     if (hasMultipleMIDIInputs()) g = LockGuard(MidiInputMutex);
681    
682 iliev 2012 Event event = pEngine->pEventGenerator->CreateEvent();
683     event.Type = Event::type_control_change;
684     event.Param.CC.Controller = Controller;
685     event.Param.CC.Value = Value;
686 persson 2317 event.Param.CC.Channel = MidiChannel;
687 schoenebeck 2600 event.Format = {}; // init format specific stuff with zeroes
688 iliev 2012 event.pEngineChannel = this;
689     if (this->pEventQueue->write_space() > 0) this->pEventQueue->push(&event);
690     else dmsg(1,("AbstractEngineChannel: Input event queue full!"));
691     }
692     }
693    
694     /**
695     * Will be called by the MIDIIn Thread to signal the audio thread that a
696     * continuous controller value has changed. This method is meant for
697     * offline rendering and / or for cases where the exact position of the
698     * event in the current audio fragment is already known.
699     *
700     * @param Controller - MIDI controller number of the occured control change
701     * @param Value - value of the control change
702     * @param FragmentPos - sample point position in the current audio
703     * fragment to which this event belongs to
704     */
705 persson 2317 void AbstractEngineChannel::SendControlChange(uint8_t Controller, uint8_t Value, uint8_t MidiChannel, int32_t FragmentPos) {
706 iliev 2012 if (FragmentPos < 0) {
707     dmsg(1,("AbstractEngineChannel::SendControlChange(): negative FragmentPos! Seems MIDI driver is buggy!"));
708     }
709     else if (pEngine) {
710 schoenebeck 2500 // protection in case there are more than 1 MIDI input threads sending MIDI events to this EngineChannel
711     LockGuard g;
712     if (hasMultipleMIDIInputs()) g = LockGuard(MidiInputMutex);
713    
714 iliev 2012 Event event = pEngine->pEventGenerator->CreateEvent(FragmentPos);
715     event.Type = Event::type_control_change;
716     event.Param.CC.Controller = Controller;
717     event.Param.CC.Value = Value;
718 persson 2317 event.Param.CC.Channel = MidiChannel;
719 schoenebeck 2600 event.Format = {}; // init format specific stuff with zeroes
720 iliev 2012 event.pEngineChannel = this;
721     if (this->pEventQueue->write_space() > 0) this->pEventQueue->push(&event);
722     else dmsg(1,("AbstractEngineChannel: Input event queue full!"));
723     }
724     }
725    
726 schoenebeck 2559 void AbstractEngineChannel::SendChannelPressure(uint8_t Value, uint8_t MidiChannel) {
727     if (pEngine) {
728     // protection in case there are more than 1 MIDI input threads sending MIDI events to this EngineChannel
729     LockGuard g;
730     if (hasMultipleMIDIInputs()) g = LockGuard(MidiInputMutex);
731    
732     Event event = pEngine->pEventGenerator->CreateEvent();
733     event.Type = Event::type_channel_pressure;
734     event.Param.ChannelPressure.Value = Value;
735     event.Param.ChannelPressure.Channel = MidiChannel;
736 schoenebeck 2600 event.Format = {}; // init format specific stuff with zeroes
737 schoenebeck 2559 event.pEngineChannel = this;
738     if (this->pEventQueue->write_space() > 0) this->pEventQueue->push(&event);
739     else dmsg(1,("AbstractEngineChannel: Input event queue full!"));
740     }
741     }
742    
743     void AbstractEngineChannel::SendChannelPressure(uint8_t Value, uint8_t MidiChannel, int32_t FragmentPos) {
744     if (pEngine) {
745     // protection in case there are more than 1 MIDI input threads sending MIDI events to this EngineChannel
746     LockGuard g;
747     if (hasMultipleMIDIInputs()) g = LockGuard(MidiInputMutex);
748    
749     Event event = pEngine->pEventGenerator->CreateEvent(FragmentPos);
750     event.Type = Event::type_channel_pressure;
751     event.Param.ChannelPressure.Value = Value;
752     event.Param.ChannelPressure.Channel = MidiChannel;
753 schoenebeck 2600 event.Format = {}; // init format specific stuff with zeroes
754 schoenebeck 2559 event.pEngineChannel = this;
755     if (this->pEventQueue->write_space() > 0) this->pEventQueue->push(&event);
756     else dmsg(1,("AbstractEngineChannel: Input event queue full!"));
757     }
758     }
759    
760     void AbstractEngineChannel::SendPolyphonicKeyPressure(uint8_t Key, uint8_t Value, uint8_t MidiChannel) {
761     if (pEngine) {
762     // protection in case there are more than 1 MIDI input threads sending MIDI events to this EngineChannel
763     LockGuard g;
764     if (hasMultipleMIDIInputs()) g = LockGuard(MidiInputMutex);
765    
766     Event event = pEngine->pEventGenerator->CreateEvent();
767     event.Type = Event::type_note_pressure;
768     event.Param.NotePressure.Key = Key;
769     event.Param.NotePressure.Value = Value;
770     event.Param.NotePressure.Channel = MidiChannel;
771 schoenebeck 2600 event.Format = {}; // init format specific stuff with zeroes
772 schoenebeck 2559 event.pEngineChannel = this;
773     if (this->pEventQueue->write_space() > 0) this->pEventQueue->push(&event);
774     else dmsg(1,("AbstractEngineChannel: Input event queue full!"));
775     }
776     }
777    
778     void AbstractEngineChannel::SendPolyphonicKeyPressure(uint8_t Key, uint8_t Value, uint8_t MidiChannel, int32_t FragmentPos) {
779     if (pEngine) {
780     // protection in case there are more than 1 MIDI input threads sending MIDI events to this EngineChannel
781     LockGuard g;
782     if (hasMultipleMIDIInputs()) g = LockGuard(MidiInputMutex);
783    
784     Event event = pEngine->pEventGenerator->CreateEvent(FragmentPos);
785     event.Type = Event::type_note_pressure;
786     event.Param.NotePressure.Key = Key;
787     event.Param.NotePressure.Value = Value;
788     event.Param.NotePressure.Channel = MidiChannel;
789 schoenebeck 2600 event.Format = {}; // init format specific stuff with zeroes
790 schoenebeck 2559 event.pEngineChannel = this;
791     if (this->pEventQueue->write_space() > 0) this->pEventQueue->push(&event);
792     else dmsg(1,("AbstractEngineChannel: Input event queue full!"));
793     }
794     }
795    
796 iliev 2012 /**
797     * Copy all events from the engine channel's input event queue buffer to
798     * the internal event list. This will be done at the beginning of each
799     * audio cycle (that is each RenderAudio() call) to distinguish all
800     * events which have to be processed in the current audio cycle. Each
801     * EngineChannel has it's own input event queue for the common channel
802     * specific events (like NoteOn, NoteOff and ControlChange events).
803     * Beside that, the engine also has a input event queue for global
804     * events (usually SysEx messages).
805     *
806     * @param Samples - number of sample points to be processed in the
807     * current audio cycle
808     */
809     void AbstractEngineChannel::ImportEvents(uint Samples) {
810     // import events from pure software MIDI "devices"
811     // (e.g. virtual keyboard in instrument editor)
812     {
813 persson 2317 const uint8_t channel = MidiChannel() == midi_chan_all ? 0 : MidiChannel();
814 iliev 2012 const int FragmentPos = 0; // randomly chosen, we don't care about jitter for virtual MIDI devices
815     Event event = pEngine->pEventGenerator->CreateEvent(FragmentPos);
816     VirtualMidiDevice::event_t devEvent; // the event format we get from the virtual MIDI device
817     // as we're going to (carefully) write some status to the
818     // synchronized struct, we cast away the const
819     ArrayList<VirtualMidiDevice*>& devices =
820     const_cast<ArrayList<VirtualMidiDevice*>&>(virtualMidiDevicesReader_AudioThread.Lock());
821     // iterate through all virtual MIDI devices
822     for (int i = 0; i < devices.size(); i++) {
823     VirtualMidiDevice* pDev = devices[i];
824     // I think we can simply flush the whole FIFO(s), the user shouldn't be so fast ;-)
825     while (pDev->GetMidiEventFromDevice(devEvent)) {
826 schoenebeck 2025 switch (devEvent.Type) {
827     case VirtualMidiDevice::EVENT_TYPE_NOTEON:
828     event.Type = Event::type_note_on;
829     event.Param.Note.Key = devEvent.Arg1;
830     event.Param.Note.Velocity = devEvent.Arg2;
831 persson 2317 event.Param.Note.Channel = channel;
832 schoenebeck 2025 break;
833     case VirtualMidiDevice::EVENT_TYPE_NOTEOFF:
834     event.Type = Event::type_note_off;
835     event.Param.Note.Key = devEvent.Arg1;
836     event.Param.Note.Velocity = devEvent.Arg2;
837 persson 2317 event.Param.Note.Channel = channel;
838 schoenebeck 2025 break;
839     case VirtualMidiDevice::EVENT_TYPE_CC:
840 schoenebeck 2521 switch (devEvent.Arg1) {
841     case 0: // bank select MSB ...
842     SetMidiBankMsb(devEvent.Arg2);
843     continue; // don't push this event into FIFO
844     case 32: // bank select LSB ...
845     SetMidiBankLsb(devEvent.Arg2);
846     continue; // don't push this event into FIFO
847     default: // regular MIDI CC ...
848     event.Type = Event::type_control_change;
849     event.Param.CC.Controller = devEvent.Arg1;
850     event.Param.CC.Value = devEvent.Arg2;
851     event.Param.CC.Channel = channel;
852     }
853 schoenebeck 2025 break;
854 schoenebeck 2521 case VirtualMidiDevice::EVENT_TYPE_PITCHBEND:
855     event.Type = Event::type_pitchbend;
856     event.Param.Pitch.Pitch = int(devEvent.Arg2 << 7 | devEvent.Arg1) - 8192;
857     event.Param.Pitch.Channel = channel;
858     break;
859     case VirtualMidiDevice::EVENT_TYPE_PROGRAM:
860     SendProgramChange(devEvent.Arg1);
861     continue; // don't push this event into FIFO
862 schoenebeck 2025 default:
863     std::cerr << "AbstractEngineChannel::ImportEvents() ERROR: unknown event type ("
864     << devEvent.Type << "). This is a bug!";
865     continue;
866     }
867     event.pEngineChannel = this;
868 iliev 2012 // copy event to internal event list
869     if (pEvents->poolIsEmpty()) {
870     dmsg(1,("Event pool emtpy!\n"));
871     goto exitVirtualDevicesLoop;
872     }
873     *pEvents->allocAppend() = event;
874     }
875     }
876     }
877     exitVirtualDevicesLoop:
878     virtualMidiDevicesReader_AudioThread.Unlock();
879    
880     // import events from the regular MIDI devices
881     RingBuffer<Event,false>::NonVolatileReader eventQueueReader = pEventQueue->get_non_volatile_reader();
882     Event* pEvent;
883     while (true) {
884     // get next event from input event queue
885     if (!(pEvent = eventQueueReader.pop())) break;
886     // if younger event reached, ignore that and all subsequent ones for now
887     if (pEvent->FragmentPos() >= Samples) {
888     eventQueueReader--;
889     dmsg(2,("Younger Event, pos=%d ,Samples=%d!\n",pEvent->FragmentPos(),Samples));
890     pEvent->ResetFragmentPos();
891     break;
892     }
893     // copy event to internal event list
894     if (pEvents->poolIsEmpty()) {
895     dmsg(1,("Event pool emtpy!\n"));
896     break;
897     }
898     *pEvents->allocAppend() = *pEvent;
899     }
900     eventQueueReader.free(); // free all copied events from input queue
901     }
902 schoenebeck 2598
903 schoenebeck 2596 /**
904     * Called by real-time instrument script functions to schedule a new event
905     * somewhere in future.
906 schoenebeck 2598 *
907     * @returns unique event ID of scheduled new event
908 schoenebeck 2596 */
909 schoenebeck 2598 int AbstractEngineChannel::ScheduleEvent(const Event* pEvent, int delay) { //TODO: delay not implemented yet
910 schoenebeck 2596 // since delay is not implemented yet, we simply add the new event
911     // to the event list of the current audio fragmet cycle for now
912     RTList<Event>::Iterator itEvent = pEvents->allocAppend();
913     if (itEvent) *itEvent = *pEvent; // copy event
914 schoenebeck 2598 return pEvents->getID(itEvent);
915 schoenebeck 2596 }
916 iliev 2012
917 schoenebeck 2598 /**
918     * Called by real-time instrument script functions to ignore the event
919     * reflected by given event ID. The event will be freed immediately to its
920     * pool and cannot be dereferenced by its old ID anymore. Even if its
921     * allocated back from the Pool later on, it will have a different ID.
922     */
923     void AbstractEngineChannel::IgnoreEvent(int id) {
924     RTList<Event>::Iterator it = pEvents->fromID(id);
925     if (it) pEvents->free(it);
926     }
927    
928 iliev 2012 FxSend* AbstractEngineChannel::AddFxSend(uint8_t MidiCtrl, String Name) throw (Exception) {
929     if (pEngine) pEngine->DisableAndLock();
930     FxSend* pFxSend = new FxSend(this, MidiCtrl, Name);
931     if (fxSends.empty()) {
932     if (pEngine && pEngine->pAudioOutputDevice) {
933     AudioOutputDevice* pDevice = pEngine->pAudioOutputDevice;
934     // create local render buffers
935     pChannelLeft = new AudioChannel(0, pDevice->MaxSamplesPerCycle());
936     pChannelRight = new AudioChannel(1, pDevice->MaxSamplesPerCycle());
937     } else {
938     // postpone local render buffer creation until audio device is assigned
939     pChannelLeft = NULL;
940     pChannelRight = NULL;
941     }
942     }
943     fxSends.push_back(pFxSend);
944     if (pEngine) pEngine->Enable();
945     fireFxSendCountChanged(GetSamplerChannel()->Index(), GetFxSendCount());
946    
947     return pFxSend;
948     }
949    
950     FxSend* AbstractEngineChannel::GetFxSend(uint FxSendIndex) {
951     return (FxSendIndex < fxSends.size()) ? fxSends[FxSendIndex] : NULL;
952     }
953    
954     uint AbstractEngineChannel::GetFxSendCount() {
955     return fxSends.size();
956     }
957    
958     void AbstractEngineChannel::RemoveFxSend(FxSend* pFxSend) {
959     if (pEngine) pEngine->DisableAndLock();
960     for (
961     std::vector<FxSend*>::iterator iter = fxSends.begin();
962     iter != fxSends.end(); iter++
963     ) {
964     if (*iter == pFxSend) {
965     delete pFxSend;
966     fxSends.erase(iter);
967     if (fxSends.empty()) {
968     // destroy local render buffers
969     if (pChannelLeft) delete pChannelLeft;
970     if (pChannelRight) delete pChannelRight;
971     // fallback to render directly into AudioOutputDevice's buffers
972     if (pEngine && pEngine->pAudioOutputDevice) {
973     pChannelLeft = pEngine->pAudioOutputDevice->Channel(AudioDeviceChannelLeft);
974     pChannelRight = pEngine->pAudioOutputDevice->Channel(AudioDeviceChannelRight);
975     } else { // we update the pointers later
976     pChannelLeft = NULL;
977     pChannelRight = NULL;
978     }
979     }
980     break;
981     }
982     }
983     if (pEngine) pEngine->Enable();
984     fireFxSendCountChanged(GetSamplerChannel()->Index(), GetFxSendCount());
985     }
986    
987     void AbstractEngineChannel::RemoveAllFxSends() {
988     if (pEngine) pEngine->DisableAndLock();
989     if (!fxSends.empty()) { // free local render buffers
990     if (pChannelLeft) {
991     delete pChannelLeft;
992     if (pEngine && pEngine->pAudioOutputDevice) {
993     // fallback to render directly to the AudioOutputDevice's buffer
994     pChannelLeft = pEngine->pAudioOutputDevice->Channel(AudioDeviceChannelLeft);
995     } else pChannelLeft = NULL;
996     }
997     if (pChannelRight) {
998     delete pChannelRight;
999     if (pEngine && pEngine->pAudioOutputDevice) {
1000     // fallback to render directly to the AudioOutputDevice's buffer
1001     pChannelRight = pEngine->pAudioOutputDevice->Channel(AudioDeviceChannelRight);
1002     } else pChannelRight = NULL;
1003     }
1004     }
1005     for (int i = 0; i < fxSends.size(); i++) delete fxSends[i];
1006     fxSends.clear();
1007     if (pEngine) pEngine->Enable();
1008     }
1009    
1010 persson 2114 /**
1011     * Add a group number to the set of key groups. Should be called
1012     * when an instrument is loaded to make sure there are event lists
1013     * for all key groups.
1014     */
1015     void AbstractEngineChannel::AddGroup(uint group) {
1016     if (group) {
1017 persson 2127 std::pair<ActiveKeyGroupMap::iterator, bool> p =
1018     ActiveKeyGroups.insert(ActiveKeyGroupMap::value_type(group, 0));
1019 persson 2114 if (p.second) {
1020 persson 2127 // If the engine channel is pending deletion (see bug
1021     // #113), pEngine will be null, so we can't use
1022     // pEngine->pEventPool here. Instead we're using a
1023     // specialized RTList that allows specifying the pool
1024     // later.
1025     (*p.first).second = new LazyList<Event>;
1026 persson 2114 }
1027     }
1028     }
1029    
1030     /**
1031     * Handle key group (a.k.a. exclusive group) conflicts.
1032     */
1033     void AbstractEngineChannel::HandleKeyGroupConflicts(uint KeyGroup, Pool<Event>::Iterator& itNoteOnEvent) {
1034     dmsg(4,("HandelKeyGroupConflicts KeyGroup=%d\n", KeyGroup));
1035     if (KeyGroup) {
1036     // send a release event to all active voices in the group
1037 persson 2127 RTList<Event>::Iterator itEvent = ActiveKeyGroups[KeyGroup]->allocAppend(pEngine->pEventPool);
1038 persson 2114 *itEvent = *itNoteOnEvent;
1039     }
1040     }
1041    
1042     /**
1043     * Empty the lists of group events. Should be called from the
1044     * audio thread, after all voices have been rendered.
1045     */
1046     void AbstractEngineChannel::ClearGroupEventLists() {
1047 persson 2127 for (ActiveKeyGroupMap::iterator iter = ActiveKeyGroups.begin();
1048 persson 2114 iter != ActiveKeyGroups.end(); iter++) {
1049     if (iter->second) {
1050     iter->second->clear();
1051     } else {
1052     dmsg(1,("EngineChannel: group event list was NULL"));
1053     }
1054     }
1055     }
1056    
1057     /**
1058     * Remove all lists with group events.
1059     */
1060     void AbstractEngineChannel::DeleteGroupEventLists() {
1061 persson 2127 for (ActiveKeyGroupMap::iterator iter = ActiveKeyGroups.begin();
1062 persson 2114 iter != ActiveKeyGroups.end(); iter++) {
1063     delete iter->second;
1064     }
1065     ActiveKeyGroups.clear();
1066     }
1067    
1068 iliev 2012 } // namespace LinuxSampler

  ViewVC Help
Powered by ViewVC