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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 3988 - (show annotations) (download)
Wed Aug 4 18:23:05 2021 UTC (2 years, 8 months ago) by schoenebeck
File size: 54688 byte(s)
* AbstractEngineChannel: fix CCs not being forwarded to all
  VirtualMIDIDevices.

* Bumped version (2.2.0.svn6).

1 /***************************************************************************
2 * *
3 * LinuxSampler - modular, streaming capable sampler *
4 * *
5 * Copyright (C) 2003,2004 by Benno Senoner and Christian Schoenebeck *
6 * Copyright (C) 2005-2021 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
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
53 AbstractEngineChannel::~AbstractEngineChannel() {
54 delete pEventQueue;
55 DeleteGroupEventLists();
56 RemoveAllFxSends();
57 }
58
59 Engine* AbstractEngineChannel::GetEngine() {
60 return pEngine;
61 }
62
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 */
77 void AbstractEngineChannel::PrepareLoadInstrument(const char* FileName, uint Instrument) {
78 InstrumentFile = FileName;
79 InstrumentIdx = Instrument;
80 InstrumentStat = 0;
81 }
82
83 String AbstractEngineChannel::InstrumentFileName() {
84 return InstrumentFile;
85 }
86
87 String AbstractEngineChannel::InstrumentName() {
88 return InstrumentIdxName;
89 }
90
91 int AbstractEngineChannel::InstrumentIndex() {
92 return InstrumentIdx;
93 }
94
95 int AbstractEngineChannel::InstrumentStatus() {
96 return InstrumentStat;
97 }
98
99 String AbstractEngineChannel::EngineName() {
100 return AbstractEngine::GetFormatString(GetEngineFormat());
101 }
102
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
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 */
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 */
168 bool AbstractEngineChannel::StatusChanged(bool bNewStatus) {
169 bool b = bStatusChanged;
170 bStatusChanged = bNewStatus;
171 return b;
172 }
173
174 float AbstractEngineChannel::Volume() {
175 return GlobalVolume;
176 }
177
178 void AbstractEngineChannel::Volume(float f) {
179 GlobalVolume = f;
180 bStatusChanged = true; // status of engine channel has changed, so set notify flag
181 }
182
183 float AbstractEngineChannel::Pan() {
184 return float(iLastPanRequest - 64) / 64.0f;
185 }
186
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
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 */
202 AudioOutputDevice* AbstractEngineChannel::GetAudioOutputDeviceSafe() {
203 LockGuard lock(EngineMutex);
204 return GetAudioOutputDevice();
205 }
206
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
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
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
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
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
277 uint AbstractEngineChannel::GetMidiInputPortCount() {
278 Sync< ArrayList<MidiInputPort*> > connections = midiInputs.back();
279 return (uint) connections->size();
280 }
281
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
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
314 void AbstractEngineChannel::DisconnectMidiInputPort() {
315 DisconnectAllMidiInputPorts();
316 }
317
318 // deprecated (just for API backward compatibility) - may be removed in future
319 MidiInputPort* AbstractEngineChannel::GetMidiInputPort() {
320 return GetMidiInputPort(0);
321 }
322
323 midi_chan_t AbstractEngineChannel::MidiChannel() {
324 return midiChannel;
325 }
326
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
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
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 */
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 */
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 */
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 */
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 */
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 */
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 */
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 // inform connected virtual MIDI devices if any ...
597 // (e.g. slider / knob in instrument editor(s))
598 ArrayList<VirtualMidiDevice*>& devices =
599 const_cast<ArrayList<VirtualMidiDevice*>&>(
600 virtualMidiDevicesReader_MidiThread.Lock()
601 );
602 for (int i = 0; i < devices.size(); i++) {
603 devices[i]->SendCCToDevice(Controller, Value);
604 }
605 virtualMidiDevicesReader_MidiThread.Unlock();
606 }
607 }
608
609 /**
610 * Will be called by the MIDIIn Thread to signal the audio thread that a
611 * continuous controller value has changed. This method is meant for
612 * offline rendering and / or for cases where the exact position of the
613 * event in the current audio fragment is already known.
614 *
615 * @param Controller - MIDI controller number of the occured control change
616 * @param Value - value of the control change
617 * @param FragmentPos - sample point position in the current audio
618 * fragment to which this event belongs to
619 */
620 void AbstractEngineChannel::SendControlChange(uint8_t Controller, uint8_t Value, uint8_t MidiChannel, int32_t FragmentPos) {
621 if (FragmentPos < 0) {
622 dmsg(1,("AbstractEngineChannel::SendControlChange(): negative FragmentPos! Seems MIDI driver is buggy!"));
623 }
624 else if (pEngine) {
625 // protection in case there are more than 1 MIDI input threads sending MIDI events to this EngineChannel
626 LockGuard g;
627 if (hasMultipleMIDIInputs()) g = LockGuard(MidiInputMutex);
628
629 Event event = pEngine->pEventGenerator->CreateEvent(FragmentPos);
630 event.Type = Event::type_control_change;
631 event.Param.CC.Controller = Controller;
632 event.Param.CC.Value = Value;
633 event.Param.CC.Channel = MidiChannel;
634 event.pEngineChannel = this;
635 if (this->pEventQueue->write_space() > 0) this->pEventQueue->push(&event);
636 else dmsg(1,("AbstractEngineChannel: Input event queue full!"));
637 // inform connected virtual MIDI devices if any ...
638 // (e.g. slider / knob in instrument editor(s))
639 ArrayList<VirtualMidiDevice*>& devices =
640 const_cast<ArrayList<VirtualMidiDevice*>&>(
641 virtualMidiDevicesReader_MidiThread.Lock()
642 );
643 for (int i = 0; i < devices.size(); i++) {
644 devices[i]->SendCCToDevice(Controller, Value);
645 }
646 virtualMidiDevicesReader_MidiThread.Unlock();
647 }
648 }
649
650 void AbstractEngineChannel::SendChannelPressure(uint8_t Value, uint8_t MidiChannel) {
651 if (pEngine) {
652 // 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 Event event = pEngine->pEventGenerator->CreateEvent();
657 event.Type = Event::type_channel_pressure;
658 event.Param.ChannelPressure.Controller = CTRL_TABLE_IDX_AFTERTOUCH; // required for instrument scripts
659 event.Param.ChannelPressure.Value = Value;
660 event.Param.ChannelPressure.Channel = MidiChannel;
661 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 void AbstractEngineChannel::SendChannelPressure(uint8_t Value, uint8_t MidiChannel, int32_t FragmentPos) {
668 if (pEngine) {
669 // protection in case there are more than 1 MIDI input threads sending MIDI events to this EngineChannel
670 LockGuard g;
671 if (hasMultipleMIDIInputs()) g = LockGuard(MidiInputMutex);
672
673 Event event = pEngine->pEventGenerator->CreateEvent(FragmentPos);
674 event.Type = Event::type_channel_pressure;
675 event.Param.ChannelPressure.Controller = CTRL_TABLE_IDX_AFTERTOUCH; // required for instrument scripts
676 event.Param.ChannelPressure.Value = Value;
677 event.Param.ChannelPressure.Channel = MidiChannel;
678 event.pEngineChannel = this;
679 if (this->pEventQueue->write_space() > 0) this->pEventQueue->push(&event);
680 else dmsg(1,("AbstractEngineChannel: Input event queue full!"));
681 }
682 }
683
684 void AbstractEngineChannel::SendPolyphonicKeyPressure(uint8_t Key, uint8_t Value, uint8_t MidiChannel) {
685 if (pEngine) {
686 // protection in case there are more than 1 MIDI input threads sending MIDI events to this EngineChannel
687 LockGuard g;
688 if (hasMultipleMIDIInputs()) g = LockGuard(MidiInputMutex);
689
690 Event event = pEngine->pEventGenerator->CreateEvent();
691 event.Type = Event::type_note_pressure;
692 event.Param.NotePressure.Key = Key;
693 event.Param.NotePressure.Value = Value;
694 event.Param.NotePressure.Channel = MidiChannel;
695 event.pEngineChannel = this;
696 if (this->pEventQueue->write_space() > 0) this->pEventQueue->push(&event);
697 else dmsg(1,("AbstractEngineChannel: Input event queue full!"));
698 }
699 }
700
701 void AbstractEngineChannel::SendPolyphonicKeyPressure(uint8_t Key, uint8_t Value, uint8_t MidiChannel, int32_t FragmentPos) {
702 if (pEngine) {
703 // protection in case there are more than 1 MIDI input threads sending MIDI events to this EngineChannel
704 LockGuard g;
705 if (hasMultipleMIDIInputs()) g = LockGuard(MidiInputMutex);
706
707 Event event = pEngine->pEventGenerator->CreateEvent(FragmentPos);
708 event.Type = Event::type_note_pressure;
709 event.Param.NotePressure.Key = Key;
710 event.Param.NotePressure.Value = Value;
711 event.Param.NotePressure.Channel = MidiChannel;
712 event.pEngineChannel = this;
713 if (this->pEventQueue->write_space() > 0) this->pEventQueue->push(&event);
714 else dmsg(1,("AbstractEngineChannel: Input event queue full!"));
715 }
716 }
717
718 bool AbstractEngineChannel::applyTranspose(Event* event) {
719 if (event->Type != Event::type_note_on && event->Type != Event::type_note_off)
720 return true; // event OK (not a note event, nothing to do with it here)
721
722 //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
723 const int k = event->Param.Note.Key + GlobalTranspose;
724 if (k < 0 || k > 127)
725 return false; // bad event, drop it
726
727 event->Param.Note.Key = k;
728
729 return true; // event OK
730 }
731
732 /**
733 * Copy all events from the engine channel's input event queue buffer to
734 * the internal event list. This will be done at the beginning of each
735 * audio cycle (that is each RenderAudio() call) to distinguish all
736 * events which have to be processed in the current audio cycle. Each
737 * EngineChannel has it's own input event queue for the common channel
738 * specific events (like NoteOn, NoteOff and ControlChange events).
739 * Beside that, the engine also has a input event queue for global
740 * events (usually SysEx messages).
741 *
742 * @param Samples - number of sample points to be processed in the
743 * current audio cycle
744 */
745 void AbstractEngineChannel::ImportEvents(uint Samples) {
746 // import events from pure software MIDI "devices"
747 // (e.g. virtual keyboard in instrument editor)
748 {
749 const uint8_t channel = MidiChannel() == midi_chan_all ? 0 : MidiChannel();
750 const int FragmentPos = 0; // randomly chosen, we don't care about jitter for virtual MIDI devices
751 const Event event = pEngine->pEventGenerator->CreateEvent(FragmentPos);
752 VirtualMidiDevice::event_t devEvent; // the event format we get from the virtual MIDI device
753 // as we're going to (carefully) write some status to the
754 // synchronized struct, we cast away the const
755 ArrayList<VirtualMidiDevice*>& devices =
756 const_cast<ArrayList<VirtualMidiDevice*>&>(virtualMidiDevicesReader_AudioThread.Lock());
757 // iterate through all virtual MIDI devices
758 for (int i = 0; i < devices.size(); i++) {
759 VirtualMidiDevice* pDev = devices[i];
760 // I think we can simply flush the whole FIFO(s), the user shouldn't be so fast ;-)
761 while (pDev->GetMidiEventFromDevice(devEvent)) {
762 if (pEvents->poolIsEmpty()) {
763 dmsg(1,("Event pool emtpy!\n"));
764 goto exitVirtualDevicesLoop;
765 }
766
767 // copy event to internal event list (this is already
768 // required here, because the LaunchNewNote() call below
769 // requires the event to be from the internal event pool for
770 // being able to generate a valid event ID)
771 RTList<Event>::Iterator itEvent = pEvents->allocAppend();
772 *itEvent = event;
773
774 itEvent->pEngineChannel = this;
775
776 switch (devEvent.Type) {
777 case VirtualMidiDevice::EVENT_TYPE_NOTEON:
778 itEvent->Type = Event::type_note_on;
779 itEvent->Param.Note.Key = devEvent.Arg1;
780 itEvent->Param.Note.Velocity = devEvent.Arg2;
781 itEvent->Param.Note.Channel = channel;
782 // apply transpose setting to (note on/off) event
783 if (!applyTranspose(&*itEvent)) {
784 // note value is out of range, so drop this event
785 pEvents->free(itEvent);
786 continue;
787 }
788 // assign a new note to this note-on event
789 if (!pEngine->LaunchNewNote(this, itEvent)) {
790 // failed launching new note, so drop this event
791 pEvents->free(itEvent);
792 continue;
793 }
794 break;
795 case VirtualMidiDevice::EVENT_TYPE_NOTEOFF:
796 itEvent->Type = Event::type_note_off;
797 itEvent->Param.Note.Key = devEvent.Arg1;
798 itEvent->Param.Note.Velocity = devEvent.Arg2;
799 itEvent->Param.Note.Channel = channel;
800 if (!applyTranspose(&*itEvent)) {
801 // note value is out of range, so drop this event
802 pEvents->free(itEvent);
803 continue;
804 }
805 break;
806 case VirtualMidiDevice::EVENT_TYPE_CC:
807 switch (devEvent.Arg1) {
808 case 0: // bank select MSB ...
809 SetMidiBankMsb(devEvent.Arg2);
810 // don't push this event into FIFO
811 pEvents->free(itEvent);
812 continue;
813 case 32: // bank select LSB ...
814 SetMidiBankLsb(devEvent.Arg2);
815 // don't push this event into FIFO
816 pEvents->free(itEvent);
817 continue;
818 default: // regular MIDI CC ...
819 itEvent->Type = Event::type_control_change;
820 itEvent->Param.CC.Controller = devEvent.Arg1;
821 itEvent->Param.CC.Value = devEvent.Arg2;
822 itEvent->Param.CC.Channel = channel;
823 }
824 break;
825 case VirtualMidiDevice::EVENT_TYPE_PITCHBEND:
826 itEvent->Type = Event::type_pitchbend;
827 itEvent->Param.Pitch.Pitch = int(devEvent.Arg2 << 7 | devEvent.Arg1) - 8192;
828 itEvent->Param.Pitch.Channel = channel;
829 break;
830 case VirtualMidiDevice::EVENT_TYPE_PROGRAM:
831 SendProgramChange(devEvent.Arg1);
832 // don't push this event into FIFO
833 pEvents->free(itEvent);
834 continue;
835 case VirtualMidiDevice::EVENT_TYPE_CHPRESSURE:
836 itEvent->Type = Event::type_channel_pressure;
837 itEvent->Param.ChannelPressure.Controller = CTRL_TABLE_IDX_AFTERTOUCH;
838 itEvent->Param.ChannelPressure.Value = devEvent.Arg2;
839 itEvent->Param.ChannelPressure.Channel = channel;
840 break;
841 default:
842 std::cerr << "AbstractEngineChannel::ImportEvents() ERROR: unknown event type ("
843 << devEvent.Type << "). This is a bug!";
844 pEvents->free(itEvent); // drop event
845 continue;
846 }
847 }
848 }
849 }
850 exitVirtualDevicesLoop:
851 virtualMidiDevicesReader_AudioThread.Unlock();
852
853 // import events from the regular MIDI devices
854 RingBuffer<Event,false>::NonVolatileReader eventQueueReader = pEventQueue->get_non_volatile_reader();
855 Event* pEvent;
856 while (true) {
857 // get next event from input event queue
858 if (!(pEvent = eventQueueReader.pop())) break;
859 // if younger event reached, ignore that and all subsequent ones for now
860 if (pEvent->FragmentPos() >= Samples) {
861 eventQueueReader--;
862 dmsg(2,("Younger Event, pos=%d ,Samples=%d!\n",pEvent->FragmentPos(),Samples));
863 pEvent->ResetFragmentPos();
864 break;
865 }
866 if (pEvents->poolIsEmpty()) {
867 dmsg(1,("Event pool emtpy!\n"));
868 break;
869 }
870
871 // copy event to internal event list
872 // (required already because LaunchNewNote() relies on it, see
873 // comment about it above)
874 RTList<Event>::Iterator itEvent = pEvents->allocAppend();
875 *itEvent = *pEvent;
876
877 // apply transpose setting to (note on/off) event
878 if (!applyTranspose(&*itEvent)) {
879 // it's a note event which has a note value out of range, so drop this event
880 pEvents->free(itEvent);
881 continue;
882 }
883 // assign a new note to this event (if its a note-on event)
884 if (itEvent->Type == Event::type_note_on) {
885 if (!pEngine->LaunchNewNote(this, itEvent)) {
886 // failed launching new note, so drop this event
887 pEvents->free(itEvent);
888 continue;
889 }
890 }
891
892 }
893 eventQueueReader.free(); // free all copied events from input queue
894 }
895
896 /**
897 * Called by real-time instrument script functions to schedule a new event
898 * @a delay microseconds in future.
899 *
900 * @b IMPORTANT: for the supplied @a delay to be scheduled correctly, the
901 * passed @a pEvent must be assigned a valid fragment time within the
902 * current audio fragment boundaries. That fragment time will be used by
903 * this method as basis for interpreting what "now" acutally is, and thus
904 * it will be used as basis for calculating the precise scheduling time
905 * for @a delay. The easiest way to achieve this is by copying a recent
906 * event which happened within the current audio fragment cycle: i.e. the
907 * original event which caused calling this method here.
908 *
909 * @param pEvent - event to be scheduled in future (event data will be copied)
910 * @param delay - amount of microseconds in future (from now) when event shall be processed
911 * @returns unique event ID of scheduled new event, or NULL on error
912 */
913 event_id_t AbstractEngineChannel::ScheduleEventMicroSec(const Event* pEvent, int64_t delay) {
914 dmsg(3,("AbstractEngineChannel::ScheduleEventMicroSec(Event.Type=%d,delay=%" PRId64 ")\n", pEvent->Type, delay));
915 RTList<Event>::Iterator itEvent = pEvents->allocAppend();
916 if (!itEvent) {
917 dmsg(1,("AbstractEngineChannel::ScheduleEventMicroSec(): Event pool emtpy!\n"));
918 return 0;
919 }
920 RTList<ScheduledEvent>::Iterator itNode = delayedEvents.schedulerNodes.allocAppend();
921 if (!itNode) { // scheduler node pool empty ...
922 dmsg(1,("AbstractEngineChannel::ScheduleEventMicroSec(): ScheduledEvent pool empty!\n"));
923 pEvents->free(itEvent);
924 return 0;
925 }
926 // copy passed event
927 *itEvent = *pEvent;
928 // move copied event to list of delayed events
929 itEvent = itEvent.moveToEndOf(delayedEvents.pList);
930 // connect scheduler node with the copied event
931 itNode->itEvent = itEvent;
932 // add entry to time sorted scheduler queue for copied event
933 pEngine->pEventGenerator->scheduleAheadMicroSec(
934 delayedEvents.queue, *itNode, itEvent->FragmentPos(), delay
935 );
936 //dmsg(5,("ScheduledEvent queue size: %d\n", delayedEvents.queue.size()));
937 return pEvents->getID(itEvent);
938 }
939
940 /**
941 * Called by real-time instrument script functions to ignore the event
942 * reflected by given event ID. The event will be freed immediately to its
943 * pool and cannot be dereferenced by its old ID anymore. Even if its
944 * allocated back from the Pool later on, it will have a different ID.
945 *
946 * @param id - unique ID of event to be dropped
947 */
948 void AbstractEngineChannel::IgnoreEvent(event_id_t id) {
949 RTList<Event>::Iterator it = pEvents->fromID(id);
950 if (it) pEvents->free(it);
951 }
952
953 /** @brief Drop the requested event.
954 *
955 * Called by real-time instrument script functions to ignore the event
956 * reflected by the given event @a id. This method detects whether the
957 * passed ID is actually a @c Note ID or a regular @c Event ID and act
958 * accordingly.
959 *
960 * @param id - event id (from script scope)
961 * @see ScriptID
962 */
963 void AbstractEngineChannel::IgnoreEventByScriptID(const ScriptID& id) {
964 switch (id.type()) {
965 case ScriptID::EVENT:
966 IgnoreEvent( id.eventID() );
967 break;
968 case ScriptID::NOTE:
969 IgnoreNote( id.noteID() );
970 break;
971 }
972 }
973
974 /** @brief Order resuming of script execution instance "now".
975 *
976 * Called by real-time instrument script function stop_wait() to resume a
977 * script callback currently being suspended (i.e. due to a wait() script
978 * function call).
979 *
980 * @param itCallback - suspended script callback to be resumed
981 * @param now - current scheduler time to be "now"
982 * @param forever - whether this particulare script callback should ignore
983 * all subsequent wait*() script function calls
984 */
985 void AbstractEngineChannel::ScheduleResumeOfScriptCallback(RTList<ScriptEvent>::Iterator& itCallback, sched_time_t now, bool forever) {
986 // ignore if invalid iterator was passed
987 if (!itCallback) return;
988
989 ScriptEvent* pCallback = &*itCallback;
990
991 // mark this callback to ignore all subsequent built-in wait*() script function calls
992 if (forever) pCallback->ignoreAllWaitCalls = true;
993
994 // ignore if callback is not in the scheduler queue
995 if (pCallback->currentSchedulerQueue() != &pScript->suspendedEvents) return;
996
997 // ignore if callback is already scheduled to be resumed "now"
998 if (pCallback->scheduleTime <= now) return;
999
1000 // take it out from the scheduler queue and re-insert callback
1001 // to schedule the script callback for resuming execution "now"
1002 pScript->suspendedEvents.erase(*pCallback);
1003 pCallback->scheduleTime = now + 1;
1004 pScript->suspendedEvents.insert(*pCallback);
1005 }
1006
1007 /** @brief Fork the given script execution instance.
1008 *
1009 * Called by real-time instrument script function fork() to create a new
1010 * script execution instance (child) of the script execution instance
1011 * (parent) that was calling fork(). This is essentially like creating a
1012 * new thread for a script handler being executing. The entire execution
1013 * state of parent is copied to the "forked" child.
1014 *
1015 * @param parent - original active script callback instance from which the
1016 * new child shall be forked from
1017 * @param bAutoAbort - whether the forked child shall automatically be
1018 * terminated as soon as parent terminates
1019 * @returns forked new child execution instance
1020 */
1021 RTList<ScriptEvent>::Iterator AbstractEngineChannel::forkScriptCallback(ScriptEvent* parent, bool bAutoAbort) {
1022 // check if the max. amount of child forks for this parent event handler
1023 // instance have not been exceeded yet
1024 if (parent->countChildHandlers() >= MAX_FORK_PER_SCRIPT_HANDLER)
1025 return RTList<ScriptEvent>::Iterator();
1026
1027 // allocate a new script callback instance for child to be forked
1028 RTList<ScriptEvent>::Iterator itChild = pScript->pEvents->allocAppend();
1029 if (!itChild) return itChild;
1030
1031 // copy entire script handler state from parent to forked child
1032 parent->forkTo(&*itChild, bAutoAbort);
1033
1034 // stick the parent ID and child ID respectively to each other
1035 itChild->parentHandlerID = GetScriptCallbackID(parent);
1036 parent->addChildHandlerID( GetScriptCallbackID(&*itChild) );
1037
1038 // insert newly created (forked) child event handler instance to the
1039 // scheduler queue for being executed soon
1040 pEngine->pEventGenerator->scheduleAheadMicroSec(
1041 pScript->suspendedEvents, // scheduler queue
1042 *itChild, // script event
1043 parent->cause.FragmentPos(), // current time of script event (basis for its next execution)
1044 0 // "resume" new child script callback instance ASAP
1045 );
1046
1047 return itChild;
1048 }
1049
1050 FxSend* AbstractEngineChannel::AddFxSend(uint8_t MidiCtrl, String Name) throw (Exception) {
1051 if (pEngine) pEngine->DisableAndLock();
1052 FxSend* pFxSend = new FxSend(this, MidiCtrl, Name);
1053 if (fxSends.empty()) {
1054 if (pEngine && pEngine->pAudioOutputDevice) {
1055 AudioOutputDevice* pDevice = pEngine->pAudioOutputDevice;
1056 // create local render buffers
1057 pChannelLeft = new AudioChannel(0, pDevice->MaxSamplesPerCycle());
1058 pChannelRight = new AudioChannel(1, pDevice->MaxSamplesPerCycle());
1059 } else {
1060 // postpone local render buffer creation until audio device is assigned
1061 pChannelLeft = NULL;
1062 pChannelRight = NULL;
1063 }
1064 }
1065 fxSends.push_back(pFxSend);
1066 if (pEngine) pEngine->Enable();
1067 fireFxSendCountChanged(GetSamplerChannel()->Index(), GetFxSendCount());
1068
1069 return pFxSend;
1070 }
1071
1072 FxSend* AbstractEngineChannel::GetFxSend(uint FxSendIndex) {
1073 return (FxSendIndex < fxSends.size()) ? fxSends[FxSendIndex] : NULL;
1074 }
1075
1076 uint AbstractEngineChannel::GetFxSendCount() {
1077 return (uint)fxSends.size();
1078 }
1079
1080 void AbstractEngineChannel::RemoveFxSend(FxSend* pFxSend) {
1081 if (pEngine) pEngine->DisableAndLock();
1082 for (
1083 std::vector<FxSend*>::iterator iter = fxSends.begin();
1084 iter != fxSends.end(); iter++
1085 ) {
1086 if (*iter == pFxSend) {
1087 delete pFxSend;
1088 fxSends.erase(iter);
1089 if (fxSends.empty()) {
1090 // destroy local render buffers
1091 if (pChannelLeft) delete pChannelLeft;
1092 if (pChannelRight) delete pChannelRight;
1093 // fallback to render directly into AudioOutputDevice's buffers
1094 if (pEngine && pEngine->pAudioOutputDevice) {
1095 pChannelLeft = pEngine->pAudioOutputDevice->Channel(AudioDeviceChannelLeft);
1096 pChannelRight = pEngine->pAudioOutputDevice->Channel(AudioDeviceChannelRight);
1097 } else { // we update the pointers later
1098 pChannelLeft = NULL;
1099 pChannelRight = NULL;
1100 }
1101 }
1102 break;
1103 }
1104 }
1105 if (pEngine) pEngine->Enable();
1106 fireFxSendCountChanged(GetSamplerChannel()->Index(), GetFxSendCount());
1107 }
1108
1109 void AbstractEngineChannel::RemoveAllFxSends() {
1110 if (pEngine) pEngine->DisableAndLock();
1111 if (!fxSends.empty()) { // free local render buffers
1112 if (pChannelLeft) {
1113 delete pChannelLeft;
1114 if (pEngine && pEngine->pAudioOutputDevice) {
1115 // fallback to render directly to the AudioOutputDevice's buffer
1116 pChannelLeft = pEngine->pAudioOutputDevice->Channel(AudioDeviceChannelLeft);
1117 } else pChannelLeft = NULL;
1118 }
1119 if (pChannelRight) {
1120 delete pChannelRight;
1121 if (pEngine && pEngine->pAudioOutputDevice) {
1122 // fallback to render directly to the AudioOutputDevice's buffer
1123 pChannelRight = pEngine->pAudioOutputDevice->Channel(AudioDeviceChannelRight);
1124 } else pChannelRight = NULL;
1125 }
1126 }
1127 for (int i = 0; i < fxSends.size(); i++) delete fxSends[i];
1128 fxSends.clear();
1129 if (pEngine) pEngine->Enable();
1130 }
1131
1132 /**
1133 * Add a group number to the set of key groups. Should be called
1134 * when an instrument is loaded to make sure there are event lists
1135 * for all key groups.
1136 */
1137 void AbstractEngineChannel::AddGroup(uint group) {
1138 if (group) {
1139 std::pair<ActiveKeyGroupMap::iterator, bool> p =
1140 ActiveKeyGroups.insert(ActiveKeyGroupMap::value_type(group, 0));
1141 if (p.second) {
1142 // If the engine channel is pending deletion (see bug
1143 // #113), pEngine will be null, so we can't use
1144 // pEngine->pEventPool here. Instead we're using a
1145 // specialized RTList that allows specifying the pool
1146 // later.
1147 (*p.first).second = new LazyList<Event>;
1148 }
1149 }
1150 }
1151
1152 /**
1153 * Handle key group (a.k.a. exclusive group) conflicts.
1154 */
1155 void AbstractEngineChannel::HandleKeyGroupConflicts(uint KeyGroup, Pool<Event>::Iterator& itNoteOnEvent) {
1156 dmsg(4,("HandelKeyGroupConflicts KeyGroup=%d\n", KeyGroup));
1157 // when editing key groups with an instrument editor while sound was
1158 // already loaded, ActiveKeyGroups may not have the KeyGroup in question
1159 // so check for that to prevent a crash while editing instruments
1160 if (KeyGroup && ActiveKeyGroups.count(KeyGroup)) {
1161 // send a release event to all active voices in the group
1162 RTList<Event>::Iterator itEvent = ActiveKeyGroups[KeyGroup]->allocAppend(pEngine->pEventPool);
1163 *itEvent = *itNoteOnEvent;
1164 }
1165 }
1166
1167 /**
1168 * Empty the lists of group events. Should be called from the
1169 * audio thread, after all voices have been rendered.
1170 */
1171 void AbstractEngineChannel::ClearGroupEventLists() {
1172 for (ActiveKeyGroupMap::iterator iter = ActiveKeyGroups.begin();
1173 iter != ActiveKeyGroups.end(); iter++) {
1174 if (iter->second) {
1175 iter->second->clear();
1176 } else {
1177 dmsg(1,("EngineChannel: group event list was NULL"));
1178 }
1179 }
1180 }
1181
1182 /**
1183 * Remove all lists with group events.
1184 */
1185 void AbstractEngineChannel::DeleteGroupEventLists() {
1186 for (ActiveKeyGroupMap::iterator iter = ActiveKeyGroups.begin();
1187 iter != ActiveKeyGroups.end(); iter++) {
1188 delete iter->second;
1189 }
1190 ActiveKeyGroups.clear();
1191 }
1192
1193 } // namespace LinuxSampler

  ViewVC Help
Powered by ViewVC