/[svn]/linuxsampler/trunk/src/engines/EngineBase.h
ViewVC logotype

Contents of /linuxsampler/trunk/src/engines/EngineBase.h

Parent Directory Parent Directory | Revision Log Revision Log


Revision 3699 - (show annotations) (download) (as text)
Sat Jan 4 14:07:02 2020 UTC (4 years, 3 months ago) by schoenebeck
File MIME type: text/x-c++hdr
File size: 146512 byte(s)
* Added support for MIDI CC #96 (data increment).

* Added support for MIDI CC #97 (data decrement).

* Bumped version (2.1.1.svn34).

1 /***************************************************************************
2 * *
3 * LinuxSampler - modular, streaming capable sampler *
4 * *
5 * Copyright (C) 2003,2004 by Benno Senoner and Christian Schoenebeck *
6 * Copyright (C) 2005-2008 Christian Schoenebeck *
7 * Copyright (C) 2009-2012 Christian Schoenebeck and Grigor Iliev *
8 * Copyright (C) 2012-2017 Christian Schoenebeck and 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 #ifndef __LS_ENGINEBASE_H__
27 #define __LS_ENGINEBASE_H__
28
29 #include "AbstractEngine.h"
30 #include "EngineChannelBase.h"
31 #include "common/DiskThreadBase.h"
32 #include "common/MidiKeyboardManager.h"
33 #include "InstrumentManager.h"
34 #include "../common/global_private.h"
35
36 // a bit headroom over CONFIG_MAX_VOICES to avoid minor complications i.e. under voice stealing conditions
37 #define MAX_NOTES_HEADROOM 3
38 #define GLOBAL_MAX_NOTES (GLOBAL_MAX_VOICES * MAX_NOTES_HEADROOM)
39
40 namespace LinuxSampler {
41
42 class AbstractEngineChannel;
43
44 template <
45 class V /* Voice */,
46 class RR /* Root Region */,
47 class R /* Region */,
48 class D /* Disk Thread */,
49 class IM /* Instrument Manager */,
50 class I /* Instrument */
51 >
52 class EngineBase: public AbstractEngine, public RegionPools<R>, public NotePool<V> {
53
54 public:
55 typedef typename RTList< Note<V> >::Iterator NoteIterator;
56 typedef typename RTList<V>::Iterator VoiceIterator;
57 typedef typename Pool<V>::Iterator PoolVoiceIterator;
58 typedef typename RTList<RR*>::Iterator RootRegionIterator;
59 typedef typename MidiKeyboardManager<V>::MidiKey MidiKey;
60
61 EngineBase() : noteIDPool(GLOBAL_MAX_NOTES), SuspendedRegions(128) {
62 pDiskThread = NULL;
63 pNotePool = new Pool< Note<V> >(GLOBAL_MAX_NOTES);
64 pNotePool->setPoolElementIDsReservedBits(INSTR_SCRIPT_EVENT_ID_RESERVED_BITS);
65 pVoicePool = new Pool<V>(GLOBAL_MAX_VOICES);
66 pRegionPool[0] = new Pool<R*>(GLOBAL_MAX_VOICES);
67 pRegionPool[1] = new Pool<R*>(GLOBAL_MAX_VOICES);
68 pVoiceStealingQueue = new RTList<Event>(pEventPool);
69 iMaxDiskStreams = GLOBAL_MAX_STREAMS;
70
71 // init all Voice objects in voice pool
72 for (VoiceIterator iterVoice = pVoicePool->allocAppend();
73 iterVoice; iterVoice = pVoicePool->allocAppend())
74 {
75 iterVoice->SetEngine(this);
76 }
77 pVoicePool->clear();
78
79 // init all Note objects in note pool
80 for (NoteIterator itNote = pNotePool->allocAppend(); itNote;
81 itNote = pNotePool->allocAppend())
82 {
83 itNote->init(pVoicePool, &noteIDPool);
84 }
85 pNotePool->clear();
86
87 ResetInternal();
88 ResetScaleTuning();
89 ResetSuspendedRegions();
90 }
91
92 virtual ~EngineBase() {
93 if (pDiskThread) {
94 dmsg(1,("Stopping disk thread..."));
95 pDiskThread->StopThread();
96 delete pDiskThread;
97 dmsg(1,("OK\n"));
98 }
99
100 if (pNotePool) {
101 pNotePool->clear();
102 delete pNotePool;
103 }
104
105 if (pVoicePool) {
106 pVoicePool->clear();
107 delete pVoicePool;
108 }
109
110 if (pVoiceStealingQueue) delete pVoiceStealingQueue;
111
112 if (pRegionPool[0]) delete pRegionPool[0];
113 if (pRegionPool[1]) delete pRegionPool[1];
114 ResetSuspendedRegions();
115 }
116
117 // implementation of abstract methods derived from class 'LinuxSampler::Engine'
118
119 /**
120 * Let this engine proceed to render the given amount of sample points.
121 * The engine will iterate through all engine channels and render audio
122 * for each engine channel independently. The calculated audio data of
123 * all voices of each engine channel will be placed into the audio sum
124 * buffers of the respective audio output device, connected to the
125 * respective engine channel.
126 *
127 * @param Samples - number of sample points to be rendered
128 * @returns 0 on success
129 */
130 virtual int RenderAudio(uint Samples) OVERRIDE {
131 dmsg(8,("RenderAudio(Samples=%d)\n", Samples));
132
133 // return if engine disabled
134 if (EngineDisabled.Pop()) {
135 dmsg(5,("EngineBase: engine disabled (val=%d)\n",EngineDisabled.GetUnsafe()));
136 EngineDisabled.RttDone();
137 return 0;
138 }
139
140 // process requests for suspending / resuming regions (i.e. to avoid
141 // crashes while these regions are modified by an instrument editor)
142 ProcessSuspensionsChanges();
143
144 // update time of start and end of this audio fragment (as events' time stamps relate to this)
145 pEventGenerator->UpdateFragmentTime(Samples);
146
147 // We only allow the given maximum number of voices to be spawned
148 // in each audio fragment. All subsequent request for spawning new
149 // voices in the same audio fragment will be ignored.
150 VoiceSpawnsLeft = MaxVoices();
151
152 // get all events from the engine's global input event queue which belong to the current fragment
153 // (these are usually just SysEx messages)
154 ImportEvents(Samples);
155
156 // process engine global events (these are currently only MIDI System Exclusive messages)
157 {
158 RTList<Event>::Iterator itEvent = pGlobalEvents->first();
159 RTList<Event>::Iterator end = pGlobalEvents->end();
160 for (; itEvent != end; ++itEvent) {
161 switch (itEvent->Type) {
162 case Event::type_sysex:
163 dmsg(5,("Engine: Sysex received\n"));
164 ProcessSysex(itEvent);
165 break;
166 default: ; // noop
167 }
168 }
169 }
170
171 // In case scale tuning has been changed, recalculate pitch for
172 // all active voices.
173 ProcessScaleTuningChange();
174
175 // reset internal voice counter (just for statistic of active voices)
176 ActiveVoiceCountTemp = 0;
177
178 HandleInstrumentChanges();
179
180 // handle events on all engine channels
181 for (int i = 0; i < engineChannels.size(); i++) {
182 ProcessEvents(engineChannels[i], Samples);
183 }
184
185 // render all 'normal', active voices on all engine channels
186 for (int i = 0; i < engineChannels.size(); i++) {
187 RenderActiveVoices(engineChannels[i], Samples);
188 }
189
190 // now that all ordinary voices on ALL engine channels are rendered, render new stolen voices
191 RenderStolenVoices(Samples);
192
193 // handle audio routing for engine channels with FX sends
194 for (int i = 0; i < engineChannels.size(); i++) {
195 AbstractEngineChannel* pChannel = static_cast<AbstractEngineChannel*>(engineChannels[i]);
196 if (pChannel->fxSends.empty()) continue; // ignore if no FX sends
197 RouteAudio(engineChannels[i], Samples);
198 }
199
200 // handle cleanup on all engine channels for the next audio fragment
201 for (int i = 0; i < engineChannels.size(); i++) {
202 PostProcess(engineChannels[i]);
203 }
204
205 // Just for debugging: dump the amount of free Note objects to
206 // the terminal (note due to the static variables being used,
207 // this is currently just intended for debugging with only one
208 // engine channel).
209 #if (CONFIG_DEBUG_LEVEL >= 3)
210 {
211 static int slice = 0;
212 static int noteCount = -1;
213 if (slice++ % 10 == 0) {
214 int n = pNotePool->countFreeElements();
215 if (n != noteCount) {
216 noteCount = n;
217 dmsg(1,("[%d] free Note objects count = %d\n", slice / 10, n));
218 }
219 }
220 }
221 #endif
222
223 // empty the engine's event list for the next audio fragment
224 ClearEventLists();
225
226 // reset voice stealing for the next audio fragment
227 pVoiceStealingQueue->clear();
228
229 // just some statistics about this engine instance
230 SetVoiceCount(ActiveVoiceCountTemp);
231 if (VoiceCount() > ActiveVoiceCountMax) ActiveVoiceCountMax = VoiceCount();
232
233 // in case regions were previously suspended and we killed voices
234 // with disk streams due to that, check if those streams have finally
235 // been deleted by the disk thread
236 if (iPendingStreamDeletions) ProcessPendingStreamDeletions();
237
238 // Release the instrument change command. (This has to
239 // be done after all voices have been rendered and not
240 // in HandleInstrumentChanges, as the RegionsInUse
241 // list has been built up by the voice renderers.)
242 for (int i = 0; i < engineChannels.size(); i++) {
243 EngineChannelBase<V, R, I>* channel =
244 static_cast<EngineChannelBase<V, R, I>*>(engineChannels[i]);
245 channel->InstrumentChangeCommandReader.Unlock();
246 }
247 FrameTime += Samples;
248
249 EngineDisabled.RttDone();
250 return 0;
251 }
252
253 virtual int MaxVoices() OVERRIDE { return pVoicePool->poolSize(); }
254
255 virtual void SetMaxVoices(int iVoices) throw (Exception) OVERRIDE {
256 if (iVoices < 1)
257 throw Exception("Maximum voices for an engine cannot be set lower than 1");
258
259 SuspendAll();
260
261 // NOTE: we need to clear pRegionsInUse before deleting pDimRegionPool,
262 // otherwise memory corruption will occur if there are active voices (see bug #118)
263 for (int iChannel = 0; iChannel < engineChannels.size(); iChannel++) {
264 EngineChannelBase<V, R, I>* pChannel = static_cast<EngineChannelBase<V, R, I>*>(engineChannels[iChannel]);
265 pChannel->ClearRegionsInUse();
266 }
267
268 if (pRegionPool[0]) delete pRegionPool[0];
269 if (pRegionPool[1]) delete pRegionPool[1];
270
271 pRegionPool[0] = new Pool<R*>(iVoices);
272 pRegionPool[1] = new Pool<R*>(iVoices);
273
274 for (int iChannel = 0; iChannel < engineChannels.size(); iChannel++) {
275 EngineChannelBase<V, R, I>* pChannel = static_cast<EngineChannelBase<V, R, I>*>(engineChannels[iChannel]);
276 pChannel->ResetRegionsInUse(pRegionPool);
277 }
278
279 // FIXME: Shouldn't all those pool elements be freed before resizing the pools?
280 try {
281 pVoicePool->resizePool(iVoices);
282 pNotePool->resizePool(iVoices * MAX_NOTES_HEADROOM);
283 noteIDPool.resizePool(iVoices * MAX_NOTES_HEADROOM);
284 } catch (...) {
285 throw Exception("FATAL: Could not resize voice pool!");
286 }
287
288 for (VoiceIterator iterVoice = pVoicePool->allocAppend();
289 iterVoice; iterVoice = pVoicePool->allocAppend())
290 {
291 iterVoice->SetEngine(this);
292 iterVoice->pDiskThread = this->pDiskThread;
293 }
294 pVoicePool->clear();
295
296 for (NoteIterator itNote = pNotePool->allocAppend(); itNote;
297 itNote = pNotePool->allocAppend())
298 {
299 itNote->init(pVoicePool, &noteIDPool);
300 }
301 pNotePool->clear();
302
303 PostSetMaxVoices(iVoices);
304 ResumeAll();
305 }
306
307 /** Called after the new max number of voices is set and before resuming the engine. */
308 virtual void PostSetMaxVoices(int iVoices) { }
309
310 virtual uint DiskStreamCount() OVERRIDE { return (pDiskThread) ? pDiskThread->GetActiveStreamCount() : 0; }
311 virtual uint DiskStreamCountMax() OVERRIDE { return (pDiskThread) ? pDiskThread->ActiveStreamCountMax : 0; }
312 virtual int MaxDiskStreams() OVERRIDE { return iMaxDiskStreams; }
313
314 virtual void SetMaxDiskStreams(int iStreams) throw (Exception) OVERRIDE {
315 if (iStreams < 0)
316 throw Exception("Maximum disk streams for an engine cannot be set lower than 0");
317
318 SuspendAll();
319
320 iMaxDiskStreams = iStreams;
321
322 // reconnect to audio output device, because that will automatically
323 // recreate the disk thread with the required amount of streams
324 if (pAudioOutputDevice) Connect(pAudioOutputDevice);
325
326 ResumeAll();
327 }
328
329 virtual String DiskStreamBufferFillBytes() OVERRIDE { return (pDiskThread) ? pDiskThread->GetBufferFillBytes() : ""; }
330 virtual String DiskStreamBufferFillPercentage() OVERRIDE { return (pDiskThread) ? pDiskThread->GetBufferFillPercentage() : ""; }
331 virtual InstrumentManager* GetInstrumentManager() OVERRIDE { return &instruments; }
332
333 /**
334 * Connect this engine instance with the given audio output device.
335 * This method will be called when an Engine instance is created.
336 * All of the engine's data structures which are dependant to the used
337 * audio output device / driver will be (re)allocated and / or
338 * adjusted appropriately.
339 *
340 * @param pAudioOut - audio output device to connect to
341 */
342 virtual void Connect(AudioOutputDevice* pAudioOut) OVERRIDE {
343 // caution: don't ignore if connecting to the same device here,
344 // because otherwise SetMaxDiskStreams() implementation won't work anymore!
345
346 pAudioOutputDevice = pAudioOut;
347
348 ResetInternal();
349
350 // inform audio driver for the need of two channels
351 try {
352 pAudioOutputDevice->AcquireChannels(2); // default stereo
353 }
354 catch (AudioOutputException e) {
355 String msg = "Audio output device unable to provide 2 audio channels, cause: " + e.Message();
356 throw Exception(msg);
357 }
358
359 this->MaxSamplesPerCycle = pAudioOutputDevice->MaxSamplesPerCycle();
360 this->SampleRate = pAudioOutputDevice->SampleRate();
361
362 MinFadeOutSamples = int(double(SampleRate) * CONFIG_EG_MIN_RELEASE_TIME) - 1;
363 if (MaxSamplesPerCycle < MinFadeOutSamples) {
364 std::cerr << "EngineBase: WARNING, CONFIG_EG_MIN_RELEASE_TIME "
365 << "too big for current audio fragment size & sampling rate! "
366 << "May lead to click sounds if voice stealing chimes in!\n" << std::flush;
367 // force volume ramp downs at the beginning of each fragment
368 MinFadeOutSamples = MaxSamplesPerCycle;
369 // lower minimum release time
370 const float minReleaseTime = (float) MaxSamplesPerCycle / (float) SampleRate;
371 pVoicePool->clear();
372 for (VoiceIterator iterVoice = pVoicePool->allocAppend(); iterVoice == pVoicePool->last(); iterVoice = pVoicePool->allocAppend()) {
373 iterVoice->CalculateFadeOutCoeff(minReleaseTime, SampleRate);
374 }
375 pVoicePool->clear();
376 }
377
378 // (re)create disk thread
379 if (this->pDiskThread) {
380 dmsg(1,("Stopping disk thread..."));
381 this->pDiskThread->StopThread();
382 delete this->pDiskThread;
383 dmsg(1,("OK\n"));
384 }
385 this->pDiskThread = CreateDiskThread();
386
387 if (!pDiskThread) {
388 dmsg(0,("EngineBase new diskthread = NULL\n"));
389 exit(EXIT_FAILURE);
390 }
391
392 pVoicePool->clear();
393 for (VoiceIterator iterVoice = pVoicePool->allocAppend(); iterVoice == pVoicePool->last(); iterVoice = pVoicePool->allocAppend()) {
394 iterVoice->pDiskThread = this->pDiskThread;
395 dmsg(3,("d"));
396 }
397 pVoicePool->clear();
398
399 // update event generator
400 pEventGenerator->SetSampleRate(pAudioOut->SampleRate());
401
402 dmsg(1,("Starting disk thread..."));
403 pDiskThread->StartThread();
404 dmsg(1,("OK\n"));
405
406 bool printEqInfo = true;
407 for (VoiceIterator iterVoice = pVoicePool->allocAppend(); iterVoice == pVoicePool->last(); iterVoice = pVoicePool->allocAppend()) {
408 if (!iterVoice->pDiskThread) {
409 dmsg(0,("Engine -> voice::trigger: !pDiskThread\n"));
410 exit(EXIT_FAILURE);
411 }
412
413 iterVoice->CreateEq();
414
415 if(printEqInfo) {
416 iterVoice->PrintEqInfo();
417 printEqInfo = false;
418 }
419 }
420 pVoicePool->clear();
421
422 // (re)create dedicated voice audio buffers
423 //TODO: we could optimize resource usage a bit by just allocating these dedicated voice buffers when there is at least one engine channel with FX sends, because only in this case those special buffers are used actually, but since it would usually only save couple bytes in total, its probably not worth it
424 if (pDedicatedVoiceChannelLeft) delete pDedicatedVoiceChannelLeft;
425 if (pDedicatedVoiceChannelRight) delete pDedicatedVoiceChannelRight;
426 pDedicatedVoiceChannelLeft = new AudioChannel(0, MaxSamplesPerCycle);
427 pDedicatedVoiceChannelRight = new AudioChannel(1, MaxSamplesPerCycle);
428 }
429
430 // Implementattion for abstract method derived from Engine.
431 virtual void ReconnectAudioOutputDevice() OVERRIDE {
432 SuspendAll();
433 if (pAudioOutputDevice) Connect(pAudioOutputDevice);
434 ResumeAll();
435 }
436
437 /**
438 * Similar to @c Disable() but this method additionally kills all voices
439 * and disk streams and blocks until all voices and disk streams are actually
440 * killed / deleted.
441 *
442 * @e Note: only the original calling thread is able to re-enable the
443 * engine afterwards by calling @c ResumeAll() later on!
444 */
445 virtual void SuspendAll() {
446 dmsg(2,("Engine: Suspending all ...\n"));
447 // stop the engine, so we can safely modify the engine's
448 // data structures from this foreign thread
449 DisableAndLock();
450 // we could also use the respective class member variable here,
451 // but this is probably safer and cleaner
452 int iPendingStreamDeletions = 0;
453 // kill all voices on all engine channels the *die hard* way
454 for (int iChannel = 0; iChannel < engineChannels.size(); iChannel++) {
455 EngineChannelBase<V, R, I>* pEngineChannel =
456 static_cast<EngineChannelBase<V, R, I>*>(engineChannels[iChannel]);
457
458 iPendingStreamDeletions += pEngineChannel->KillAllVoicesImmediately();
459 }
460 // wait until all streams were actually deleted by the disk thread
461 while (iPendingStreamDeletions) {
462 while (
463 iPendingStreamDeletions &&
464 pDiskThread->AskForDeletedStream() != Stream::INVALID_HANDLE
465 ) iPendingStreamDeletions--;
466 if (!iPendingStreamDeletions) break;
467 usleep(10000); // sleep for 10ms
468 }
469 dmsg(2,("EngineBase: Everything suspended.\n"));
470 }
471
472 /**
473 * At the moment same as calling @c Enable() directly, but this might
474 * change in future, so better call this method as counterpart to
475 * @c SuspendAll() instead of @c Enable() !
476 */
477 virtual void ResumeAll() { Enable(); }
478
479 /**
480 * Order the engine to stop rendering audio for the given region.
481 * Additionally this method will block until all voices and their disk
482 * streams associated with that region are actually killed / deleted, so
483 * one can i.e. safely modify the region with an instrument editor after
484 * returning from this method.
485 *
486 * @param pRegion - region the engine shall stop using
487 */
488 virtual void Suspend(RR* pRegion) {
489 dmsg(2,("EngineBase: Suspending Region %p ...\n",(void*)pRegion));
490 {
491 LockGuard lock(SuspendedRegionsMutex);
492 SuspensionChangeOngoing.Set(true);
493 pPendingRegionSuspension = pRegion;
494 SuspensionChangeOngoing.WaitAndUnlockIf(true);
495 }
496 dmsg(2,("EngineBase: Region %p suspended.",(void*)pRegion));
497 }
498
499 /**
500 * Orders the engine to resume playing back the given region, previously
501 * suspended with @c Suspend() .
502 *
503 * @param pRegion - region the engine shall be allowed to use again
504 */
505 virtual void Resume(RR* pRegion) {
506 dmsg(2,("EngineBase: Resuming Region %p ...\n",(void*)pRegion));
507 {
508 LockGuard lock(SuspendedRegionsMutex);
509 SuspensionChangeOngoing.Set(true);
510 pPendingRegionResumption = pRegion;
511 SuspensionChangeOngoing.WaitAndUnlockIf(true);
512 }
513 dmsg(2,("EngineBase: Region %p resumed.\n",(void*)pRegion));
514 }
515
516 virtual void ResetSuspendedRegions() {
517 SuspendedRegions.clear();
518 iPendingStreamDeletions = 0;
519 pPendingRegionSuspension = pPendingRegionResumption = NULL;
520 SuspensionChangeOngoing.Set(false);
521 }
522
523 /**
524 * Called by the engine's (audio) thread once per cycle to process requests
525 * from the outer world to suspend or resume a given @c gig::Region .
526 */
527 virtual void ProcessSuspensionsChanges() {
528 // process request for suspending one region
529 if (pPendingRegionSuspension) {
530 // kill all voices on all engine channels that use this region
531 for (int iChannel = 0; iChannel < engineChannels.size(); iChannel++) {
532 EngineChannelBase<V, R, I>* pEngineChannel =
533 static_cast<EngineChannelBase<V, R, I>*>(engineChannels[iChannel]);
534 SuspensionVoiceHandler handler(pPendingRegionSuspension);
535 pEngineChannel->ProcessActiveVoices(&handler);
536 iPendingStreamDeletions += handler.PendingStreamDeletions;
537 }
538 // make sure the region is not yet on the list
539 bool bAlreadySuspended = false;
540 RootRegionIterator iter = SuspendedRegions.first();
541 RootRegionIterator end = SuspendedRegions.end();
542 for (; iter != end; ++iter) { // iterate through all suspended regions
543 if (*iter == pPendingRegionSuspension) { // found
544 bAlreadySuspended = true;
545 dmsg(1,("EngineBase: attempt to suspend an already suspended region !!!\n"));
546 break;
547 }
548 }
549 if (!bAlreadySuspended) {
550 // put the region on the list of suspended regions
551 RootRegionIterator iter = SuspendedRegions.allocAppend();
552 if (iter) {
553 *iter = pPendingRegionSuspension;
554 } else std::cerr << "EngineBase: Could not suspend Region, list is full. This is a bug!!!\n" << std::flush;
555 }
556 // free request slot for next caller (and to make sure that
557 // we're not going to process the same request in the next cycle)
558 pPendingRegionSuspension = NULL;
559 // if no disk stream deletions are pending, awaken other side, as
560 // we're done in this case
561 if (!iPendingStreamDeletions) SuspensionChangeOngoing.Set(false);
562 }
563
564 // process request for resuming one region
565 if (pPendingRegionResumption) {
566 // remove region from the list of suspended regions
567 RootRegionIterator iter = SuspendedRegions.first();
568 RootRegionIterator end = SuspendedRegions.end();
569 for (; iter != end; ++iter) { // iterate through all suspended regions
570 if (*iter == pPendingRegionResumption) { // found
571 SuspendedRegions.free(iter);
572 break; // done
573 }
574 }
575 // free request slot for next caller
576 pPendingRegionResumption = NULL;
577 // awake other side as we're done
578 SuspensionChangeOngoing.Set(false);
579 }
580 }
581
582 /**
583 * Called by the engine's (audio) thread once per cycle to check if
584 * streams of voices that were killed due to suspension request have
585 * finally really been deleted by the disk thread.
586 */
587 virtual void ProcessPendingStreamDeletions() {
588 if (!iPendingStreamDeletions) return;
589 //TODO: or shall we better store a list with stream handles instead of a scalar amount of streams to be deleted? might be safer
590 while (
591 iPendingStreamDeletions &&
592 pDiskThread->AskForDeletedStream() != Stream::INVALID_HANDLE
593 ) iPendingStreamDeletions--;
594 // just for safety ...
595 while (pDiskThread->AskForDeletedStream() != Stream::INVALID_HANDLE);
596 // now that all disk streams are deleted, awake other side as
597 // we're finally done with suspending the requested region
598 if (!iPendingStreamDeletions) SuspensionChangeOngoing.Set(false);
599 }
600
601 /**
602 * Returns @c true if the given region is currently set to be suspended
603 * from being used, @c false otherwise.
604 */
605 virtual bool RegionSuspended(RR* pRegion) {
606 if (SuspendedRegions.isEmpty()) return false;
607 //TODO: or shall we use a sorted container instead of the RTList? might be faster ... or trivial ;-)
608 RootRegionIterator iter = SuspendedRegions.first();
609 RootRegionIterator end = SuspendedRegions.end();
610 for (; iter != end; ++iter) // iterate through all suspended regions
611 if (*iter == pRegion) return true;
612 return false;
613 }
614
615 // implementation of abstract method derived from class 'LinuxSampler::RegionPools'
616 virtual Pool<R*>* GetRegionPool(int index) OVERRIDE {
617 if (index < 0 || index > 1) throw Exception("Index out of bounds");
618 return pRegionPool[index];
619 }
620
621 // implementation of abstract methods derived from class 'LinuxSampler::NotePool'
622 virtual Pool<V>* GetVoicePool() OVERRIDE { return pVoicePool; }
623 virtual Pool< Note<V> >* GetNotePool() OVERRIDE { return pNotePool; }
624 virtual Pool<note_id_t>* GetNoteIDPool() OVERRIDE { return &noteIDPool; }
625
626 D* GetDiskThread() { return pDiskThread; }
627
628 //friend class EngineChannelBase<V, R, I>;
629
630 static IM instruments;
631
632 protected:
633 class SuspensionVoiceHandler : public MidiKeyboardManager<V>::VoiceHandler {
634 public:
635 int PendingStreamDeletions;
636 RR* pPendingRegionSuspension;
637
638 SuspensionVoiceHandler(RR* pPendingRegionSuspension) {
639 PendingStreamDeletions = 0;
640 this->pPendingRegionSuspension = pPendingRegionSuspension;
641 }
642
643 virtual bool Process(MidiKey* pMidiKey) OVERRIDE {
644 NoteIterator itNote = pMidiKey->pActiveNotes->first();
645 VoiceIterator itVoice = itNote->pActiveVoices->first();
646 // if current key is not associated with this region, skip this key
647 if (itVoice->GetRegion()->GetParent() != pPendingRegionSuspension) return false;
648
649 return true;
650 }
651
652 virtual void Process(VoiceIterator& itVoice) OVERRIDE {
653 // request a notification from disk thread side for stream deletion
654 const Stream::Handle hStream = itVoice->KillImmediately(true);
655 if (hStream != Stream::INVALID_HANDLE) { // voice actually used a stream
656 PendingStreamDeletions++;
657 }
658 //NOTE: maybe we should call FreeVoice() here, shouldn't cause a harm though I think, since the voices should be freed by RenderActiveVoices() in the render loop, they are probably just freed a bit later than they could/should be
659 }
660 };
661
662 Pool<R*>* pRegionPool[2]; ///< Double buffered pool, used by the engine channels to keep track of regions in use.
663 int MinFadeOutSamples; ///< The number of samples needed to make an instant fade out (e.g. for voice stealing) without leading to clicks.
664 D* pDiskThread;
665
666 int ActiveVoiceCountTemp; ///< number of currently active voices (for internal usage, will be used for incrementation)
667 VoiceIterator itLastStolenVoice; ///< Only for voice stealing: points to the last voice which was theft in current audio fragment, NULL otherwise.
668 NoteIterator itLastStolenNote; ///< Only for voice stealing: points to the last note from which was theft in current audio fragment, NULL otherwise.
669 RTList<uint>::Iterator iuiLastStolenKey; ///< Only for voice stealing: key number of last key on which the last voice was theft in current audio fragment, NULL otherwise.
670 EngineChannelBase<V, R, I>* pLastStolenChannel; ///< Only for voice stealing: points to the engine channel on which the previous voice was stolen in this audio fragment.
671 VoiceIterator itLastStolenVoiceGlobally; ///< Same as itLastStolenVoice, but engine globally
672 NoteIterator itLastStolenNoteGlobally; ///< Same as itLastStolenNote, but engine globally
673 RTList<uint>::Iterator iuiLastStolenKeyGlobally; ///< Same as iuiLastStolenKey, but engine globally
674 RTList<Event>* pVoiceStealingQueue; ///< All voice-launching events which had to be postponed due to free voice shortage.
675 Mutex ResetInternalMutex; ///< Mutex to protect the ResetInternal function for concurrent usage (e.g. by the lscp and instrument loader threads).
676 int iMaxDiskStreams;
677
678 NoteBase* NoteByID(note_id_t id) OVERRIDE {
679 NoteIterator itNote = GetNotePool()->fromID(id);
680 if (!itNote) return NULL;
681 return &*itNote;
682 }
683
684 /**
685 * Gets a new @c Note object from the note pool, initializes it
686 * appropriately, links it with requested parent note (if
687 * requested), moves it to the appropriate key's list of active
688 * notes it, and sticks the new note's unique ID to the
689 * passed @a pNoteOnEvent.
690 *
691 * @param pEngineChannel - engine channel on which this event happened
692 * @param pNoteOnEvent - event which caused this
693 * @returns new note's unique ID (or zero on error)
694 */
695 note_id_t LaunchNewNote(LinuxSampler::EngineChannel* pEngineChannel, Pool<Event>::Iterator& itNoteOnEvent) OVERRIDE {
696 EngineChannelBase<V, R, I>* pChannel = static_cast<EngineChannelBase<V, R, I>*>(pEngineChannel);
697 Pool< Note<V> >* pNotePool = GetNotePool();
698
699 if (pNotePool->poolIsEmpty()) {
700 dmsg(1,("Engine: Could not launch new note; Note pool empty!\n"));
701 return 0; // error
702 }
703
704 // create a new note (for new voices to be assigned to)
705 //NoteIterator itNewNote = pKey->pActiveNotes->allocAppend();
706 NoteIterator itNewNote = pNotePool->allocAppend();
707 const note_id_t newNoteID = pNotePool->getID(itNewNote);
708
709 // remember the engine's time when this note was triggered exactly
710 itNewNote->triggerSchedTime = itNoteOnEvent->SchedTime();
711
712 // usually the new note (and its subsequent voices) will be
713 // allocated on the key provided by the event's note number,
714 // however if this new note is requested not to be a regular
715 // note, but rather a child note, then this new note will be
716 // allocated on the parent note's key instead in order to
717 // release the child note simultaniously with its parent note
718 itNewNote->hostKey = itNoteOnEvent->Param.Note.Key;
719
720 // in case this new note was requested to be a child note,
721 // then retrieve its parent note and link them with each other
722 const note_id_t parentNoteID = itNoteOnEvent->Param.Note.ParentNoteID;
723 if (parentNoteID) {
724 NoteIterator itParentNote = pNotePool->fromID(parentNoteID);
725 if (itParentNote) {
726 RTList<note_id_t>::Iterator itChildNoteID = itParentNote->pChildNotes->allocAppend();
727 if (itChildNoteID) {
728 // link parent and child note with each other
729 *itChildNoteID = newNoteID;
730 itNewNote->parentNoteID = parentNoteID;
731 itNewNote->hostKey = itParentNote->hostKey;
732 } else {
733 dmsg(1,("Engine: Could not assign new note as child note; Note ID pool empty!\n"));
734 pNotePool->free(itNewNote);
735 return 0; // error
736 }
737 } else {
738 // the parent note was apparently released already, so
739 // free the new note again and inform caller that it
740 // should drop the event
741 dmsg(3,("Engine: Could not assign new note as child note; Parent note is gone!\n"));
742 pNotePool->free(itNewNote);
743 return 0; // error
744 }
745 }
746
747 dmsg(2,("Launched new note on host key %d\n", itNewNote->hostKey));
748
749 // copy event which caused this note
750 itNewNote->cause = *itNoteOnEvent;
751 itNewNote->eventID = pEventPool->getID(itNoteOnEvent);
752 if (!itNewNote->eventID) {
753 dmsg(0,("Engine: No valid event ID resolved for note. This is a bug!!!\n"));
754 }
755
756 // move new note to its host key
757 MidiKey* pKey = &pChannel->pMIDIKeyInfo[itNewNote->hostKey];
758 itNewNote.moveToEndOf(pKey->pActiveNotes);
759 pChannel->markKeyAsActive(pKey);
760
761 // assign unique note ID of this new note to the original note on event
762 itNoteOnEvent->Param.Note.ID = newNoteID;
763
764 return newNoteID; // success
765 }
766
767 /**
768 * Dispatch and handle all events in this audio fragment for the given
769 * engine channel.
770 *
771 * @param pEngineChannel - engine channel on which events should be
772 * processed
773 * @param Samples - amount of sample points to be processed in
774 * this audio fragment cycle
775 */
776 void ProcessEvents(EngineChannel* pEngineChannel, uint Samples) {
777 // get all events from the engine channels's input event queue which belong to the current fragment
778 // (these are the common events like NoteOn, NoteOff, ControlChange, etc.)
779 AbstractEngineChannel* pChannel = static_cast<AbstractEngineChannel*>(pEngineChannel);
780 pChannel->ImportEvents(Samples);
781
782 // if a valid real-time instrument script is loaded, pre-process
783 // the event list by running the script now, since the script
784 // might filter events or add new ones for this cycle
785 if (pChannel->pScript) {
786 const sched_time_t fragmentEndTime = pEventGenerator->schedTimeAtCurrentFragmentEnd();
787
788 // resume suspended script executions been scheduled for
789 // this audio fragment cycle (which were suspended in a
790 // previous audio fragment cycle)
791 ProcessSuspendedScriptEvents(pChannel, fragmentEndTime);
792
793 // spawn new script executions for the new MIDI events of
794 // this audio fragment cycle
795 //
796 // FIXME: it would probably be better to just schedule newly spawned script executions here and then execute them altogether with already suspended ones all at once in order of all their scheduled timing
797 for (RTList<Event>::Iterator itEvent = pChannel->pEvents->first(),
798 end = pChannel->pEvents->end(); itEvent != end; )
799 {
800 //HACK: avoids iterator invalidation which might happen below since an instrument script might drop an event by direct raw pointer access (it would be considerable to extend the Iterator class to detect and circumvent this case by checking the "reincarnation" member variable).
801 RTList<Event>::Iterator itNext = itEvent;
802 ++itNext;
803
804 switch (itEvent->Type) {
805 case Event::type_note_on:
806 if (pChannel->pScript->handlerNote)
807 ProcessEventByScript(pChannel, itEvent, pChannel->pScript->handlerNote);
808 break;
809 case Event::type_note_off:
810 if (pChannel->pScript->handlerRelease)
811 ProcessEventByScript(pChannel, itEvent, pChannel->pScript->handlerRelease);
812 break;
813 case Event::type_control_change:
814 case Event::type_channel_pressure:
815 case Event::type_pitchbend:
816 if (pChannel->pScript->handlerController)
817 ProcessEventByScript(pChannel, itEvent, pChannel->pScript->handlerController);
818 break;
819 case Event::type_note_pressure:
820 //TODO: ...
821 break;
822
823 case Event::type_sysex:
824 //TODO: ...
825 break;
826
827 case Event::type_cancel_release_key:
828 case Event::type_release_key:
829 case Event::type_release_note:
830 case Event::type_play_note:
831 case Event::type_stop_note:
832 case Event::type_kill_note:
833 case Event::type_note_synth_param:
834 break; // noop
835 }
836
837 // see HACK comment above
838 itEvent = itNext;
839 }
840
841 // this has to be run again, since the newly spawned scripts
842 // above may have cause suspended scripts that must be
843 // resumed within this same audio fragment cycle
844 //
845 // FIXME: see FIXME comment above
846 ProcessSuspendedScriptEvents(pChannel, fragmentEndTime);
847 }
848
849 // if there are any delayed events scheduled for the current
850 // audio fragment cycle, then move and sort them into the main
851 // event list
852 if (!pChannel->delayedEvents.queue.isEmpty()) {
853 dmsg(5,("Engine: There are delayed MIDI events (total queue size: %d) ...\n", pChannel->delayedEvents.queue.size()));
854 const sched_time_t fragmentEndTime = pEventGenerator->schedTimeAtCurrentFragmentEnd();
855 RTList<Event>::Iterator itEvent = pChannel->pEvents->first();
856 while (true) {
857 RTList<ScheduledEvent>::Iterator itDelayedEventNode =
858 pEventGenerator->popNextScheduledEvent(
859 pChannel->delayedEvents.queue,
860 pChannel->delayedEvents.schedulerNodes,
861 fragmentEndTime
862 );
863 if (!itDelayedEventNode) break;
864 // get the actual delayed event object and free the used scheduler node
865 RTList<Event>::Iterator itDelayedEvent = itDelayedEventNode->itEvent;
866 pChannel->delayedEvents.schedulerNodes.free(itDelayedEventNode);
867 if (!itDelayedEvent) { // should never happen, but just to be sure ...
868 dmsg(1,("Engine: Oops, invalid delayed event!\n"));
869 continue;
870 }
871 // skip all events on main event list which have a time
872 // before (or equal to) the delayed event to be inserted
873 for (; itEvent && itEvent->FragmentPos() <= itDelayedEvent->FragmentPos();
874 ++itEvent);
875 // now move delayed event from delayedEvents.pList to
876 // the current position on the main event list
877 itEvent = itDelayedEvent.moveBefore(itEvent);
878 dmsg(5,("Engine: Inserted event of type %d into main event list (queue size: %d).\n", itEvent->Type, pChannel->delayedEvents.queue.size()));
879 }
880 dmsg(5,("Engine: End of delayed events (total queue size: %d).\n", pChannel->delayedEvents.queue.size()));
881 }
882
883 // now process all events regularly
884 {
885 RTList<Event>::Iterator itEvent = pChannel->pEvents->first();
886 RTList<Event>::Iterator end = pChannel->pEvents->end();
887 for (; itEvent != end; ++itEvent) {
888 bool bIsCC = false; // just for resetting RPN/NRPN below
889 switch (itEvent->Type) {
890 case Event::type_note_on:
891 dmsg(5,("Engine: Note on received\n"));
892 ProcessNoteOn((EngineChannel*)itEvent->pEngineChannel, itEvent);
893 break;
894 case Event::type_play_note:
895 dmsg(5,("Engine: Play Note received\n"));
896 ProcessNoteOn((EngineChannel*)itEvent->pEngineChannel, itEvent);
897 break;
898 case Event::type_note_off:
899 dmsg(5,("Engine: Note off received\n"));
900 ProcessNoteOff((EngineChannel*)itEvent->pEngineChannel, itEvent);
901 break;
902 case Event::type_stop_note:
903 dmsg(5,("Engine: Stop Note received\n"));
904 ProcessNoteOff((EngineChannel*)itEvent->pEngineChannel, itEvent);
905 break;
906 case Event::type_kill_note:
907 dmsg(5,("Engine: Kill Note received\n"));
908 ProcessKillNote((EngineChannel*)itEvent->pEngineChannel, itEvent);
909 break;
910 case Event::type_control_change:
911 dmsg(5,("Engine: MIDI CC received\n"));
912 ProcessControlChange((EngineChannel*)itEvent->pEngineChannel, itEvent);
913 bIsCC = true;
914 break;
915 case Event::type_rpn: // this can only be reached here by an instrument script having called set_rpn()
916 dmsg(5,("Engine: MIDI RPN received\n"));
917 ProcessHardcodedRpn((EngineChannel*)itEvent->pEngineChannel, itEvent);
918 bIsCC = true;
919 break;
920 case Event::type_nrpn: // this can only be reached here by an instrument script having called set_nrpn()
921 dmsg(5,("Engine: MIDI NRPN received\n"));
922 ProcessHardcodedNrpn((EngineChannel*)itEvent->pEngineChannel, itEvent);
923 bIsCC = true;
924 break;
925 case Event::type_channel_pressure:
926 dmsg(5,("Engine: MIDI Chan. Pressure received\n"));
927 ProcessChannelPressure((EngineChannel*)itEvent->pEngineChannel, itEvent);
928 break;
929 case Event::type_note_pressure:
930 dmsg(5,("Engine: MIDI Note Pressure received\n"));
931 ProcessPolyphonicKeyPressure((EngineChannel*)itEvent->pEngineChannel, itEvent);
932 break;
933 case Event::type_pitchbend:
934 dmsg(5,("Engine: Pitchbend received\n"));
935 ProcessPitchbend(static_cast<AbstractEngineChannel*>(itEvent->pEngineChannel), itEvent);
936 break;
937 case Event::type_note_synth_param:
938 dmsg(5,("Engine: Note Synth Param received\n"));
939 ProcessNoteSynthParam(itEvent->pEngineChannel, itEvent);
940 break;
941 case Event::type_sysex:
942 break; // TODO ...
943
944 case Event::type_cancel_release_key:
945 case Event::type_release_key:
946 case Event::type_release_note:
947 break; // noop
948 }
949 // reset cached RPN/NRPN parameter number and data in
950 // case this event was not a control change event
951 if (!bIsCC) {
952 if (pChannel->GetMidiRpnParameter() >= 0)
953 pChannel->ResetMidiRpnParameter();
954 if (pChannel->GetMidiNrpnParameter() >= 0)
955 pChannel->ResetMidiNrpnParameter();
956 }
957 }
958 }
959
960 // reset voice stealing for the next engine channel (or next audio fragment)
961 itLastStolenVoice = VoiceIterator();
962 itLastStolenVoiceGlobally = VoiceIterator();
963 itLastStolenNote = NoteIterator();
964 itLastStolenNoteGlobally = NoteIterator();
965 iuiLastStolenKey = RTList<uint>::Iterator();
966 iuiLastStolenKeyGlobally = RTList<uint>::Iterator();
967 pLastStolenChannel = NULL;
968 }
969
970 /**
971 * Run all suspended script execution instances which are scheduled
972 * to be resumed for the current audio fragment cycle.
973 *
974 * @param pChannel - engine channel on which suspended events occurred
975 */
976 void ProcessSuspendedScriptEvents(AbstractEngineChannel* pChannel, const sched_time_t fragmentEndTime) {
977 while (true) {
978 RTList<ScriptEvent>::Iterator itEvent =
979 pEventGenerator->popNextScheduledScriptEvent(
980 pChannel->pScript->suspendedEvents,
981 *pChannel->pScript->pEvents, fragmentEndTime
982 );
983 if (!itEvent) break;
984 ResumeScriptEvent(pChannel, itEvent);
985 }
986 }
987
988 /** @brief Call instrument script's event handler for this event.
989 *
990 * Causes a new execution instance of the currently loaded real-time
991 * instrument script's event handler (callback) to be spawned for
992 * the given MIDI event.
993 *
994 * @param pChannel - engine channel on which the MIDI event occurred
995 * @param itEvent - MIDI event that causes this new script execution
996 * @param pEventHandler - script's event handler to be executed
997 */
998 void ProcessEventByScript(AbstractEngineChannel* pChannel, RTList<Event>::Iterator& itEvent, VMEventHandler* pEventHandler) {
999 const int key = itEvent->Param.Note.Key; // even if this is not a note on/off event, accessing it does not mean any harm
1000 // check if polyphonic data is passed from "note" to "release"
1001 // script event handlers
1002 if (pEventHandler == pChannel->pScript->handlerRelease &&
1003 pChannel->pScript->handlerNote &&
1004 pChannel->pScript->handlerNote->isPolyphonic() &&
1005 pChannel->pScript->handlerRelease->isPolyphonic() &&
1006 !pChannel->pScript->pKeyEvents[key]->isEmpty())
1007 {
1008 // polyphonic variable data is used/passed from "note" to
1009 // "release" script callback, so we have to recycle the
1010 // original "note on" script event(s)
1011 RTList<ScriptEvent>::Iterator it = pChannel->pScript->pKeyEvents[key]->first();
1012 RTList<ScriptEvent>::Iterator end = pChannel->pScript->pKeyEvents[key]->end();
1013 for (; it != end; ++it) {
1014 ProcessScriptEvent(
1015 pChannel, itEvent, pEventHandler, it
1016 );
1017 }
1018 } else {
1019 // no polyphonic data is used/passed from "note" to
1020 // "release" script callback, so just use a new fresh
1021 // script event object
1022 RTList<ScriptEvent>::Iterator itScriptEvent =
1023 pChannel->pScript->pEvents->allocAppend();
1024 // if event handler uses polyphonic variables, reset them
1025 // to zero values before starting to execute the handler
1026 if (pEventHandler->isPolyphonic())
1027 itScriptEvent->execCtx->resetPolyphonicData();
1028 ProcessScriptEvent(
1029 pChannel, itEvent, pEventHandler, itScriptEvent
1030 );
1031 }
1032 }
1033
1034 /** @brief Spawn new execution instance of an instrument script handler.
1035 *
1036 * Will be called to initiate a new execution of a real-time
1037 * instrument script event right from the start of the script's
1038 * respective handler. If script execution did not complete after
1039 * calling this method, the respective script exeuction is then
1040 * suspended and a call to ResumeScriptEvent() will be used next
1041 * time to continue its execution.
1042 *
1043 * @param pChannel - engine channel this script is running for
1044 * @param itEvent - event which caused execution of this script
1045 * event handler
1046 * @param pEventHandler - VM representation of event handler to be
1047 * executed
1048 * @param itScriptEvent - script event that shall be processed
1049 */
1050 void ProcessScriptEvent(AbstractEngineChannel* pChannel, RTList<Event>::Iterator& itEvent, VMEventHandler* pEventHandler, RTList<ScriptEvent>::Iterator& itScriptEvent) {
1051 if (!itScriptEvent) return; // not a valid script event (i.e. because no free script event was left in the script event pool)
1052
1053 // fill the list of script handlers to be executed by this event
1054 int i = 0;
1055 itScriptEvent->handlers[i++] = pEventHandler; // actual event handler (i.e. note, controller)
1056 itScriptEvent->handlers[i] = NULL; // NULL termination of list
1057
1058 // initialize/reset other members
1059 itScriptEvent->cause = *itEvent;
1060 itScriptEvent->scheduleTime = itEvent->SchedTime();
1061 itScriptEvent->currentHandler = 0;
1062 itScriptEvent->executionSlices = 0;
1063 itScriptEvent->ignoreAllWaitCalls = false;
1064 itScriptEvent->handlerType = pEventHandler->eventHandlerType();
1065 itScriptEvent->parentHandlerID = 0;
1066 itScriptEvent->childHandlerID[0] = 0;
1067 itScriptEvent->autoAbortByParent = false;
1068 itScriptEvent->forkIndex = 0;
1069 // this is the native representation of the $EVENT_ID script variable
1070 itScriptEvent->id =
1071 (itEvent->Type == Event::type_note_on)
1072 ? ScriptID::fromNoteID( itEvent->Param.Note.ID )
1073 : ScriptID::fromEventID( pEventPool->getID(itEvent) );
1074
1075 // run script handler(s)
1076 VMExecStatus_t res = pScriptVM->exec(
1077 pChannel->pScript->parserContext, &*itScriptEvent
1078 );
1079
1080 // was the script suspended?
1081 if (res & VM_EXEC_SUSPENDED) { // script was suspended ...
1082 // in case the script was suspended, keep it on the allocated
1083 // ScriptEvent list to be resume at the scheduled time in future,
1084 // additionally insert it into a sorted time queue
1085 pEventGenerator->scheduleAheadMicroSec(
1086 pChannel->pScript->suspendedEvents, // scheduler queue
1087 *itScriptEvent, // script event
1088 itScriptEvent->cause.FragmentPos(), // current time of script event (basis for its next execution)
1089 itScriptEvent->execCtx->suspensionTimeMicroseconds() // how long shall it be suspended
1090 );
1091 } else { // script execution has finished without 'suspended' status ...
1092 // if "polyphonic" variable data is passed from script's
1093 // "note" event handler to its "release" event handler, then
1094 // the script event must be kept and recycled for the later
1095 // occuring "release" script event ...
1096 if (pEventHandler == pChannel->pScript->handlerNote &&
1097 pChannel->pScript->handlerRelease &&
1098 pChannel->pScript->handlerNote->isPolyphonic() &&
1099 pChannel->pScript->handlerRelease->isPolyphonic())
1100 {
1101 const int key = itEvent->Param.Note.Key;
1102 itScriptEvent.moveToEndOf(pChannel->pScript->pKeyEvents[key & 127]);
1103 } else {
1104 // ... otherwise if no polyphonic data is passed and
1105 // script's execution has finished without suspension
1106 // status, then free the script event for a new future
1107 // script event to be triggered from start
1108 pChannel->pScript->pEvents->free(itScriptEvent);
1109 }
1110 }
1111 }
1112
1113 /** @brief Resume execution of instrument script.
1114 *
1115 * Will be called to resume execution of a real-time instrument
1116 * script event which has been suspended previously.
1117 *
1118 * Script execution might be suspended for various reasons. Usually
1119 * a script will be suspended if the script called the built-in
1120 * "wait()" function, but it might also be suspended automatically
1121 * if the script took too much execution time in an audio fragment
1122 * cycle. So in the latter case automatic suspension is performed in
1123 * order to avoid harm for the sampler's overall real-time
1124 * requirements.
1125 *
1126 * @param pChannel - engine channel this script is running for
1127 * @param itScriptEvent - script execution that shall be resumed
1128 */
1129 void ResumeScriptEvent(AbstractEngineChannel* pChannel, RTList<ScriptEvent>::Iterator& itScriptEvent) {
1130 VMEventHandler* handler = itScriptEvent->handlers[itScriptEvent->currentHandler];
1131
1132 // run script
1133 VMExecStatus_t res = pScriptVM->exec(
1134 pChannel->pScript->parserContext, &*itScriptEvent
1135 );
1136
1137 // was the script suspended?
1138 if (res & VM_EXEC_SUSPENDED) {
1139 // in case the script was suspended, keep it on the allocated
1140 // ScriptEvent list to be resume at the scheduled time in future,
1141 // additionally insert it into a sorted time queue
1142 pEventGenerator->scheduleAheadMicroSec(
1143 pChannel->pScript->suspendedEvents, // scheduler queue
1144 *itScriptEvent, // script event
1145 itScriptEvent->cause.FragmentPos(), // current time of script event (basis for its next execution)
1146 itScriptEvent->execCtx->suspensionTimeMicroseconds() // how long shall it be suspended
1147 );
1148 } else { // script execution has finished without 'suspended' status ...
1149 // if "polyphonic" variable data is passed from script's
1150 // "note" event handler to its "release" event handler, then
1151 // the script event must be kept and recycled for the later
1152 // occuring "release" script event ...
1153 if (handler && handler == pChannel->pScript->handlerNote &&
1154 pChannel->pScript->handlerRelease &&
1155 pChannel->pScript->handlerNote->isPolyphonic() &&
1156 pChannel->pScript->handlerRelease->isPolyphonic())
1157 {
1158 const int key = itScriptEvent->cause.Param.Note.Key;
1159 itScriptEvent.moveToEndOf(pChannel->pScript->pKeyEvents[key & 127]);
1160 } else {
1161 // ... otherwise if no polyphonic data is passed and
1162 // script's execution has finished without suspension
1163 // status, then free the script event for a new future
1164 // script event to be triggered from start
1165 pChannel->pScript->pEvents->free(itScriptEvent);
1166 }
1167 }
1168 }
1169
1170 /**
1171 * Will be called by LaunchVoice() method in case there are no free
1172 * voices left. This method will select and kill one old voice for
1173 * voice stealing and postpone the note-on event until the selected
1174 * voice actually died.
1175 *
1176 * @param pEngineChannel - engine channel on which this event occurred on
1177 * @param itNoteOnEvent - key, velocity and time stamp of the event
1178 * @returns 0 on success, a value < 0 if no active voice could be picked for voice stealing
1179 */
1180 int StealVoice(EngineChannel* pEngineChannel, Pool<Event>::Iterator& itNoteOnEvent) {
1181 dmsg(3,("StealVoice()\n"));
1182 if (VoiceSpawnsLeft <= 0) {
1183 dmsg(1,("Max. voice thefts per audio fragment reached (you may raise CONFIG_MAX_VOICES).\n"));
1184 return -1;
1185 }
1186
1187 EngineChannelBase<V, R, I>* pEngineChn = static_cast<EngineChannelBase<V, R, I>*>(pEngineChannel);
1188
1189 if (pEventPool->poolIsEmpty()) {
1190 dmsg(1,("Event pool emtpy!\n"));
1191 return -1;
1192 }
1193
1194 if (!pEngineChn->StealVoice(itNoteOnEvent, &itLastStolenVoice, &itLastStolenNote, &iuiLastStolenKey)) {
1195 --VoiceSpawnsLeft;
1196 return 0;
1197 }
1198
1199 // if we couldn't steal a voice from the same engine channel then
1200 // steal oldest voice on the oldest key from any other engine channel
1201 // (the smaller engine channel number, the higher priority)
1202 EngineChannelBase<V, R, I>* pSelectedChannel;
1203 int iChannelIndex;
1204 VoiceIterator itSelectedVoice;
1205
1206 #if CONFIG_DEVMODE
1207 EngineChannel* pBegin = NULL; // to detect endless loop
1208 #endif
1209
1210 // select engine channel
1211 if (pLastStolenChannel) {
1212 pSelectedChannel = pLastStolenChannel;
1213 iChannelIndex = pSelectedChannel->iEngineIndexSelf;
1214 } else { // pick the engine channel followed by this engine channel
1215 iChannelIndex = (pEngineChn->iEngineIndexSelf + 1) % engineChannels.size();
1216 pSelectedChannel = static_cast<EngineChannelBase<V, R, I>*>(engineChannels[iChannelIndex]);
1217 }
1218
1219 // if we already stole in this fragment, try to proceed on same note
1220 if (this->itLastStolenVoiceGlobally) {
1221 itSelectedVoice = this->itLastStolenVoiceGlobally;
1222 do {
1223 ++itSelectedVoice;
1224 } while (itSelectedVoice && !itSelectedVoice->IsStealable()); // proceed iterating if voice was created in this fragment cycle
1225 }
1226 // did we find a 'stealable' voice?
1227 if (itSelectedVoice && itSelectedVoice->IsStealable()) {
1228 // remember which voice we stole, so we can simply proceed on next voice stealing
1229 this->itLastStolenVoiceGlobally = itSelectedVoice;
1230 // done
1231 goto stealable_voice_found;
1232 }
1233
1234 // get (next) oldest note
1235 if (this->itLastStolenNoteGlobally) {
1236 for (NoteIterator itNote = ++this->itLastStolenNoteGlobally;
1237 itNote; ++itNote)
1238 {
1239 for (itSelectedVoice = itNote->pActiveVoices->first(); itSelectedVoice; ++itSelectedVoice) {
1240 // proceed iterating if voice was created in this audio fragment cycle
1241 if (itSelectedVoice->IsStealable()) {
1242 // remember which voice of which note we stole, so we can simply proceed on next voice stealing
1243 this->itLastStolenNoteGlobally = itNote;
1244 this->itLastStolenVoiceGlobally = itSelectedVoice;
1245 goto stealable_voice_found; // selection succeeded
1246 }
1247 }
1248 }
1249 }
1250
1251 #if CONFIG_DEVMODE
1252 pBegin = pSelectedChannel; // to detect endless loop
1253 #endif // CONFIG_DEVMODE
1254
1255 while (true) { // iterate through engine channels
1256 // get (next) oldest key
1257 RTList<uint>::Iterator iuiSelectedKey = (this->iuiLastStolenKeyGlobally) ? ++this->iuiLastStolenKeyGlobally : pSelectedChannel->pActiveKeys->first();
1258 this->iuiLastStolenKeyGlobally = RTList<uint>::Iterator(); // to prevent endless loop (see line above)
1259 while (iuiSelectedKey) {
1260 MidiKey* pSelectedKey = &pSelectedChannel->pMIDIKeyInfo[*iuiSelectedKey];
1261
1262 for (NoteIterator itNote = pSelectedKey->pActiveNotes->first(),
1263 itNotesEnd = pSelectedKey->pActiveNotes->end();
1264 itNote != itNotesEnd; ++itNote)
1265 {
1266 itSelectedVoice = itNote->pActiveVoices->first();
1267 // proceed iterating if voice was created in this fragment cycle
1268 while (itSelectedVoice && !itSelectedVoice->IsStealable()) ++itSelectedVoice;
1269 // found a "stealable" voice ?
1270 if (itSelectedVoice && itSelectedVoice->IsStealable()) {
1271 // remember which voice of which note on which key on which engine channel we stole, so we can simply proceed on next voice stealing
1272 this->iuiLastStolenKeyGlobally = iuiSelectedKey;
1273 this->itLastStolenNoteGlobally = itNote;
1274 this->itLastStolenVoiceGlobally = itSelectedVoice;
1275 this->pLastStolenChannel = pSelectedChannel;
1276 goto stealable_voice_found; // selection succeeded
1277 }
1278 }
1279 ++iuiSelectedKey; // get next key on current engine channel
1280 }
1281 // get next engine channel
1282 iChannelIndex = (iChannelIndex + 1) % engineChannels.size();
1283 pSelectedChannel = static_cast<EngineChannelBase<V, R, I>*>(engineChannels[iChannelIndex]);
1284
1285 #if CONFIG_DEVMODE
1286 if (pSelectedChannel == pBegin) {
1287 dmsg(1,("FATAL ERROR: voice stealing endless loop!\n"));
1288 dmsg(1,("VoiceSpawnsLeft=%d.\n", VoiceSpawnsLeft));
1289 dmsg(1,("Exiting.\n"));
1290 exit(-1);
1291 }
1292 #endif // CONFIG_DEVMODE
1293 }
1294
1295 // jump point if a 'stealable' voice was found
1296 stealable_voice_found:
1297
1298 #if CONFIG_DEVMODE
1299 if (!itSelectedVoice->IsActive()) {
1300 dmsg(1,("EngineBase: ERROR, tried to steal a voice which was not active !!!\n"));
1301 return -1;
1302 }
1303 #endif // CONFIG_DEVMODE
1304
1305 // now kill the selected voice
1306 itSelectedVoice->Kill(itNoteOnEvent);
1307
1308 --VoiceSpawnsLeft;
1309
1310 return 0; // success
1311 }
1312
1313 void HandleInstrumentChanges() {
1314 bool instrumentChanged = false;
1315 for (int i = 0; i < engineChannels.size(); i++) {
1316 EngineChannelBase<V, R, I>* pEngineChannel =
1317 static_cast<EngineChannelBase<V, R, I>*>(engineChannels[i]);
1318
1319 // as we're going to (carefully) write some status to the
1320 // synchronized struct, we cast away the const
1321 InstrumentChangeCmd<R, I>& cmd =
1322 const_cast<InstrumentChangeCmd<R, I>&>(pEngineChannel->InstrumentChangeCommandReader.Lock());
1323
1324 pEngineChannel->pRegionsInUse = cmd.pRegionsInUse;
1325 pEngineChannel->pRegionsInUse->clear();
1326
1327 if (cmd.bChangeInstrument) {
1328 // change instrument
1329 dmsg(5,("Engine: instrument change command received\n"));
1330 cmd.bChangeInstrument = false;
1331 pEngineChannel->pInstrument = cmd.pInstrument;
1332 pEngineChannel->pScript =
1333 cmd.pScript->bHasValidScript ? cmd.pScript : NULL;
1334 instrumentChanged = true;
1335
1336 pEngineChannel->MarkAllActiveVoicesAsOrphans();
1337
1338 // the script's "init" event handler is only executed
1339 // once (when the script is loaded or reloaded)
1340 if (pEngineChannel->pScript && pEngineChannel->pScript->handlerInit) {
1341 dmsg(5,("Engine: exec handlerInit %p\n", pEngineChannel->pScript->handlerInit));
1342 RTList<ScriptEvent>::Iterator itScriptEvent =
1343 pEngineChannel->pScript->pEvents->allocAppend();
1344
1345 itScriptEvent->cause = pEventGenerator->CreateEvent(0);
1346 itScriptEvent->cause.Type = (Event::type_t) -1; // some invalid type to avoid random event processing
1347 itScriptEvent->cause.pEngineChannel = pEngineChannel;
1348 itScriptEvent->cause.pMidiInputPort = pEngineChannel->GetMidiInputPort();
1349 itScriptEvent->id = 0;
1350 itScriptEvent->handlers[0] = pEngineChannel->pScript->handlerInit;
1351 itScriptEvent->handlers[1] = NULL;
1352 itScriptEvent->currentHandler = 0;
1353 itScriptEvent->executionSlices = 0;
1354 itScriptEvent->ignoreAllWaitCalls = false;
1355 itScriptEvent->handlerType = VM_EVENT_HANDLER_INIT;
1356 itScriptEvent->parentHandlerID = 0;
1357 itScriptEvent->childHandlerID[0] = 0;
1358 itScriptEvent->autoAbortByParent = false;
1359 itScriptEvent->forkIndex = 0;
1360
1361 VMExecStatus_t res;
1362 size_t instructionsCount = 0;
1363 const size_t maxInstructions = 200000; // aiming approx. 1 second max. (based on very roughly 5us / instruction)
1364 bool bWarningShown = false;
1365 do {
1366 res = pScriptVM->exec(
1367 pEngineChannel->pScript->parserContext, &*itScriptEvent
1368 );
1369 instructionsCount += itScriptEvent->execCtx->instructionsPerformed();
1370 if (instructionsCount > maxInstructions && !bWarningShown) {
1371 bWarningShown = true;
1372 dmsg(0,("[ScriptVM] WARNING: \"init\" event handler of instrument script executing for long time!\n"));
1373 }
1374 } while (res & VM_EXEC_SUSPENDED && !(res & VM_EXEC_ERROR));
1375
1376 pEngineChannel->pScript->pEvents->free(itScriptEvent);
1377 }
1378 }
1379 }
1380
1381 if (instrumentChanged) {
1382 //TODO: this is a lazy solution ATM and not safe in case somebody is currently editing the instrument we're currently switching to (we should store all suspended regions on instrument manager side and when switching to another instrument copy that list to the engine's local list of suspensions
1383 ResetSuspendedRegions();
1384 }
1385 }
1386
1387 /**
1388 * Render all 'normal' voices (that is voices which were not stolen in
1389 * this fragment) on the given engine channel.
1390 *
1391 * @param pEngineChannel - engine channel on which audio should be
1392 * rendered
1393 * @param Samples - amount of sample points to be rendered in
1394 * this audio fragment cycle
1395 */
1396 void RenderActiveVoices(EngineChannel* pEngineChannel, uint Samples) {
1397 #if !CONFIG_PROCESS_MUTED_CHANNELS
1398 if (pEngineChannel->GetMute()) return; // skip if sampler channel is muted
1399 #endif
1400
1401 EngineChannelBase<V, R, I>* pChannel =
1402 static_cast<EngineChannelBase<V, R, I>*>(pEngineChannel);
1403 pChannel->RenderActiveVoices(Samples);
1404
1405 ActiveVoiceCountTemp += pEngineChannel->GetVoiceCount();
1406 }
1407
1408 /**
1409 * Render all stolen voices (only voices which were stolen in this
1410 * fragment) on the given engine channel. Stolen voices are rendered
1411 * after all normal voices have been rendered; this is needed to render
1412 * audio of those voices which were selected for voice stealing until
1413 * the point were the stealing (that is the take over of the voice)
1414 * actually happened.
1415 *
1416 * @param pEngineChannel - engine channel on which audio should be
1417 * rendered
1418 * @param Samples - amount of sample points to be rendered in
1419 * this audio fragment cycle
1420 */
1421 void RenderStolenVoices(uint Samples) {
1422 RTList<Event>::Iterator itVoiceStealEvent = pVoiceStealingQueue->first();
1423 RTList<Event>::Iterator end = pVoiceStealingQueue->end();
1424 for (; itVoiceStealEvent != end; ++itVoiceStealEvent) {
1425 EngineChannelBase<V, R, I>* pEngineChannel =
1426 static_cast<EngineChannelBase<V, R, I>*>(itVoiceStealEvent->pEngineChannel);;
1427 if (!pEngineChannel->pInstrument) continue; // ignore if no instrument loaded
1428
1429 PoolVoiceIterator itNewVoice =
1430 LaunchVoice(pEngineChannel, itVoiceStealEvent, itVoiceStealEvent->Param.Note.Layer, itVoiceStealEvent->Param.Note.ReleaseTrigger, false, false);
1431 if (itNewVoice) {
1432 // usually there should already be a new Note object
1433 NoteIterator itNote = GetNotePool()->fromID(itVoiceStealEvent->Param.Note.ID);
1434 if (!itNote) { // should not happen, but just to be sure ...
1435 dmsg(2,("Engine: No Note object for stolen voice!\n"));
1436 const note_id_t noteID = LaunchNewNote(pEngineChannel, itVoiceStealEvent);
1437 if (!noteID) {
1438 dmsg(1,("Engine: Voice stealing failed; No Note object and Note pool empty!\n"));
1439 continue;
1440 }
1441 itNote = GetNotePool()->fromID(noteID);
1442 }
1443 // move voice from whereever it was, to the new note's list of active voices
1444 itNewVoice = itNewVoice.moveToEndOf(itNote->pActiveVoices);
1445 // render audio of this new voice for the first time
1446 itNewVoice->Render(Samples);
1447 if (itNewVoice->IsActive()) { // still active
1448 *(pEngineChannel->pRegionsInUse->allocAppend()) = itNewVoice->GetRegion();
1449 ActiveVoiceCountTemp++;
1450 pEngineChannel->SetVoiceCount(pEngineChannel->GetVoiceCount() + 1);
1451
1452 if (itNewVoice->PlaybackState == Voice::playback_state_disk) {
1453 if (itNewVoice->DiskStreamRef.State != Stream::state_unused) {
1454 pEngineChannel->SetDiskStreamCount(pEngineChannel->GetDiskStreamCount() + 1);
1455 }
1456 }
1457 } else { // voice reached end, is now inactive
1458 pEngineChannel->FreeVoice(itNewVoice); // remove voice from the list of active voices
1459 }
1460 }
1461 else dmsg(1,("EngineBase: ERROR, voice stealing didn't work out!\n"));
1462
1463 // we need to clear the key's event list explicitly here in case key was never active
1464 MidiKey* pKey = &pEngineChannel->pMIDIKeyInfo[itVoiceStealEvent->Param.Note.Key];
1465 pKey->VoiceTheftsQueued--;
1466 if (!pKey->Active && !pKey->VoiceTheftsQueued) pKey->pEvents->clear();
1467 }
1468 }
1469
1470 /**
1471 * Free all keys which have turned inactive in this audio fragment, from
1472 * the list of active keys and clear all event lists on that engine
1473 * channel.
1474 *
1475 * @param pEngineChannel - engine channel to cleanup
1476 */
1477 void PostProcess(EngineChannel* pEngineChannel) {
1478 EngineChannelBase<V, R, I>* pChannel =
1479 static_cast<EngineChannelBase<V, R, I>*>(pEngineChannel);
1480 pChannel->FreeAllInactiveKeys();
1481
1482 // empty the engine channel's own event lists
1483 // (only events of the current audio fragment cycle)
1484 pChannel->ClearEventListsOfCurrentFragment();
1485 }
1486
1487 /**
1488 * Process MIDI control change events with hard coded behavior,
1489 * that is controllers whose behavior is defined independently
1490 * of the actual sampler engine type and instrument.
1491 *
1492 * @param pEngineChannel - engine channel on which the MIDI CC event was received
1493 * @param itControlChangeEvent - the actual MIDI CC event
1494 */
1495 void ProcessHardcodedControllers (
1496 EngineChannel* pEngineChannel,
1497 Pool<Event>::Iterator& itControlChangeEvent
1498 ) {
1499 EngineChannelBase<V, R, I>* pChannel =
1500 static_cast<EngineChannelBase<V, R, I>*>(pEngineChannel);
1501
1502 // will be set to true if this CC event has anything to do with RPN/NRPN
1503 bool bIsRpn = false, bIsNrpn = false;
1504
1505 switch (itControlChangeEvent->Param.CC.Controller) {
1506 case 5: { // portamento time
1507 pChannel->PortamentoTime = (float) itControlChangeEvent->Param.CC.Value / 127.0f * (float) CONFIG_PORTAMENTO_TIME_MAX + (float) CONFIG_PORTAMENTO_TIME_MIN;
1508 break;
1509 }
1510 case 6: { // data entry (MSB)
1511 //dmsg(1,("DATA ENTRY MSB %d\n", itControlChangeEvent->Param.CC.Value));
1512 if (pChannel->GetMidiRpnParameter() >= 0) { // RPN parameter number was sent previously ...
1513 pChannel->SetMidiRpnDataMsb(
1514 itControlChangeEvent->Param.CC.Value
1515 );
1516 bIsRpn = true;
1517
1518 // look-ahead: if next MIDI event is data entry LSB,
1519 // then skip this event here for now (to avoid double
1520 // handling of what's supposed to be one RPN event)
1521 if (isNextEventCCNr(itControlChangeEvent, 38))
1522 break;
1523
1524 int ch = itControlChangeEvent->Param.CC.Channel;
1525 int param = pChannel->GetMidiRpnParameter();
1526 int value = pChannel->GetMidiRpnData();
1527
1528 // transform event type: CC event -> RPN event
1529 itControlChangeEvent->Type = Event::type_rpn;
1530 itControlChangeEvent->Param.RPN.Channel = ch;
1531 itControlChangeEvent->Param.RPN.Parameter = param;
1532 itControlChangeEvent->Param.RPN.Value = value;
1533
1534 // if there's a RPN script handler, run it ...
1535 if (pChannel->pScript->handlerRpn) {
1536 const event_id_t eventID =
1537 pEventPool->getID(itControlChangeEvent);
1538 // run the RPN script handler
1539 ProcessEventByScript(
1540 pChannel, itControlChangeEvent,
1541 pChannel->pScript->handlerRpn
1542 );
1543 // if RPN event was dropped by script, abort
1544 // here to avoid hard coded RPN processing below
1545 if (!pEventPool->fromID(eventID))
1546 break;
1547 }
1548
1549 // do the actual (hard-coded) RPN value change processing
1550 ProcessHardcodedRpn(pEngineChannel, itControlChangeEvent);
1551
1552 } else if (pChannel->GetMidiNrpnParameter() >= 0) { // NRPN parameter number was sent previously ...
1553 pChannel->SetMidiNrpnDataMsb(
1554 itControlChangeEvent->Param.CC.Value
1555 );
1556 bIsNrpn = true;
1557
1558 // look-ahead: if next MIDI event is data entry LSB,
1559 // then skip this event here for now (to avoid double
1560 // handling of what's supposed to be one NRPN event)
1561 if (isNextEventCCNr(itControlChangeEvent, 38))
1562 break;
1563
1564 int ch = itControlChangeEvent->Param.CC.Channel;
1565 int param = pChannel->GetMidiNrpnParameter();
1566 int value = pChannel->GetMidiNrpnData();
1567
1568 // transform event type: CC event -> NRPN event
1569 itControlChangeEvent->Type = Event::type_nrpn;
1570 itControlChangeEvent->Param.NRPN.Channel = ch;
1571 itControlChangeEvent->Param.NRPN.Parameter = param;
1572 itControlChangeEvent->Param.NRPN.Value = value;
1573
1574 // if there's a NRPN script handler, run it ...
1575 if (pChannel->pScript->handlerNrpn) {
1576 const event_id_t eventID =
1577 pEventPool->getID(itControlChangeEvent);
1578 // run the NRPN script handler
1579 ProcessEventByScript(
1580 pChannel, itControlChangeEvent,
1581 pChannel->pScript->handlerNrpn
1582 );
1583 // if NRPN event was dropped by script, abort
1584 // here to avoid hard coded NRPN processing below
1585 if (!pEventPool->fromID(eventID))
1586 break;
1587 }
1588
1589 // do the actual (hard-coded) NRPN value change processing
1590 ProcessHardcodedNrpn(pEngineChannel, itControlChangeEvent);
1591 }
1592 break;
1593 }
1594 case 7: { // volume
1595 //TODO: not sample accurate yet
1596 pChannel->MidiVolume = VolumeCurve[itControlChangeEvent->Param.CC.Value];
1597 pChannel->bStatusChanged = true; // engine channel status has changed, so set notify flag
1598 break;
1599 }
1600 case 10: { // panpot
1601 //TODO: not sample accurate yet
1602 pChannel->iLastPanRequest = itControlChangeEvent->Param.CC.Value;
1603 break;
1604 }
1605 case 38: { // data entry (LSB)
1606 //dmsg(1,("DATA ENTRY LSB %d\n", itControlChangeEvent->Param.CC.Value));
1607 if (pChannel->GetMidiRpnParameter() >= 0) { // RPN parameter number was sent previously ...
1608 pChannel->SetMidiRpnDataLsb(
1609 itControlChangeEvent->Param.CC.Value
1610 );
1611 bIsRpn = true;
1612
1613 int ch = itControlChangeEvent->Param.CC.Channel;
1614 int param = pChannel->GetMidiRpnParameter();
1615 int value = pChannel->GetMidiRpnData();
1616
1617 // transform event type: CC event -> RPN event
1618 itControlChangeEvent->Type = Event::type_rpn;
1619 itControlChangeEvent->Param.RPN.Channel = ch;
1620 itControlChangeEvent->Param.RPN.Parameter = param;
1621 itControlChangeEvent->Param.RPN.Value = value;
1622
1623 // if there's a RPN script handler, run it ...
1624 if (pChannel->pScript->handlerRpn) {
1625 const event_id_t eventID =
1626 pEventPool->getID(itControlChangeEvent);
1627 // run the RPN script handler
1628 ProcessEventByScript(
1629 pChannel, itControlChangeEvent,
1630 pChannel->pScript->handlerRpn
1631 );
1632 // if RPN event was dropped by script, abort
1633 // here to avoid hard coded RPN processing below
1634 if (!pEventPool->fromID(eventID))
1635 break;
1636 }
1637
1638 // do the actual (hard-coded) RPN value change processing
1639 ProcessHardcodedRpn(pEngineChannel, itControlChangeEvent);
1640
1641 } else if (pChannel->GetMidiNrpnParameter() >= 0) { // NRPN parameter number was sent previously ...
1642 pChannel->SetMidiNrpnDataLsb(
1643 itControlChangeEvent->Param.CC.Value
1644 );
1645 bIsNrpn = true;
1646
1647 int ch = itControlChangeEvent->Param.CC.Channel;
1648 int param = pChannel->GetMidiNrpnParameter();
1649 int value = pChannel->GetMidiNrpnData();
1650
1651 // transform event type: CC event -> NRPN event
1652 itControlChangeEvent->Type = Event::type_nrpn;
1653 itControlChangeEvent->Param.NRPN.Channel = ch;
1654 itControlChangeEvent->Param.NRPN.Parameter = param;
1655 itControlChangeEvent->Param.NRPN.Value = value;
1656
1657 // if there's a NRPN script handler, run it ...
1658 if (pChannel->pScript->handlerNrpn) {
1659 const event_id_t eventID =
1660 pEventPool->getID(itControlChangeEvent);
1661 // run the NRPN script handler
1662 ProcessEventByScript(
1663 pChannel, itControlChangeEvent,
1664 pChannel->pScript->handlerNrpn
1665 );
1666 // if NRPN event was dropped by script, abort
1667 // here to avoid hard coded NRPN processing below
1668 if (!pEventPool->fromID(eventID))
1669 break;
1670 }
1671
1672 // do the actual (hard-coded) NRPN value change processing
1673 ProcessHardcodedNrpn(pEngineChannel, itControlChangeEvent);
1674 }
1675 break;
1676 }
1677 case 64: { // sustain
1678 if (itControlChangeEvent->Param.CC.Value >= 64 && !pChannel->SustainPedal) {
1679 dmsg(4,("DAMPER (RIGHT) PEDAL DOWN\n"));
1680 pChannel->SustainPedal = true;
1681 pChannel->listeners.PreProcessSustainPedalDown();
1682
1683 #if !CONFIG_PROCESS_MUTED_CHANNELS
1684 if (pEngineChannel->GetMute()) { // skip if sampler channel is muted
1685 pChannel->listeners.PostProcessSustainPedalDown();
1686 return;
1687 }
1688 #endif
1689
1690 pChannel->ProcessSustainPedalDown(itControlChangeEvent);
1691 pChannel->listeners.PostProcessSustainPedalDown();
1692 }
1693 if (itControlChangeEvent->Param.CC.Value < 64 && pChannel->SustainPedal) {
1694 dmsg(4,("DAMPER (RIGHT) PEDAL UP\n"));
1695 pChannel->SustainPedal = false;
1696 pChannel->listeners.PreProcessSustainPedalUp();
1697
1698 #if !CONFIG_PROCESS_MUTED_CHANNELS
1699 if (pChannel->GetMute()) { // skip if sampler channel is muted
1700 pChannel->listeners.PostProcessSustainPedalUp();
1701 return;
1702 }
1703 #endif
1704
1705 pChannel->ProcessSustainPedalUp(itControlChangeEvent);
1706 pChannel->listeners.PostProcessSustainPedalUp();
1707 }
1708 break;
1709 }
1710 case 65: { // portamento on / off
1711 const bool bPortamento = itControlChangeEvent->Param.CC.Value >= 64;
1712 if (bPortamento != pChannel->PortamentoMode)
1713 KillAllVoices(pChannel, itControlChangeEvent);
1714 pChannel->PortamentoMode = bPortamento;
1715 break;
1716 }
1717 case 66: { // sostenuto
1718 if (itControlChangeEvent->Param.CC.Value >= 64 && !pChannel->SostenutoPedal) {
1719 dmsg(4,("SOSTENUTO (CENTER) PEDAL DOWN\n"));
1720 pChannel->SostenutoPedal = true;
1721 pChannel->listeners.PreProcessSostenutoPedalDown();
1722
1723 #if !CONFIG_PROCESS_MUTED_CHANNELS
1724 if (pEngineChannel->GetMute()) { // skip if sampler channel is muted
1725 pChannel->listeners.PostProcessSostenutoPedalDown();
1726 return;
1727 }
1728 #endif
1729
1730 pChannel->ProcessSostenutoPedalDown();
1731 pChannel->listeners.PostProcessSostenutoPedalDown();
1732 }
1733 if (itControlChangeEvent->Param.CC.Value < 64 && pChannel->SostenutoPedal) {
1734 dmsg(4,("SOSTENUTO (CENTER) PEDAL UP\n"));
1735 pChannel->SostenutoPedal = false;
1736 pChannel->listeners.PreProcessSostenutoPedalUp();
1737
1738 #if !CONFIG_PROCESS_MUTED_CHANNELS
1739 if (pEngineChannel->GetMute()) { // skip if sampler channel is muted
1740 pChannel->listeners.PostProcessSostenutoPedalUp();
1741 return;
1742 }
1743 #endif
1744
1745 pChannel->ProcessSostenutoPedalUp(itControlChangeEvent);
1746 pChannel->listeners.PostProcessSostenutoPedalUp();
1747 }
1748 break;
1749 }
1750 case 96: { // data increment (data entry +1)
1751 //dmsg(1,("DATA INC\n"));
1752 if (pChannel->GetMidiRpnParameter() >= 0) { // RPN parameter number was sent previously ...
1753 pChannel->SetMidiRpnData(
1754 pChannel->GetMidiRpnData() + 1
1755 );
1756 bIsRpn = true;
1757
1758 int ch = itControlChangeEvent->Param.CC.Channel;
1759 int param = pChannel->GetMidiRpnParameter();
1760 int value = pChannel->GetMidiRpnData();
1761
1762 // transform event type: CC event -> RPN event
1763 itControlChangeEvent->Type = Event::type_rpn;
1764 itControlChangeEvent->Param.RPN.Channel = ch;
1765 itControlChangeEvent->Param.RPN.Parameter = param;
1766 itControlChangeEvent->Param.RPN.Value = value;
1767
1768 // if there's a RPN script handler, run it ...
1769 if (pChannel->pScript->handlerRpn) {
1770 const event_id_t eventID =
1771 pEventPool->getID(itControlChangeEvent);
1772 // run the RPN script handler
1773 ProcessEventByScript(
1774 pChannel, itControlChangeEvent,
1775 pChannel->pScript->handlerRpn
1776 );
1777 // if RPN event was dropped by script, abort
1778 // here to avoid hard coded RPN processing below
1779 if (!pEventPool->fromID(eventID))
1780 break;
1781 }
1782
1783 // do the actual (hard-coded) RPN value change processing
1784 ProcessHardcodedRpn(pEngineChannel, itControlChangeEvent);
1785
1786 } else if (pChannel->GetMidiNrpnParameter() >= 0) { // NRPN parameter number was sent previously ...
1787 pChannel->SetMidiNrpnData(
1788 pChannel->GetMidiNrpnData() + 1
1789 );
1790 bIsNrpn = true;
1791
1792 int ch = itControlChangeEvent->Param.CC.Channel;
1793 int param = pChannel->GetMidiNrpnParameter();
1794 int value = pChannel->GetMidiNrpnData();
1795
1796 // transform event type: CC event -> NRPN event
1797 itControlChangeEvent->Type = Event::type_nrpn;
1798 itControlChangeEvent->Param.NRPN.Channel = ch;
1799 itControlChangeEvent->Param.NRPN.Parameter = param;
1800 itControlChangeEvent->Param.NRPN.Value = value;
1801
1802 // if there's a NRPN script handler, run it ...
1803 if (pChannel->pScript->handlerNrpn) {
1804 const event_id_t eventID =
1805 pEventPool->getID(itControlChangeEvent);
1806 // run the NRPN script handler
1807 ProcessEventByScript(
1808 pChannel, itControlChangeEvent,
1809 pChannel->pScript->handlerNrpn
1810 );
1811 // if NRPN event was dropped by script, abort
1812 // here to avoid hard coded NRPN processing below
1813 if (!pEventPool->fromID(eventID))
1814 break;
1815 }
1816
1817 // do the actual (hard-coded) NRPN value change processing
1818 ProcessHardcodedNrpn(pEngineChannel, itControlChangeEvent);
1819 }
1820 break;
1821 }
1822 case 97: { // data decrement (data entry -1)
1823 //dmsg(1,("DATA DEC\n"));
1824 if (pChannel->GetMidiRpnParameter() >= 0) { // RPN parameter number was sent previously ...
1825 pChannel->SetMidiRpnData(
1826 pChannel->GetMidiRpnData() - 1
1827 );
1828 bIsRpn = true;
1829
1830 int ch = itControlChangeEvent->Param.CC.Channel;
1831 int param = pChannel->GetMidiRpnParameter();
1832 int value = pChannel->GetMidiRpnData();
1833
1834 // transform event type: CC event -> RPN event
1835 itControlChangeEvent->Type = Event::type_rpn;
1836 itControlChangeEvent->Param.RPN.Channel = ch;
1837 itControlChangeEvent->Param.RPN.Parameter = param;
1838 itControlChangeEvent->Param.RPN.Value = value;
1839
1840 // if there's a RPN script handler, run it ...
1841 if (pChannel->pScript->handlerRpn) {
1842 const event_id_t eventID =
1843 pEventPool->getID(itControlChangeEvent);
1844 // run the RPN script handler
1845 ProcessEventByScript(
1846 pChannel, itControlChangeEvent,
1847 pChannel->pScript->handlerRpn
1848 );
1849 // if RPN event was dropped by script, abort
1850 // here to avoid hard coded RPN processing below
1851 if (!pEventPool->fromID(eventID))
1852 break;
1853 }
1854
1855 // do the actual (hard-coded) RPN value change processing
1856 ProcessHardcodedRpn(pEngineChannel, itControlChangeEvent);
1857
1858 } else if (pChannel->GetMidiNrpnParameter() >= 0) { // NRPN parameter number was sent previously ...
1859 pChannel->SetMidiNrpnData(
1860 pChannel->GetMidiNrpnData() - 1
1861 );
1862 bIsNrpn = true;
1863
1864 int ch = itControlChangeEvent->Param.CC.Channel;
1865 int param = pChannel->GetMidiNrpnParameter();
1866 int value = pChannel->GetMidiNrpnData();
1867
1868 // transform event type: CC event -> NRPN event
1869 itControlChangeEvent->Type = Event::type_nrpn;
1870 itControlChangeEvent->Param.NRPN.Channel = ch;
1871 itControlChangeEvent->Param.NRPN.Parameter = param;
1872 itControlChangeEvent->Param.NRPN.Value = value;
1873
1874 // if there's a NRPN script handler, run it ...
1875 if (pChannel->pScript->handlerNrpn) {
1876 const event_id_t eventID =
1877 pEventPool->getID(itControlChangeEvent);
1878 // run the NRPN script handler
1879 ProcessEventByScript(
1880 pChannel, itControlChangeEvent,
1881 pChannel->pScript->handlerNrpn
1882 );
1883 // if NRPN event was dropped by script, abort
1884 // here to avoid hard coded NRPN processing below
1885 if (!pEventPool->fromID(eventID))
1886 break;
1887 }
1888
1889 // do the actual (hard-coded) NRPN value change processing
1890 ProcessHardcodedNrpn(pEngineChannel, itControlChangeEvent);
1891 }
1892 break;
1893 }
1894 case 98: { // NRPN parameter LSB
1895 dmsg(4,("NRPN LSB %d\n", itControlChangeEvent->Param.CC.Value));
1896 bIsNrpn = true;
1897 pEngineChannel->SetMidiNrpnParameterLsb(itControlChangeEvent->Param.CC.Value);
1898 break;
1899 }
1900 case 99: { // NRPN parameter MSB
1901 dmsg(4,("NRPN MSB %d\n", itControlChangeEvent->Param.CC.Value));
1902 bIsNrpn = true;
1903 pEngineChannel->SetMidiNrpnParameterMsb(itControlChangeEvent->Param.CC.Value);
1904 break;
1905 }
1906 case 100: { // RPN parameter LSB
1907 dmsg(4,("RPN LSB %d\n", itControlChangeEvent->Param.CC.Value));
1908 bIsRpn = true;
1909 pEngineChannel->SetMidiRpnParameterLsb(itControlChangeEvent->Param.CC.Value);
1910 break;
1911 }
1912 case 101: { // RPN parameter MSB
1913 dmsg(4,("RPN MSB %d\n", itControlChangeEvent->Param.CC.Value));
1914 bIsRpn = true;
1915 pEngineChannel->SetMidiRpnParameterMsb(itControlChangeEvent->Param.CC.Value);
1916 break;
1917 }
1918
1919
1920 // Channel Mode Messages
1921
1922 case 120: { // all sound off
1923 KillAllVoices(pEngineChannel, itControlChangeEvent);
1924 break;
1925 }
1926 case 121: { // reset all controllers
1927 pChannel->ResetControllers();
1928 break;
1929 }
1930 case 123: { // all notes off
1931 #if CONFIG_PROCESS_ALL_NOTES_OFF
1932 pChannel->ReleaseAllVoices(itControlChangeEvent);
1933 #endif // CONFIG_PROCESS_ALL_NOTES_OFF
1934 break;
1935 }
1936 case 126: { // mono mode on
1937 if (!pChannel->SoloMode)
1938 KillAllVoices(pEngineChannel, itControlChangeEvent);
1939 pChannel->SoloMode = true;
1940 break;
1941 }
1942 case 127: { // poly mode on
1943 if (pChannel->SoloMode)
1944 KillAllVoices(pEngineChannel, itControlChangeEvent);
1945 pChannel->SoloMode = false;
1946 break;
1947 }
1948 }
1949
1950 // reset cached RPN/NRPN parameter number and data in case this
1951 // CC event had nothing to do with RPN/NRPN
1952 if (!bIsRpn && pChannel->GetMidiRpnParameter() >= 0)
1953 pChannel->ResetMidiRpnParameter();
1954 if (!bIsNrpn && pChannel->GetMidiNrpnParameter() >= 0)
1955 pChannel->ResetMidiNrpnParameter();
1956 }
1957
1958 /**
1959 * Process MIDI RPN events with hard coded behavior.
1960 *
1961 * @param pEngineChannel - engine channel on which the MIDI RPN
1962 * event was received
1963 * @param itRpnEvent - the actual MIDI RPN event
1964 */
1965 void ProcessHardcodedRpn(EngineChannel* pEngineChannel,
1966 Pool<Event>::Iterator& itRpnEvent)
1967 {
1968 EngineChannelBase<V, R, I>* pChannel =
1969 static_cast<EngineChannelBase<V, R, I>*>(pEngineChannel);
1970
1971 if (itRpnEvent->Param.RPN.Parameter == 2) { // coarse tuning in half tones
1972 int transpose = (int) itRpnEvent->Param.RPN.ValueMSB() - 64;
1973 // limit to +- two octaves for now
1974 transpose = RTMath::Min(transpose, 24);
1975 transpose = RTMath::Max(transpose, -24);
1976 pChannel->GlobalTranspose = transpose;
1977 // workaround, so we won't have hanging notes
1978 pChannel->ReleaseAllVoices(itRpnEvent);
1979 }
1980 }
1981
1982 /**
1983 * Process MIDI NRPN events with hard coded behavior.
1984 *
1985 * @param pEngineChannel - engine channel on which the MIDI NRPN
1986 * event was received
1987 * @param itRpnEvent - the actual MIDI NRPN event
1988 */
1989 void ProcessHardcodedNrpn(EngineChannel* pEngineChannel,
1990 Pool<Event>::Iterator& itNrpnEvent)
1991 {
1992 EngineChannelBase<V, R, I>* pChannel =
1993 static_cast<EngineChannelBase<V, R, I>*>(pEngineChannel);
1994
1995 switch (itNrpnEvent->Param.NRPN.ParameterMSB()) {
1996 case 0x1a: { // volume level of note (Roland GS NRPN)
1997 const uint note = itNrpnEvent->Param.NRPN.ParameterLSB();
1998 const uint vol = itNrpnEvent->Param.NRPN.ValueMSB();
1999 dmsg(4,("Note Volume NRPN received (note=%d,vol=%d).\n", note, vol));
2000 if (note < 128 && vol < 128)
2001 pChannel->pMIDIKeyInfo[note].Volume = VolumeCurve[vol];
2002 break;
2003 }
2004 case 0x1c: { // panpot of note (Roland GS NRPN)
2005 const uint note = itNrpnEvent->Param.NRPN.ParameterLSB();
2006 const uint pan = itNrpnEvent->Param.NRPN.ValueMSB();
2007 dmsg(4,("Note Pan NRPN received (note=%d,pan=%d).\n", note, pan));
2008 if (note < 128 && pan < 128) {
2009 pChannel->pMIDIKeyInfo[note].PanLeft = PanCurve[128 - pan];
2010 pChannel->pMIDIKeyInfo[note].PanRight = PanCurve[pan];
2011 }
2012 break;
2013 }
2014 case 0x1d: { // reverb send of note (Roland GS NRPN)
2015 const uint note = itNrpnEvent->Param.NRPN.ParameterLSB();
2016 const float reverb = float(itNrpnEvent->Param.NRPN.Value) / 16383.f;
2017 dmsg(4,("Note Reverb Send NRPN received (note=%d,send=%f).\n", note, reverb));
2018 if (note < 128)
2019 pChannel->pMIDIKeyInfo[note].ReverbSend = reverb;
2020 break;
2021 }
2022 case 0x1e: { // chorus send of note (Roland GS NRPN)
2023 const uint note = itNrpnEvent->Param.NRPN.ParameterLSB();
2024 const float chorus = float(itNrpnEvent->Param.NRPN.Value) / 16383.f;
2025 dmsg(4,("Note Chorus Send NRPN received (note=%d,send=%f).\n", note, chorus));
2026 if (note < 128)
2027 pChannel->pMIDIKeyInfo[note].ChorusSend = chorus;
2028 break;
2029 }
2030 }
2031 }
2032
2033 virtual D* CreateDiskThread() = 0;
2034
2035 /**
2036 * Assigns and triggers a new voice for the respective MIDI key.
2037 *
2038 * @param pEngineChannel - engine channel on which this event occurred on
2039 * @param itNoteOnEvent - key, velocity and time stamp of the event
2040 */
2041 virtual void ProcessNoteOn(EngineChannel* pEngineChannel, Pool<Event>::Iterator& itNoteOnEvent) OVERRIDE {
2042 EngineChannelBase<V, R, I>* pChannel =
2043 static_cast<EngineChannelBase<V, R, I>*>(pEngineChannel);
2044
2045 const int key = itNoteOnEvent->Param.Note.Key;
2046 const int vel = itNoteOnEvent->Param.Note.Velocity;
2047 if (key < 0 || key > 127) return; // ignore event, key outside allowed key range
2048
2049 MidiKey* pKey = &pChannel->pMIDIKeyInfo[key];
2050
2051 // There are real MIDI note-on events (Event::type_note_on) and
2052 // programmatically spawned notes (Event::type_play_note). We have
2053 // to distinguish between them, since certain processing below
2054 // must only be done on real MIDI note-on events (i.e. for
2055 // correctly updating which MIDI keys are currently pressed down).
2056 const bool isRealMIDINoteOnEvent = itNoteOnEvent->Type == Event::type_note_on;
2057
2058 if (isRealMIDINoteOnEvent)
2059 pChannel->listeners.PreProcessNoteOn(key, vel);
2060
2061 #if !CONFIG_PROCESS_MUTED_CHANNELS
2062 if (pEngineChannel->GetMute()) { // skip if sampler channel is muted
2063 if (isRealMIDINoteOnEvent)
2064 pChannel->listeners.PostProcessNoteOn(key, vel);
2065 return;
2066 }
2067 #endif
2068
2069 if (!pChannel->pInstrument) {
2070 if (isRealMIDINoteOnEvent)
2071 pChannel->listeners.PostProcessNoteOn(key, vel);
2072 return; // ignore if no instrument loaded
2073 }
2074
2075 // move note on event to the key's own event list
2076 RTList<Event>::Iterator itNoteOnEventOnKeyList = itNoteOnEvent.moveToEndOf(pKey->pEvents);
2077
2078 // if Solo Mode then kill all already active voices
2079 if (pChannel->SoloMode && isRealMIDINoteOnEvent) {
2080 Pool<uint>::Iterator itYoungestKey = pChannel->pActiveKeys->last();
2081 if (itYoungestKey) {
2082 const int iYoungestKey = *itYoungestKey;
2083 const MidiKey* pOtherKey = &pChannel->pMIDIKeyInfo[iYoungestKey];
2084 if (pOtherKey->Active) {
2085 // get final portamento position of currently active voice
2086 if (pChannel->PortamentoMode) {
2087 NoteIterator itNote = pOtherKey->pActiveNotes->last();
2088 if (itNote) {
2089 VoiceIterator itVoice = itNote->pActiveVoices->last();
2090 if (itVoice) itVoice->UpdatePortamentoPos(itNoteOnEventOnKeyList);
2091 }
2092 }
2093 // kill all voices on the (other) key
2094 for (NoteIterator itNote = pOtherKey->pActiveNotes->first(); itNote; ++itNote) {
2095 VoiceIterator itVoiceToBeKilled = itNote->pActiveVoices->first();
2096 VoiceIterator end = itNote->pActiveVoices->end();
2097 for (; itVoiceToBeKilled != end; ++itVoiceToBeKilled) {
2098 if (!(itVoiceToBeKilled->Type & Voice::type_release_trigger))
2099 itVoiceToBeKilled->Kill(itNoteOnEventOnKeyList);
2100 }
2101 }
2102 }
2103 }
2104 // set this key as 'currently active solo key'
2105 pChannel->SoloKey = key;
2106 }
2107
2108 if (isRealMIDINoteOnEvent) {
2109 pChannel->ProcessKeySwitchChange(key);
2110
2111 pKey->KeyPressed = true; // the MIDI key was now pressed down
2112 pChannel->KeyDown[key] = true; // just used as built-in %KEY_DOWN script variable
2113 pKey->Velocity = itNoteOnEventOnKeyList->Param.Note.Velocity;
2114 pKey->NoteOnTime = FrameTime + itNoteOnEventOnKeyList->FragmentPos(); // will be used to calculate note length
2115 }
2116
2117 // cancel release process of voices on this key if needed
2118 if (pKey->Active && !pChannel->SustainPedal && isRealMIDINoteOnEvent) {
2119 RTList<Event>::Iterator itCancelReleaseEvent = pKey->pEvents->allocAppend();
2120 if (itCancelReleaseEvent) {
2121 *itCancelReleaseEvent = *itNoteOnEventOnKeyList; // copy event
2122 itCancelReleaseEvent->Type = Event::type_cancel_release_key; // transform event type
2123 }
2124 else dmsg(1,("Event pool emtpy!\n"));
2125 }
2126
2127 TriggerNewVoices(pEngineChannel, itNoteOnEventOnKeyList);
2128
2129 // if neither a voice was spawned or postponed then remove note on event from key again
2130 if (!pKey->Active && !pKey->VoiceTheftsQueued)
2131 pKey->pEvents->free(itNoteOnEventOnKeyList);
2132
2133 if (isRealMIDINoteOnEvent && (!pChannel->SoloMode || pChannel->PortamentoPos < 0.0f))
2134 pChannel->PortamentoPos = (float) key;
2135
2136 //NOTE: Hmm, I guess its a matter of taste whether round robin should be advanced only on real MIDI note-on events, isn't it?
2137 if (pKey->pRoundRobinIndex) {
2138 (*pKey->pRoundRobinIndex)++; // counter specific for the key or region
2139 pChannel->RoundRobinIndex++; // common counter for the channel
2140 }
2141
2142 if (isRealMIDINoteOnEvent)
2143 pChannel->listeners.PostProcessNoteOn(key, vel);
2144 }
2145
2146 /**
2147 * Allocate and trigger new voice(s) for the key.
2148 */
2149 virtual void TriggerNewVoices (
2150 EngineChannel* pEngineChannel,
2151 RTList<Event>::Iterator& itNoteOnEvent,
2152 bool HandleKeyGroupConflicts = true
2153 ) = 0;
2154
2155 /**
2156 * Allocate and trigger release voice(s) for the key.
2157 */
2158 virtual void TriggerReleaseVoices (
2159 EngineChannel* pEngineChannel,
2160 RTList<Event>::Iterator& itNoteOffEvent
2161 ) = 0;
2162
2163 /**
2164 * Releases the voices on the given key if sustain pedal is not pressed.
2165 * If sustain is pressed, the release of the note will be postponed until
2166 * sustain pedal will be released or voice turned inactive by itself (e.g.
2167 * due to completion of sample playback).
2168 *
2169 * @param pEngineChannel - engine channel on which this event occurred on
2170 * @param itNoteOffEvent - key, velocity and time stamp of the event
2171 */
2172 virtual void ProcessNoteOff(EngineChannel* pEngineChannel, Pool<Event>::Iterator& itNoteOffEvent) OVERRIDE {
2173 EngineChannelBase<V, R, I>* pChannel = static_cast<EngineChannelBase<V, R, I>*>(pEngineChannel);
2174
2175 const int iKey = itNoteOffEvent->Param.Note.Key;
2176 const int vel = itNoteOffEvent->Param.Note.Velocity;
2177 if (iKey < 0 || iKey > 127) return; // ignore event, key outside allowed key range
2178
2179 MidiKey* pKey = &pChannel->pMIDIKeyInfo[iKey];
2180
2181 // There are real MIDI note-off events (Event::type_note_off) and
2182 // programmatically spawned notes (Event::type_stop_note). We have
2183 // to distinguish between them, since certain processing below
2184 // must only be done on real MIDI note-off events (i.e. for
2185 // correctly updating which MIDI keys are currently pressed down),
2186 // plus a stop-note event just releases voices of one particular
2187 // note, whereas a note-off event releases all voices on a
2188 // particular MIDI key instead.
2189 const bool isRealMIDINoteOffEvent = itNoteOffEvent->Type == Event::type_note_off;
2190
2191 if (isRealMIDINoteOffEvent)
2192 pChannel->listeners.PreProcessNoteOff(iKey, vel);
2193
2194 #if !CONFIG_PROCESS_MUTED_CHANNELS
2195 if (pEngineChannel->GetMute()) { // skip if sampler channel is muted
2196 if (isRealMIDINoteOffEvent)
2197 pChannel->listeners.PostProcessNoteOff(iKey, vel);
2198 return;
2199 }
2200 #endif
2201
2202 if (isRealMIDINoteOffEvent) {
2203 pKey->KeyPressed = false; // the MIDI key was now released
2204 pChannel->KeyDown[iKey] = false; // just used as built-in %KEY_DOWN script variable
2205 }
2206
2207 // move event to the key's own event list
2208 RTList<Event>::Iterator itNoteOffEventOnKeyList = itNoteOffEvent.moveToEndOf(pKey->pEvents);
2209
2210 if (isRealMIDINoteOffEvent) {
2211 bool bShouldRelease = pKey->Active && pChannel->ShouldReleaseVoice(itNoteOffEventOnKeyList->Param.Note.Key);
2212
2213 // in case Solo Mode is enabled, kill all voices on this key and respawn a voice on the highest pressed key (if any)
2214 if (pChannel->SoloMode && pChannel->pInstrument) { //TODO: this feels like too much code just for handling solo mode :P
2215 bool bOtherKeysPressed = false;
2216 if (iKey == pChannel->SoloKey) {
2217 pChannel->SoloKey = -1;
2218 // if there's still a key pressed down, respawn a voice (group) on the highest key
2219 for (int i = 127; i > 0; i--) {
2220 MidiKey* pOtherKey = &pChannel->pMIDIKeyInfo[i];
2221 if (pOtherKey->KeyPressed) {
2222 bOtherKeysPressed = true;
2223 // make the other key the new 'currently active solo key'
2224 pChannel->SoloKey = i;
2225 // get final portamento position of currently active voice
2226 if (pChannel->PortamentoMode) {
2227 NoteIterator itNote = pKey->pActiveNotes->first();
2228 VoiceIterator itVoice = itNote->pActiveVoices->first();
2229 if (itVoice) itVoice->UpdatePortamentoPos(itNoteOffEventOnKeyList);
2230 }
2231 // create a pseudo note on event
2232 RTList<Event>::Iterator itPseudoNoteOnEvent = pOtherKey->pEvents->allocAppend();
2233 if (itPseudoNoteOnEvent) {
2234 // copy event
2235 *itPseudoNoteOnEvent = *itNoteOffEventOnKeyList;
2236 // transform event to a note on event
2237 itPseudoNoteOnEvent->Type = Event::type_note_on; //FIXME: should probably use Event::type_play_note instead (to avoid i.e. hanging notes)
2238 itPseudoNoteOnEvent->Param.Note.Key = i;
2239 itPseudoNoteOnEvent->Param.Note.Velocity = pOtherKey->Velocity;
2240 // assign a new note to this note-on event
2241 if (LaunchNewNote(pChannel, itPseudoNoteOnEvent)) {
2242 // allocate and trigger new voice(s) for the other key
2243 TriggerNewVoices(pChannel, itPseudoNoteOnEvent, false);
2244 }
2245 // if neither a voice was spawned or postponed then remove note on event from key again
2246 if (!pOtherKey->Active && !pOtherKey->VoiceTheftsQueued)
2247 pOtherKey->pEvents->free(itPseudoNoteOnEvent);
2248
2249 } else dmsg(1,("Could not respawn voice, no free event left\n"));
2250 break; // done
2251 }
2252 }
2253 }
2254 if (bOtherKeysPressed) {
2255 if (pKey->Active) { // kill all voices on this key
2256 bShouldRelease = false; // no need to release, as we kill it here
2257 for (NoteIterator itNote = pKey->pActiveNotes->first(); itNote; ++itNote) {
2258 VoiceIterator itVoiceToBeKilled = itNote->pActiveVoices->first();
2259 VoiceIterator end = itNote->pActiveVoices->end();
2260 for (; itVoiceToBeKilled != end; ++itVoiceToBeKilled) {
2261 if (!(itVoiceToBeKilled->Type & Voice::type_release_trigger))
2262 itVoiceToBeKilled->Kill(itNoteOffEventOnKeyList);
2263 }
2264 }
2265 }
2266 } else pChannel->PortamentoPos = -1.0f;
2267 }
2268
2269 // if no solo mode (the usual case) or if solo mode and no other key pressed, then release voices on this key if needed
2270 if (bShouldRelease) {
2271 itNoteOffEventOnKeyList->Type = Event::type_release_key; // transform event type
2272 // spawn release triggered voice(s) if needed
2273 if (pKey->ReleaseTrigger & release_trigger_noteoff)
2274 ProcessReleaseTrigger(pChannel, itNoteOffEventOnKeyList, pKey);
2275 }
2276 } else if (itNoteOffEventOnKeyList->Type == Event::type_stop_note) {
2277 // This programmatically caused event is caused by a call to
2278 // the built-in instrument script function note_off(). In
2279 // contrast to a real MIDI note-off event the stop-note
2280 // event just intends to release voices of one particular note.
2281 NoteBase* pNote = pChannel->pEngine->NoteByID( itNoteOffEventOnKeyList->Param.Note.ID );
2282 if (pNote) { // the requested note is still alive ...
2283 itNoteOffEventOnKeyList->Type = Event::type_release_note; // transform event type
2284 } else { // note is dead and gone ..
2285 pKey->pEvents->free(itNoteOffEventOnKeyList); // remove stop-note event from key again
2286 return; // prevent event to be removed a 2nd time below
2287 }
2288 }
2289
2290 // if neither a voice was spawned or postponed on this key then remove note off event from key again
2291 if (!pKey->Active && !pKey->VoiceTheftsQueued)
2292 pKey->pEvents->free(itNoteOffEventOnKeyList);
2293
2294 if (isRealMIDINoteOffEvent)
2295 pChannel->listeners.PostProcessNoteOff(iKey, vel);
2296 }
2297
2298 /**
2299 * Called on sustain pedal up events to check and if required,
2300 * launch release trigger voices on the respective active key.
2301 *
2302 * @param pEngineChannel - engine channel on which this event occurred on
2303 * @param itEvent - release trigger event (contains note number)
2304 */
2305 virtual void ProcessReleaseTriggerBySustain(EngineChannel* pEngineChannel, RTList<Event>::Iterator& itEvent) OVERRIDE {
2306 EngineChannelBase<V, R, I>* pChannel = static_cast<EngineChannelBase<V, R, I>*>(pEngineChannel);
2307
2308 const int iKey = itEvent->Param.Note.Key;
2309 if (iKey < 0 || iKey > 127) return; // ignore event, key outside allowed key range
2310
2311 MidiKey* pKey = &pChannel->pMIDIKeyInfo[iKey];
2312
2313 ProcessReleaseTrigger(pChannel, itEvent, pKey);
2314 }
2315
2316 /**
2317 * Called on note-off and sustain pedal up events to check and if
2318 * required, launch release trigger voices on the respective active
2319 * key.
2320 *
2321 * @param pEngineChannel - engine channel on which this event occurred on
2322 * @param itEvent - note off event / release trigger event
2323 * @param pKey - key on which the release trigger voices shall be spawned
2324 */
2325 inline void ProcessReleaseTrigger(EngineChannelBase<V, R, I>* pChannel, RTList<Event>::Iterator& itEvent, MidiKey* pKey) {
2326 // spawn release triggered voice(s) if needed
2327 if (pKey->ReleaseTrigger && pChannel->pInstrument) {
2328 // assign a new note to this release event
2329 if (LaunchNewNote(pChannel, itEvent)) {
2330 // allocate and trigger new release voice(s)
2331 TriggerReleaseVoices(pChannel, itEvent);
2332 }
2333 pKey->ReleaseTrigger = release_trigger_none;
2334 }
2335 }
2336
2337 /**
2338 * Called on "kill note" events, which currently only happens on
2339 * built-in real-time instrument script function fade_out(). This
2340 * method only fulfills one task: moving the even to the Note's own
2341 * event list so that its voices can process the kill event sample
2342 * accurately.
2343 */
2344 void ProcessKillNote(EngineChannel* pEngineChannel, RTList<Event>::Iterator& itEvent) {
2345 EngineChannelBase<V, R, I>* pChannel = static_cast<EngineChannelBase<V, R, I>*>(pEngineChannel);
2346
2347 NoteBase* pNote = pChannel->pEngine->NoteByID( itEvent->Param.Note.ID );
2348 if (!pNote || pNote->hostKey < 0 || pNote->hostKey >= 128) return;
2349
2350 // move note kill event to its MIDI key
2351 MidiKey* pKey = &pChannel->pMIDIKeyInfo[pNote->hostKey];
2352 itEvent.moveToEndOf(pKey->pEvents);
2353 }
2354
2355 /**
2356 * Called on note synthesis parameter change events. These are
2357 * internal events caused by calling built-in real-time instrument
2358 * script functions like change_vol(), change_tune(), etc.
2359 *
2360 * This method performs two tasks:
2361 *
2362 * - It converts the event's relative values changes (Deltas) to
2363 * the respective final new synthesis parameter value (AbsValue),
2364 * for that particular moment of the event that is.
2365 *
2366 * - It moves the individual events to the Note's own event list
2367 * (or actually to the event list of the MIDI key), so that
2368 * voices can process those events sample accurately.
2369 *
2370 * @param pEngineChannel - engine channel on which this event occurred on
2371 * @param itEvent - note synthesis parameter change event
2372 */
2373 virtual void ProcessNoteSynthParam(EngineChannel* pEngineChannel, RTList<Event>::Iterator& itEvent) {
2374 EngineChannelBase<V, R, I>* pChannel = static_cast<EngineChannelBase<V, R, I>*>(pEngineChannel);
2375
2376 NoteBase* pNote = pChannel->pEngine->NoteByID( itEvent->Param.NoteSynthParam.NoteID );
2377 if (!pNote || pNote->hostKey < 0 || pNote->hostKey >= 128) return;
2378
2379 switch (itEvent->Param.NoteSynthParam.Type) {
2380 case Event::synth_param_volume:
2381 pNote->apply(itEvent, &NoteBase::_Override::Volume);
2382 break;
2383 case Event::synth_param_volume_time:
2384 pNote->Override.VolumeTime = itEvent->Param.NoteSynthParam.AbsValue = itEvent->Param.NoteSynthParam.Delta;
2385 break;
2386 case Event::synth_param_volume_curve:
2387 itEvent->Param.NoteSynthParam.AbsValue = itEvent->Param.NoteSynthParam.Delta;
2388 pNote->Override.VolumeCurve = (fade_curve_t) itEvent->Param.NoteSynthParam.AbsValue;
2389 break;
2390 case Event::synth_param_pitch:
2391 pNote->apply(itEvent, &NoteBase::_Override::Pitch);
2392 break;
2393 case Event::synth_param_pitch_time:
2394 pNote->Override.PitchTime = itEvent->Param.NoteSynthParam.AbsValue = itEvent->Param.NoteSynthParam.Delta;
2395 break;
2396 case Event::synth_param_pitch_curve:
2397 itEvent->Param.NoteSynthParam.AbsValue = itEvent->Param.NoteSynthParam.Delta;
2398 pNote->Override.PitchCurve = (fade_curve_t) itEvent->Param.NoteSynthParam.AbsValue;
2399 break;
2400 case Event::synth_param_pan:
2401 pNote->apply(itEvent, &NoteBase::_Override::Pan);
2402 break;
2403 case Event::synth_param_pan_time:
2404 pNote->Override.PanTime = itEvent->Param.NoteSynthParam.AbsValue = itEvent->Param.NoteSynthParam.Delta;
2405 break;
2406 case Event::synth_param_pan_curve:
2407 itEvent->Param.NoteSynthParam.AbsValue = itEvent->Param.NoteSynthParam.Delta;
2408 pNote->Override.PanCurve = (fade_curve_t) itEvent->Param.NoteSynthParam.AbsValue;
2409 break;
2410 case Event::synth_param_cutoff:
2411 pNote->apply(itEvent, &NoteBase::_Override::Cutoff);
2412 break;
2413 case Event::synth_param_resonance:
2414 pNote->apply(itEvent, &NoteBase::_Override::Resonance);
2415 break;
2416 case Event::synth_param_attack:
2417 pNote->apply(itEvent, &NoteBase::_Override::Attack);
2418 break;
2419 case Event::synth_param_decay:
2420 pNote->apply(itEvent, &NoteBase::_Override::Decay);
2421 break;
2422 case Event::synth_param_sustain:
2423 pNote->apply(itEvent, &NoteBase::_Override::Sustain);
2424 break;
2425 case Event::synth_param_release:
2426 pNote->apply(itEvent, &NoteBase::_Override::Release);
2427 break;
2428
2429 case Event::synth_param_cutoff_attack:
2430 pNote->apply(itEvent, &NoteBase::_Override::CutoffAttack);
2431 break;
2432 case Event::synth_param_cutoff_decay:
2433 pNote->apply(itEvent, &NoteBase::_Override::CutoffDecay);
2434 break;
2435 case Event::synth_param_cutoff_sustain:
2436 pNote->apply(itEvent, &NoteBase::_Override::CutoffSustain);
2437 break;
2438 case Event::synth_param_cutoff_release:
2439 pNote->apply(itEvent, &NoteBase::_Override::CutoffRelease);
2440 break;
2441
2442 case Event::synth_param_amp_lfo_depth:
2443 pNote->apply(itEvent, &NoteBase::_Override::AmpLFODepth);
2444 break;
2445 case Event::synth_param_amp_lfo_freq:
2446 pNote->apply(itEvent, &NoteBase::_Override::AmpLFOFreq);
2447 break;
2448 case Event::synth_param_cutoff_lfo_depth:
2449 pNote->apply(itEvent, &NoteBase::_Override::CutoffLFODepth);
2450 break;
2451 case Event::synth_param_cutoff_lfo_freq:
2452 pNote->apply(itEvent, &NoteBase::_Override::CutoffLFOFreq);
2453 break;
2454 case Event::synth_param_pitch_lfo_depth:
2455 pNote->apply(itEvent, &NoteBase::_Override::PitchLFODepth);
2456 break;
2457 case Event::synth_param_pitch_lfo_freq:
2458 pNote->apply(itEvent, &NoteBase::_Override::PitchLFOFreq);
2459 break;
2460 }
2461
2462 // move note parameter event to its MIDI key
2463 MidiKey* pKey = &pChannel->pMIDIKeyInfo[pNote->hostKey];
2464 itEvent.moveToEndOf(pKey->pEvents);
2465 }
2466
2467 /**
2468 * Reset all voices and disk thread and clear input event queue and all
2469 * control and status variables. This method is protected by a mutex.
2470 */
2471 virtual void ResetInternal() OVERRIDE {
2472 LockGuard lock(ResetInternalMutex);
2473
2474 // make sure that the engine does not get any sysex messages
2475 // while it's reseting
2476 bool sysexDisabled = MidiInputPort::RemoveSysexListener(this);
2477 SetVoiceCount(0);
2478 ActiveVoiceCountMax = 0;
2479
2480 // reset voice stealing parameters
2481 pVoiceStealingQueue->clear();
2482 itLastStolenVoice = VoiceIterator();
2483 itLastStolenVoiceGlobally = VoiceIterator();
2484 itLastStolenNote = NoteIterator();
2485 itLastStolenNoteGlobally = NoteIterator();
2486 iuiLastStolenKey = RTList<uint>::Iterator();
2487 iuiLastStolenKeyGlobally = RTList<uint>::Iterator();
2488 pLastStolenChannel = NULL;
2489
2490 // reset all notes
2491 pNotePool->clear();
2492 for (NoteIterator itNote = pNotePool->allocAppend(); itNote;
2493 itNote = pNotePool->allocAppend())
2494 {
2495 itNote->reset();
2496 }
2497 pNotePool->clear();
2498
2499 // reset all voices
2500 pVoicePool->clear();
2501 for (VoiceIterator iterVoice = pVoicePool->allocAppend(); iterVoice == pVoicePool->last(); iterVoice = pVoicePool->allocAppend()) {
2502 iterVoice->Reset();
2503 }
2504 pVoicePool->clear();
2505
2506 // reset all engine channels
2507 for (int i = 0; i < engineChannels.size(); i++) {
2508 AbstractEngineChannel* pEngineChannel =
2509 static_cast<AbstractEngineChannel*>(engineChannels[i]);
2510 pEngineChannel->ResetInternal(false/*don't reset engine*/);
2511 }
2512
2513 // reset disk thread
2514 if (pDiskThread) pDiskThread->Reset();
2515
2516 // delete all input events
2517 pEventQueue->init();
2518 pSysexBuffer->init();
2519 if (sysexDisabled) MidiInputPort::AddSysexListener(this);
2520 }
2521
2522 /**
2523 * Kills all voices on an engine channel as soon as possible. Voices
2524 * won't get into release state, their volume level will be ramped down
2525 * as fast as possible.
2526 *
2527 * @param pEngineChannel - engine channel on which all voices should be killed
2528 * @param itKillEvent - event which caused this killing of all voices
2529 */
2530 virtual void KillAllVoices(EngineChannel* pEngineChannel, Pool<Event>::Iterator& itKillEvent) OVERRIDE {
2531 EngineChannelBase<V, R, I>* pChannel = static_cast<EngineChannelBase<V, R, I>*>(pEngineChannel);
2532 int count = pChannel->KillAllVoices(itKillEvent);
2533 VoiceSpawnsLeft -= count; //FIXME: just a temporary workaround, we should check the cause in StealVoice() instead
2534 }
2535
2536 /**
2537 * Allocates and triggers a new voice. This method will usually be
2538 * called by the ProcessNoteOn() method and by the voices itself
2539 * (e.g. to spawn further voices on the same key for layered sounds).
2540 *
2541 * @param pEngineChannel - engine channel on which this event occurred on
2542 * @param itNoteOnEvent - key, velocity and time stamp of the event
2543 * @param iLayer - layer index for the new voice (optional - only
2544 * in case of layered sounds of course)
2545 * @param ReleaseTriggerVoice - if new voice is a release triggered voice
2546 * (optional, default = false)
2547 * @param VoiceStealing - if voice stealing should be performed
2548 * when there is no free voice
2549 * (optional, default = true)
2550 * @param HandleKeyGroupConflicts - if voices should be killed due to a
2551 * key group conflict
2552 * @returns pointer to new voice or NULL if there was no free voice or
2553 * if the voice wasn't triggered (for example when no region is
2554 * defined for the given key).
2555 */
2556 virtual PoolVoiceIterator LaunchVoice (
2557 EngineChannel* pEngineChannel,
2558 Pool<Event>::Iterator& itNoteOnEvent,
2559 int iLayer,
2560 bool ReleaseTriggerVoice,
2561 bool VoiceStealing,
2562 bool HandleKeyGroupConflicts
2563 ) = 0;
2564
2565 virtual int GetMinFadeOutSamples() OVERRIDE { return MinFadeOutSamples; }
2566
2567 int InitNewVoice (
2568 EngineChannelBase<V, R, I>* pChannel,
2569 R* pRegion,
2570 Pool<Event>::Iterator& itNoteOnEvent,
2571 Voice::type_t VoiceType,
2572 int iLayer,
2573 int iKeyGroup,
2574 bool ReleaseTriggerVoice,
2575 bool VoiceStealing,
2576 typename Pool<V>::Iterator& itNewVoice
2577 ) {
2578 int key = itNoteOnEvent->Param.Note.Key;
2579 typename MidiKeyboardManager<V>::MidiKey* pKey = &pChannel->pMIDIKeyInfo[key];
2580 if (itNewVoice) {
2581 // launch the new voice
2582 if (itNewVoice->Trigger(pChannel, itNoteOnEvent, pChannel->Pitch, pRegion, VoiceType, iKeyGroup) < 0) {
2583 dmsg(4,("Voice not triggered\n"));
2584 GetVoicePool()->free(itNewVoice);
2585 }
2586 else { // on success
2587 --VoiceSpawnsLeft;
2588
2589 // should actually be superfluous now, since this is
2590 // already done in LaunchNewNote()
2591 pChannel->markKeyAsActive(pKey);
2592
2593 if (itNewVoice->Type & Voice::type_release_trigger_required)
2594 pKey->ReleaseTrigger |= itNewVoice->GetReleaseTriggerFlags(); // mark key for the need of release triggered voice(s)
2595 return 0; // success
2596 }
2597 }
2598 else if (VoiceStealing) {
2599 // try to steal one voice
2600 int result = StealVoice(pChannel, itNoteOnEvent);
2601 if (!result) { // voice stolen successfully
2602 // put note-on event into voice-stealing queue, so it will be reprocessed after killed voice died
2603 RTList<Event>::Iterator itStealEvent = pVoiceStealingQueue->allocAppend();
2604 if (itStealEvent) {
2605 *itStealEvent = *itNoteOnEvent; // copy event
2606 itStealEvent->Param.Note.Layer = iLayer;
2607 itStealEvent->Param.Note.ReleaseTrigger = ReleaseTriggerVoice;
2608 pKey->VoiceTheftsQueued++;
2609 }
2610 else dmsg(1,("Voice stealing queue full!\n"));
2611 }
2612 }
2613
2614 return -1;
2615 }
2616
2617 /**
2618 * Checks whether scale tuning setting has been changed since last
2619 * time this method was called, if yes, it recalculates the pitch
2620 * for all active voices.
2621 */
2622 void ProcessScaleTuningChange() {
2623 const bool changed = ScaleTuningChanged.readAndReset();
2624 if (!changed) return;
2625
2626 for (int i = 0; i < engineChannels.size(); i++) {
2627 EngineChannelBase<V, R, I>* channel =
2628 static_cast<EngineChannelBase<V, R, I>*>(engineChannels[i]);
2629 channel->OnScaleTuningChanged();
2630 }
2631 }
2632
2633 private:
2634 Pool< Note<V> >* pNotePool;
2635 Pool<note_id_t> noteIDPool;
2636 Pool<V>* pVoicePool; ///< Contains all voices that can be activated.
2637 Pool<RR*> SuspendedRegions;
2638 Mutex SuspendedRegionsMutex;
2639 Condition SuspensionChangeOngoing;
2640 RR* pPendingRegionSuspension;
2641 RR* pPendingRegionResumption;
2642 int iPendingStreamDeletions;
2643 };
2644
2645 template <class V, class RR, class R, class D, class IM, class I>
2646 IM EngineBase<V, RR, R, D, IM, I>::instruments;
2647
2648 } // namespace LinuxSampler
2649
2650 #endif /* __LS_ENGINEBASE_H__ */
2651

  ViewVC Help
Powered by ViewVC