/[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 3704 - (show annotations) (download) (as text)
Wed Jan 8 20:20:46 2020 UTC (9 months, 2 weeks ago) by schoenebeck
File MIME type: text/x-c++hdr
File size: 147333 byte(s)
Fixed missing case warnings.

1 /***************************************************************************
2 * *
3 * LinuxSampler - modular, streaming capable sampler *
4 * *
5 * Copyright (C) 2003,2004 by Benno Senoner and Christian Schoenebeck *
6 * Copyright (C) 2005-2020 Christian Schoenebeck *
7 * Copyright (C) 2009-2012 Grigor Iliev *
8 * Copyright (C) 2012-2017 Andreas Persson *
9 * *
10 * This program is free software; you can redistribute it and/or modify *
11 * it under the terms of the GNU General Public License as published by *
12 * the Free Software Foundation; either version 2 of the License, or *
13 * (at your option) any later version. *
14 * *
15 * This program is distributed in the hope that it will be useful, *
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
18 * GNU General Public License for more details. *
19 * *
20 * You should have received a copy of the GNU General Public License *
21 * along with this program; if not, write to the Free Software *
22 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, *
23 * MA 02111-1307 USA *
24 ***************************************************************************/
25
26 #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_rpn: // rpn handled in ProcessHardcodedControllers() instead ATM
828 case Event::type_nrpn: // nrpn handled in ProcessHardcodedControllers() instead ATM
829 case Event::type_cancel_release_key:
830 case Event::type_release_key:
831 case Event::type_release_note:
832 case Event::type_play_note:
833 case Event::type_stop_note:
834 case Event::type_kill_note:
835 case Event::type_note_synth_param:
836 break; // noop
837 }
838
839 // see HACK comment above
840 itEvent = itNext;
841 }
842
843 // this has to be run again, since the newly spawned scripts
844 // above may have cause suspended scripts that must be
845 // resumed within this same audio fragment cycle
846 //
847 // FIXME: see FIXME comment above
848 ProcessSuspendedScriptEvents(pChannel, fragmentEndTime);
849 }
850
851 // if there are any delayed events scheduled for the current
852 // audio fragment cycle, then move and sort them into the main
853 // event list
854 if (!pChannel->delayedEvents.queue.isEmpty()) {
855 dmsg(5,("Engine: There are delayed MIDI events (total queue size: %d) ...\n", pChannel->delayedEvents.queue.size()));
856 const sched_time_t fragmentEndTime = pEventGenerator->schedTimeAtCurrentFragmentEnd();
857 RTList<Event>::Iterator itEvent = pChannel->pEvents->first();
858 while (true) {
859 RTList<ScheduledEvent>::Iterator itDelayedEventNode =
860 pEventGenerator->popNextScheduledEvent(
861 pChannel->delayedEvents.queue,
862 pChannel->delayedEvents.schedulerNodes,
863 fragmentEndTime
864 );
865 if (!itDelayedEventNode) break;
866 // get the actual delayed event object and free the used scheduler node
867 RTList<Event>::Iterator itDelayedEvent = itDelayedEventNode->itEvent;
868 pChannel->delayedEvents.schedulerNodes.free(itDelayedEventNode);
869 if (!itDelayedEvent) { // should never happen, but just to be sure ...
870 dmsg(1,("Engine: Oops, invalid delayed event!\n"));
871 continue;
872 }
873 // skip all events on main event list which have a time
874 // before (or equal to) the delayed event to be inserted
875 for (; itEvent && itEvent->FragmentPos() <= itDelayedEvent->FragmentPos();
876 ++itEvent);
877 // now move delayed event from delayedEvents.pList to
878 // the current position on the main event list
879 itEvent = itDelayedEvent.moveBefore(itEvent);
880 dmsg(5,("Engine: Inserted event of type %d into main event list (queue size: %d).\n", itEvent->Type, pChannel->delayedEvents.queue.size()));
881 }
882 dmsg(5,("Engine: End of delayed events (total queue size: %d).\n", pChannel->delayedEvents.queue.size()));
883 }
884
885 // now process all events regularly
886 {
887 RTList<Event>::Iterator itEvent = pChannel->pEvents->first();
888 RTList<Event>::Iterator end = pChannel->pEvents->end();
889 for (; itEvent != end; ++itEvent) {
890 bool bIsCC = false; // just for resetting RPN/NRPN below
891 switch (itEvent->Type) {
892 case Event::type_note_on:
893 dmsg(5,("Engine: Note on received\n"));
894 ProcessNoteOn((EngineChannel*)itEvent->pEngineChannel, itEvent);
895 break;
896 case Event::type_play_note:
897 dmsg(5,("Engine: Play Note received\n"));
898 ProcessNoteOn((EngineChannel*)itEvent->pEngineChannel, itEvent);
899 break;
900 case Event::type_note_off:
901 dmsg(5,("Engine: Note off received\n"));
902 ProcessNoteOff((EngineChannel*)itEvent->pEngineChannel, itEvent);
903 break;
904 case Event::type_stop_note:
905 dmsg(5,("Engine: Stop Note received\n"));
906 ProcessNoteOff((EngineChannel*)itEvent->pEngineChannel, itEvent);
907 break;
908 case Event::type_kill_note:
909 dmsg(5,("Engine: Kill Note received\n"));
910 ProcessKillNote((EngineChannel*)itEvent->pEngineChannel, itEvent);
911 break;
912 case Event::type_control_change:
913 dmsg(5,("Engine: MIDI CC received\n"));
914 ProcessControlChange((EngineChannel*)itEvent->pEngineChannel, itEvent);
915 bIsCC = true;
916 break;
917 case Event::type_rpn: // this can only be reached here by an instrument script having called set_rpn()
918 dmsg(5,("Engine: MIDI RPN received\n"));
919 ProcessHardcodedRpn((EngineChannel*)itEvent->pEngineChannel, itEvent);
920 bIsCC = true;
921 break;
922 case Event::type_nrpn: // this can only be reached here by an instrument script having called set_nrpn()
923 dmsg(5,("Engine: MIDI NRPN received\n"));
924 ProcessHardcodedNrpn((EngineChannel*)itEvent->pEngineChannel, itEvent);
925 bIsCC = true;
926 break;
927 case Event::type_channel_pressure:
928 dmsg(5,("Engine: MIDI Chan. Pressure received\n"));
929 ProcessChannelPressure((EngineChannel*)itEvent->pEngineChannel, itEvent);
930 break;
931 case Event::type_note_pressure:
932 dmsg(5,("Engine: MIDI Note Pressure received\n"));
933 ProcessPolyphonicKeyPressure((EngineChannel*)itEvent->pEngineChannel, itEvent);
934 break;
935 case Event::type_pitchbend:
936 dmsg(5,("Engine: Pitchbend received\n"));
937 ProcessPitchbend(static_cast<AbstractEngineChannel*>(itEvent->pEngineChannel), itEvent);
938 break;
939 case Event::type_note_synth_param:
940 dmsg(5,("Engine: Note Synth Param received\n"));
941 ProcessNoteSynthParam(itEvent->pEngineChannel, itEvent);
942 break;
943 case Event::type_sysex:
944 break; // TODO ...
945
946 case Event::type_cancel_release_key:
947 case Event::type_release_key:
948 case Event::type_release_note:
949 break; // noop
950 }
951 // reset cached RPN/NRPN parameter number and data in
952 // case this event was not a control change event
953 if (!bIsCC) {
954 if (pChannel->GetMidiRpnParameter() >= 0)
955 pChannel->ResetMidiRpnParameter();
956 if (pChannel->GetMidiNrpnParameter() >= 0)
957 pChannel->ResetMidiNrpnParameter();
958 }
959 }
960 }
961
962 // reset voice stealing for the next engine channel (or next audio fragment)
963 itLastStolenVoice = VoiceIterator();
964 itLastStolenVoiceGlobally = VoiceIterator();
965 itLastStolenNote = NoteIterator();
966 itLastStolenNoteGlobally = NoteIterator();
967 iuiLastStolenKey = RTList<uint>::Iterator();
968 iuiLastStolenKeyGlobally = RTList<uint>::Iterator();
969 pLastStolenChannel = NULL;
970 }
971
972 /**
973 * Run all suspended script execution instances which are scheduled
974 * to be resumed for the current audio fragment cycle.
975 *
976 * @param pChannel - engine channel on which suspended events occurred
977 */
978 void ProcessSuspendedScriptEvents(AbstractEngineChannel* pChannel, const sched_time_t fragmentEndTime) {
979 while (true) {
980 RTList<ScriptEvent>::Iterator itEvent =
981 pEventGenerator->popNextScheduledScriptEvent(
982 pChannel->pScript->suspendedEvents,
983 *pChannel->pScript->pEvents, fragmentEndTime
984 );
985 if (!itEvent) break;
986 ResumeScriptEvent(pChannel, itEvent);
987 }
988 }
989
990 /** @brief Call instrument script's event handler for this event.
991 *
992 * Causes a new execution instance of the currently loaded real-time
993 * instrument script's event handler (callback) to be spawned for
994 * the given MIDI event.
995 *
996 * @param pChannel - engine channel on which the MIDI event occurred
997 * @param itEvent - MIDI event that causes this new script execution
998 * @param pEventHandler - script's event handler to be executed
999 */
1000 void ProcessEventByScript(AbstractEngineChannel* pChannel, RTList<Event>::Iterator& itEvent, VMEventHandler* pEventHandler) {
1001 const int key = itEvent->Param.Note.Key; // even if this is not a note on/off event, accessing it does not mean any harm
1002 // check if polyphonic data is passed from "note" to "release"
1003 // script event handlers
1004 if (pEventHandler == pChannel->pScript->handlerRelease &&
1005 pChannel->pScript->handlerNote &&
1006 pChannel->pScript->handlerNote->isPolyphonic() &&
1007 pChannel->pScript->handlerRelease->isPolyphonic() &&
1008 !pChannel->pScript->pKeyEvents[key]->isEmpty())
1009 {
1010 // polyphonic variable data is used/passed from "note" to
1011 // "release" script callback, so we have to recycle the
1012 // original "note on" script event(s)
1013 RTList<ScriptEvent>::Iterator it = pChannel->pScript->pKeyEvents[key]->first();
1014 RTList<ScriptEvent>::Iterator end = pChannel->pScript->pKeyEvents[key]->end();
1015 for (; it != end; ++it) {
1016 ProcessScriptEvent(
1017 pChannel, itEvent, pEventHandler, it
1018 );
1019 }
1020 } else {
1021 // no polyphonic data is used/passed from "note" to
1022 // "release" script callback, so just use a new fresh
1023 // script event object
1024 RTList<ScriptEvent>::Iterator itScriptEvent =
1025 pChannel->pScript->pEvents->allocAppend();
1026 // if event handler uses polyphonic variables, reset them
1027 // to zero values before starting to execute the handler
1028 if (pEventHandler->isPolyphonic())
1029 itScriptEvent->execCtx->resetPolyphonicData();
1030 ProcessScriptEvent(
1031 pChannel, itEvent, pEventHandler, itScriptEvent
1032 );
1033 }
1034 }
1035
1036 /** @brief Spawn new execution instance of an instrument script handler.
1037 *
1038 * Will be called to initiate a new execution of a real-time
1039 * instrument script event right from the start of the script's
1040 * respective handler. If script execution did not complete after
1041 * calling this method, the respective script exeuction is then
1042 * suspended and a call to ResumeScriptEvent() will be used next
1043 * time to continue its execution.
1044 *
1045 * @param pChannel - engine channel this script is running for
1046 * @param itEvent - event which caused execution of this script
1047 * event handler
1048 * @param pEventHandler - VM representation of event handler to be
1049 * executed
1050 * @param itScriptEvent - script event that shall be processed
1051 */
1052 void ProcessScriptEvent(AbstractEngineChannel* pChannel, RTList<Event>::Iterator& itEvent, VMEventHandler* pEventHandler, RTList<ScriptEvent>::Iterator& itScriptEvent) {
1053 if (!itScriptEvent) return; // not a valid script event (i.e. because no free script event was left in the script event pool)
1054
1055 // fill the list of script handlers to be executed by this event
1056 int i = 0;
1057 itScriptEvent->handlers[i++] = pEventHandler; // actual event handler (i.e. note, controller)
1058 itScriptEvent->handlers[i] = NULL; // NULL termination of list
1059
1060 // initialize/reset other members
1061 itScriptEvent->cause = *itEvent;
1062 itScriptEvent->scheduleTime = itEvent->SchedTime();
1063 itScriptEvent->currentHandler = 0;
1064 itScriptEvent->executionSlices = 0;
1065 itScriptEvent->ignoreAllWaitCalls = false;
1066 itScriptEvent->handlerType = pEventHandler->eventHandlerType();
1067 itScriptEvent->parentHandlerID = 0;
1068 itScriptEvent->childHandlerID[0] = 0;
1069 itScriptEvent->autoAbortByParent = false;
1070 itScriptEvent->forkIndex = 0;
1071 // this is the native representation of the $EVENT_ID script variable
1072 itScriptEvent->id =
1073 (itEvent->Type == Event::type_note_on)
1074 ? ScriptID::fromNoteID( itEvent->Param.Note.ID )
1075 : ScriptID::fromEventID( pEventPool->getID(itEvent) );
1076
1077 // run script handler(s)
1078 VMExecStatus_t res = pScriptVM->exec(
1079 pChannel->pScript->parserContext, &*itScriptEvent
1080 );
1081
1082 // was the script suspended?
1083 if (res & VM_EXEC_SUSPENDED) { // script was suspended ...
1084 // in case the script was suspended, keep it on the allocated
1085 // ScriptEvent list to be resume at the scheduled time in future,
1086 // additionally insert it into a sorted time queue
1087 pEventGenerator->scheduleAheadMicroSec(
1088 pChannel->pScript->suspendedEvents, // scheduler queue
1089 *itScriptEvent, // script event
1090 itScriptEvent->cause.FragmentPos(), // current time of script event (basis for its next execution)
1091 itScriptEvent->execCtx->suspensionTimeMicroseconds() // how long shall it be suspended
1092 );
1093 } else { // script execution has finished without 'suspended' status ...
1094 // if "polyphonic" variable data is passed from script's
1095 // "note" event handler to its "release" event handler, then
1096 // the script event must be kept and recycled for the later
1097 // occuring "release" script event ...
1098 if (pEventHandler == pChannel->pScript->handlerNote &&
1099 pChannel->pScript->handlerRelease &&
1100 pChannel->pScript->handlerNote->isPolyphonic() &&
1101 pChannel->pScript->handlerRelease->isPolyphonic())
1102 {
1103 const int key = itEvent->Param.Note.Key;
1104 itScriptEvent.moveToEndOf(pChannel->pScript->pKeyEvents[key & 127]);
1105 } else {
1106 // ... otherwise if no polyphonic data is passed and
1107 // script's execution has finished without suspension
1108 // status, then free the script event for a new future
1109 // script event to be triggered from start
1110 pChannel->pScript->pEvents->free(itScriptEvent);
1111 }
1112 }
1113 }
1114
1115 /** @brief Resume execution of instrument script.
1116 *
1117 * Will be called to resume execution of a real-time instrument
1118 * script event which has been suspended previously.
1119 *
1120 * Script execution might be suspended for various reasons. Usually
1121 * a script will be suspended if the script called the built-in
1122 * "wait()" function, but it might also be suspended automatically
1123 * if the script took too much execution time in an audio fragment
1124 * cycle. So in the latter case automatic suspension is performed in
1125 * order to avoid harm for the sampler's overall real-time
1126 * requirements.
1127 *
1128 * @param pChannel - engine channel this script is running for
1129 * @param itScriptEvent - script execution that shall be resumed
1130 */
1131 void ResumeScriptEvent(AbstractEngineChannel* pChannel, RTList<ScriptEvent>::Iterator& itScriptEvent) {
1132 VMEventHandler* handler = itScriptEvent->handlers[itScriptEvent->currentHandler];
1133
1134 // run script
1135 VMExecStatus_t res = pScriptVM->exec(
1136 pChannel->pScript->parserContext, &*itScriptEvent
1137 );
1138
1139 // was the script suspended?
1140 if (res & VM_EXEC_SUSPENDED) {
1141 // in case the script was suspended, keep it on the allocated
1142 // ScriptEvent list to be resume at the scheduled time in future,
1143 // additionally insert it into a sorted time queue
1144 pEventGenerator->scheduleAheadMicroSec(
1145 pChannel->pScript->suspendedEvents, // scheduler queue
1146 *itScriptEvent, // script event
1147 itScriptEvent->cause.FragmentPos(), // current time of script event (basis for its next execution)
1148 itScriptEvent->execCtx->suspensionTimeMicroseconds() // how long shall it be suspended
1149 );
1150 } else { // script execution has finished without 'suspended' status ...
1151 // if "polyphonic" variable data is passed from script's
1152 // "note" event handler to its "release" event handler, then
1153 // the script event must be kept and recycled for the later
1154 // occuring "release" script event ...
1155 if (handler && handler == pChannel->pScript->handlerNote &&
1156 pChannel->pScript->handlerRelease &&
1157 pChannel->pScript->handlerNote->isPolyphonic() &&
1158 pChannel->pScript->handlerRelease->isPolyphonic())
1159 {
1160 const int key = itScriptEvent->cause.Param.Note.Key;
1161 itScriptEvent.moveToEndOf(pChannel->pScript->pKeyEvents[key & 127]);
1162 } else {
1163 // ... otherwise if no polyphonic data is passed and
1164 // script's execution has finished without suspension
1165 // status, then free the script event for a new future
1166 // script event to be triggered from start
1167 pChannel->pScript->pEvents->free(itScriptEvent);
1168 }
1169 }
1170 }
1171
1172 /**
1173 * Will be called by LaunchVoice() method in case there are no free
1174 * voices left. This method will select and kill one old voice for
1175 * voice stealing and postpone the note-on event until the selected
1176 * voice actually died.
1177 *
1178 * @param pEngineChannel - engine channel on which this event occurred on
1179 * @param itNoteOnEvent - key, velocity and time stamp of the event
1180 * @returns 0 on success, a value < 0 if no active voice could be picked for voice stealing
1181 */
1182 int StealVoice(EngineChannel* pEngineChannel, Pool<Event>::Iterator& itNoteOnEvent) {
1183 dmsg(3,("StealVoice()\n"));
1184 if (VoiceSpawnsLeft <= 0) {
1185 dmsg(1,("Max. voice thefts per audio fragment reached (you may raise CONFIG_MAX_VOICES).\n"));
1186 return -1;
1187 }
1188
1189 EngineChannelBase<V, R, I>* pEngineChn = static_cast<EngineChannelBase<V, R, I>*>(pEngineChannel);
1190
1191 if (pEventPool->poolIsEmpty()) {
1192 dmsg(1,("Event pool emtpy!\n"));
1193 return -1;
1194 }
1195
1196 if (!pEngineChn->StealVoice(itNoteOnEvent, &itLastStolenVoice, &itLastStolenNote, &iuiLastStolenKey)) {
1197 --VoiceSpawnsLeft;
1198 return 0;
1199 }
1200
1201 // if we couldn't steal a voice from the same engine channel then
1202 // steal oldest voice on the oldest key from any other engine channel
1203 // (the smaller engine channel number, the higher priority)
1204 EngineChannelBase<V, R, I>* pSelectedChannel;
1205 int iChannelIndex;
1206 VoiceIterator itSelectedVoice;
1207
1208 #if CONFIG_DEVMODE
1209 EngineChannel* pBegin = NULL; // to detect endless loop
1210 #endif
1211
1212 // select engine channel
1213 if (pLastStolenChannel) {
1214 pSelectedChannel = pLastStolenChannel;
1215 iChannelIndex = pSelectedChannel->iEngineIndexSelf;
1216 } else { // pick the engine channel followed by this engine channel
1217 iChannelIndex = (pEngineChn->iEngineIndexSelf + 1) % engineChannels.size();
1218 pSelectedChannel = static_cast<EngineChannelBase<V, R, I>*>(engineChannels[iChannelIndex]);
1219 }
1220
1221 // if we already stole in this fragment, try to proceed on same note
1222 if (this->itLastStolenVoiceGlobally) {
1223 itSelectedVoice = this->itLastStolenVoiceGlobally;
1224 do {
1225 ++itSelectedVoice;
1226 } while (itSelectedVoice && !itSelectedVoice->IsStealable()); // proceed iterating if voice was created in this fragment cycle
1227 }
1228 // did we find a 'stealable' voice?
1229 if (itSelectedVoice && itSelectedVoice->IsStealable()) {
1230 // remember which voice we stole, so we can simply proceed on next voice stealing
1231 this->itLastStolenVoiceGlobally = itSelectedVoice;
1232 // done
1233 goto stealable_voice_found;
1234 }
1235
1236 // get (next) oldest note
1237 if (this->itLastStolenNoteGlobally) {
1238 for (NoteIterator itNote = ++this->itLastStolenNoteGlobally;
1239 itNote; ++itNote)
1240 {
1241 for (itSelectedVoice = itNote->pActiveVoices->first(); itSelectedVoice; ++itSelectedVoice) {
1242 // proceed iterating if voice was created in this audio fragment cycle
1243 if (itSelectedVoice->IsStealable()) {
1244 // remember which voice of which note we stole, so we can simply proceed on next voice stealing
1245 this->itLastStolenNoteGlobally = itNote;
1246 this->itLastStolenVoiceGlobally = itSelectedVoice;
1247 goto stealable_voice_found; // selection succeeded
1248 }
1249 }
1250 }
1251 }
1252
1253 #if CONFIG_DEVMODE
1254 pBegin = pSelectedChannel; // to detect endless loop
1255 #endif // CONFIG_DEVMODE
1256
1257 while (true) { // iterate through engine channels
1258 // get (next) oldest key
1259 RTList<uint>::Iterator iuiSelectedKey = (this->iuiLastStolenKeyGlobally) ? ++this->iuiLastStolenKeyGlobally : pSelectedChannel->pActiveKeys->first();
1260 this->iuiLastStolenKeyGlobally = RTList<uint>::Iterator(); // to prevent endless loop (see line above)
1261 while (iuiSelectedKey) {
1262 MidiKey* pSelectedKey = &pSelectedChannel->pMIDIKeyInfo[*iuiSelectedKey];
1263
1264 for (NoteIterator itNote = pSelectedKey->pActiveNotes->first(),
1265 itNotesEnd = pSelectedKey->pActiveNotes->end();
1266 itNote != itNotesEnd; ++itNote)
1267 {
1268 itSelectedVoice = itNote->pActiveVoices->first();
1269 // proceed iterating if voice was created in this fragment cycle
1270 while (itSelectedVoice && !itSelectedVoice->IsStealable()) ++itSelectedVoice;
1271 // found a "stealable" voice ?
1272 if (itSelectedVoice && itSelectedVoice->IsStealable()) {
1273 // remember which voice of which note on which key on which engine channel we stole, so we can simply proceed on next voice stealing
1274 this->iuiLastStolenKeyGlobally = iuiSelectedKey;
1275 this->itLastStolenNoteGlobally = itNote;
1276 this->itLastStolenVoiceGlobally = itSelectedVoice;
1277 this->pLastStolenChannel = pSelectedChannel;
1278 goto stealable_voice_found; // selection succeeded
1279 }
1280 }
1281 ++iuiSelectedKey; // get next key on current engine channel
1282 }
1283 // get next engine channel
1284 iChannelIndex = (iChannelIndex + 1) % engineChannels.size();
1285 pSelectedChannel = static_cast<EngineChannelBase<V, R, I>*>(engineChannels[iChannelIndex]);
1286
1287 #if CONFIG_DEVMODE
1288 if (pSelectedChannel == pBegin) {
1289 dmsg(1,("FATAL ERROR: voice stealing endless loop!\n"));
1290 dmsg(1,("VoiceSpawnsLeft=%d.\n", VoiceSpawnsLeft));
1291 dmsg(1,("Exiting.\n"));
1292 exit(-1);
1293 }
1294 #endif // CONFIG_DEVMODE
1295 }
1296
1297 // jump point if a 'stealable' voice was found
1298 stealable_voice_found:
1299
1300 #if CONFIG_DEVMODE
1301 if (!itSelectedVoice->IsActive()) {
1302 dmsg(1,("EngineBase: ERROR, tried to steal a voice which was not active !!!\n"));
1303 return -1;
1304 }
1305 #endif // CONFIG_DEVMODE
1306
1307 // now kill the selected voice
1308 itSelectedVoice->Kill(itNoteOnEvent);
1309
1310 --VoiceSpawnsLeft;
1311
1312 return 0; // success
1313 }
1314
1315 void HandleInstrumentChanges() {
1316 bool instrumentChanged = false;
1317 for (int i = 0; i < engineChannels.size(); i++) {
1318 EngineChannelBase<V, R, I>* pEngineChannel =
1319 static_cast<EngineChannelBase<V, R, I>*>(engineChannels[i]);
1320
1321 // as we're going to (carefully) write some status to the
1322 // synchronized struct, we cast away the const
1323 InstrumentChangeCmd<R, I>& cmd =
1324 const_cast<InstrumentChangeCmd<R, I>&>(pEngineChannel->InstrumentChangeCommandReader.Lock());
1325
1326 pEngineChannel->pRegionsInUse = cmd.pRegionsInUse;
1327 pEngineChannel->pRegionsInUse->clear();
1328
1329 if (cmd.bChangeInstrument) {
1330 // change instrument
1331 dmsg(5,("Engine: instrument change command received\n"));
1332 cmd.bChangeInstrument = false;
1333 pEngineChannel->pInstrument = cmd.pInstrument;
1334 pEngineChannel->pScript =
1335 cmd.pScript->bHasValidScript ? cmd.pScript : NULL;
1336 instrumentChanged = true;
1337
1338 pEngineChannel->MarkAllActiveVoicesAsOrphans();
1339
1340 // the script's "init" event handler is only executed
1341 // once (when the script is loaded or reloaded)
1342 if (pEngineChannel->pScript && pEngineChannel->pScript->handlerInit) {
1343 dmsg(5,("Engine: exec handlerInit %p\n", pEngineChannel->pScript->handlerInit));
1344 RTList<ScriptEvent>::Iterator itScriptEvent =
1345 pEngineChannel->pScript->pEvents->allocAppend();
1346
1347 itScriptEvent->cause = pEventGenerator->CreateEvent(0);
1348 itScriptEvent->cause.Type = (Event::type_t) -1; // some invalid type to avoid random event processing
1349 itScriptEvent->cause.pEngineChannel = pEngineChannel;
1350 itScriptEvent->cause.pMidiInputPort = pEngineChannel->GetMidiInputPort();
1351 itScriptEvent->id = 0;
1352 itScriptEvent->handlers[0] = pEngineChannel->pScript->handlerInit;
1353 itScriptEvent->handlers[1] = NULL;
1354 itScriptEvent->currentHandler = 0;
1355 itScriptEvent->executionSlices = 0;
1356 itScriptEvent->ignoreAllWaitCalls = false;
1357 itScriptEvent->handlerType = VM_EVENT_HANDLER_INIT;
1358 itScriptEvent->parentHandlerID = 0;
1359 itScriptEvent->childHandlerID[0] = 0;
1360 itScriptEvent->autoAbortByParent = false;
1361 itScriptEvent->forkIndex = 0;
1362
1363 VMExecStatus_t res;
1364 size_t instructionsCount = 0;
1365 const size_t maxInstructions = 200000; // aiming approx. 1 second max. (based on very roughly 5us / instruction)
1366 bool bWarningShown = false;
1367 do {
1368 res = pScriptVM->exec(
1369 pEngineChannel->pScript->parserContext, &*itScriptEvent
1370 );
1371 instructionsCount += itScriptEvent->execCtx->instructionsPerformed();
1372 if (instructionsCount > maxInstructions && !bWarningShown) {
1373 bWarningShown = true;
1374 dmsg(0,("[ScriptVM] WARNING: \"init\" event handler of instrument script executing for long time!\n"));
1375 }
1376 } while (res & VM_EXEC_SUSPENDED && !(res & VM_EXEC_ERROR));
1377
1378 pEngineChannel->pScript->pEvents->free(itScriptEvent);
1379 }
1380 }
1381 }
1382
1383 if (instrumentChanged) {
1384 //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
1385 ResetSuspendedRegions();
1386 }
1387 }
1388
1389 /**
1390 * Render all 'normal' voices (that is voices which were not stolen in
1391 * this fragment) on the given engine channel.
1392 *
1393 * @param pEngineChannel - engine channel on which audio should be
1394 * rendered
1395 * @param Samples - amount of sample points to be rendered in
1396 * this audio fragment cycle
1397 */
1398 void RenderActiveVoices(EngineChannel* pEngineChannel, uint Samples) {
1399 #if !CONFIG_PROCESS_MUTED_CHANNELS
1400 if (pEngineChannel->GetMute()) return; // skip if sampler channel is muted
1401 #endif
1402
1403 EngineChannelBase<V, R, I>* pChannel =
1404 static_cast<EngineChannelBase<V, R, I>*>(pEngineChannel);
1405 pChannel->RenderActiveVoices(Samples);
1406
1407 ActiveVoiceCountTemp += pEngineChannel->GetVoiceCount();
1408 }
1409
1410 /**
1411 * Render all stolen voices (only voices which were stolen in this
1412 * fragment) on the given engine channel. Stolen voices are rendered
1413 * after all normal voices have been rendered; this is needed to render
1414 * audio of those voices which were selected for voice stealing until
1415 * the point were the stealing (that is the take over of the voice)
1416 * actually happened.
1417 *
1418 * @param pEngineChannel - engine channel on which audio should be
1419 * rendered
1420 * @param Samples - amount of sample points to be rendered in
1421 * this audio fragment cycle
1422 */
1423 void RenderStolenVoices(uint Samples) {
1424 RTList<Event>::Iterator itVoiceStealEvent = pVoiceStealingQueue->first();
1425 RTList<Event>::Iterator end = pVoiceStealingQueue->end();
1426 for (; itVoiceStealEvent != end; ++itVoiceStealEvent) {
1427 EngineChannelBase<V, R, I>* pEngineChannel =
1428 static_cast<EngineChannelBase<V, R, I>*>(itVoiceStealEvent->pEngineChannel);;
1429 if (!pEngineChannel->pInstrument) continue; // ignore if no instrument loaded
1430
1431 PoolVoiceIterator itNewVoice =
1432 LaunchVoice(pEngineChannel, itVoiceStealEvent, itVoiceStealEvent->Param.Note.Layer, itVoiceStealEvent->Param.Note.ReleaseTrigger, false, false);
1433 if (itNewVoice) {
1434 // usually there should already be a new Note object
1435 NoteIterator itNote = GetNotePool()->fromID(itVoiceStealEvent->Param.Note.ID);
1436 if (!itNote) { // should not happen, but just to be sure ...
1437 dmsg(2,("Engine: No Note object for stolen voice!\n"));
1438 const note_id_t noteID = LaunchNewNote(pEngineChannel, itVoiceStealEvent);
1439 if (!noteID) {
1440 dmsg(1,("Engine: Voice stealing failed; No Note object and Note pool empty!\n"));
1441 continue;
1442 }
1443 itNote = GetNotePool()->fromID(noteID);
1444 }
1445 // move voice from whereever it was, to the new note's list of active voices
1446 itNewVoice = itNewVoice.moveToEndOf(itNote->pActiveVoices);
1447 // render audio of this new voice for the first time
1448 itNewVoice->Render(Samples);
1449 if (itNewVoice->IsActive()) { // still active
1450 *(pEngineChannel->pRegionsInUse->allocAppend()) = itNewVoice->GetRegion();
1451 ActiveVoiceCountTemp++;
1452 pEngineChannel->SetVoiceCount(pEngineChannel->GetVoiceCount() + 1);
1453
1454 if (itNewVoice->PlaybackState == Voice::playback_state_disk) {
1455 if (itNewVoice->DiskStreamRef.State != Stream::state_unused) {
1456 pEngineChannel->SetDiskStreamCount(pEngineChannel->GetDiskStreamCount() + 1);
1457 }
1458 }
1459 } else { // voice reached end, is now inactive
1460 pEngineChannel->FreeVoice(itNewVoice); // remove voice from the list of active voices
1461 }
1462 }
1463 else dmsg(1,("EngineBase: ERROR, voice stealing didn't work out!\n"));
1464
1465 // we need to clear the key's event list explicitly here in case key was never active
1466 MidiKey* pKey = &pEngineChannel->pMIDIKeyInfo[itVoiceStealEvent->Param.Note.Key];
1467 pKey->VoiceTheftsQueued--;
1468 if (!pKey->Active && !pKey->VoiceTheftsQueued) pKey->pEvents->clear();
1469 }
1470 }
1471
1472 /**
1473 * Free all keys which have turned inactive in this audio fragment, from
1474 * the list of active keys and clear all event lists on that engine
1475 * channel.
1476 *
1477 * @param pEngineChannel - engine channel to cleanup
1478 */
1479 void PostProcess(EngineChannel* pEngineChannel) {
1480 EngineChannelBase<V, R, I>* pChannel =
1481 static_cast<EngineChannelBase<V, R, I>*>(pEngineChannel);
1482 pChannel->FreeAllInactiveKeys();
1483
1484 // empty the engine channel's own event lists
1485 // (only events of the current audio fragment cycle)
1486 pChannel->ClearEventListsOfCurrentFragment();
1487 }
1488
1489 /**
1490 * Process MIDI control change events with hard coded behavior,
1491 * that is controllers whose behavior is defined independently
1492 * of the actual sampler engine type and instrument.
1493 *
1494 * @param pEngineChannel - engine channel on which the MIDI CC event was received
1495 * @param itControlChangeEvent - the actual MIDI CC event
1496 */
1497 void ProcessHardcodedControllers (
1498 EngineChannel* pEngineChannel,
1499 Pool<Event>::Iterator& itControlChangeEvent
1500 ) {
1501 EngineChannelBase<V, R, I>* pChannel =
1502 static_cast<EngineChannelBase<V, R, I>*>(pEngineChannel);
1503
1504 // will be set to true if this CC event has anything to do with RPN/NRPN
1505 bool bIsRpn = false, bIsNrpn = false;
1506
1507 switch (itControlChangeEvent->Param.CC.Controller) {
1508 case 5: { // portamento time
1509 pChannel->PortamentoTime = (float) itControlChangeEvent->Param.CC.Value / 127.0f * (float) CONFIG_PORTAMENTO_TIME_MAX + (float) CONFIG_PORTAMENTO_TIME_MIN;
1510 break;
1511 }
1512 case 6: { // data entry (MSB)
1513 //dmsg(1,("DATA ENTRY MSB %d\n", itControlChangeEvent->Param.CC.Value));
1514 if (pChannel->GetMidiRpnParameter() >= 0) { // RPN parameter number was sent previously ...
1515 pChannel->SetMidiRpnDataMsb(
1516 itControlChangeEvent->Param.CC.Value
1517 );
1518 bIsRpn = true;
1519
1520 // look-ahead: if next MIDI event is data entry LSB,
1521 // then skip this event here for now (to avoid double
1522 // handling of what's supposed to be one RPN event)
1523 if (isNextEventCCNr(itControlChangeEvent, 38))
1524 break;
1525
1526 int ch = itControlChangeEvent->Param.CC.Channel;
1527 int param = pChannel->GetMidiRpnParameter();
1528 int value = pChannel->GetMidiRpnData();
1529
1530 // transform event type: CC event -> RPN event
1531 itControlChangeEvent->Type = Event::type_rpn;
1532 itControlChangeEvent->Param.RPN.Channel = ch;
1533 itControlChangeEvent->Param.RPN.Parameter = param;
1534 itControlChangeEvent->Param.RPN.Value = value;
1535
1536 // if there's a RPN script handler, run it ...
1537 if (pChannel->pScript->handlerRpn) {
1538 const event_id_t eventID =
1539 pEventPool->getID(itControlChangeEvent);
1540 // run the RPN script handler
1541 ProcessEventByScript(
1542 pChannel, itControlChangeEvent,
1543 pChannel->pScript->handlerRpn
1544 );
1545 // if RPN event was dropped by script, abort
1546 // here to avoid hard coded RPN processing below
1547 if (!pEventPool->fromID(eventID))
1548 break;
1549 }
1550
1551 // do the actual (hard-coded) RPN value change processing
1552 ProcessHardcodedRpn(pEngineChannel, itControlChangeEvent);
1553
1554 } else if (pChannel->GetMidiNrpnParameter() >= 0) { // NRPN parameter number was sent previously ...
1555 pChannel->SetMidiNrpnDataMsb(
1556 itControlChangeEvent->Param.CC.Value
1557 );
1558 bIsNrpn = true;
1559
1560 // look-ahead: if next MIDI event is data entry LSB,
1561 // then skip this event here for now (to avoid double
1562 // handling of what's supposed to be one NRPN event)
1563 if (isNextEventCCNr(itControlChangeEvent, 38))
1564 break;
1565
1566 int ch = itControlChangeEvent->Param.CC.Channel;
1567 int param = pChannel->GetMidiNrpnParameter();
1568 int value = pChannel->GetMidiNrpnData();
1569
1570 // transform event type: CC event -> NRPN event
1571 itControlChangeEvent->Type = Event::type_nrpn;
1572 itControlChangeEvent->Param.NRPN.Channel = ch;
1573 itControlChangeEvent->Param.NRPN.Parameter = param;
1574 itControlChangeEvent->Param.NRPN.Value = value;
1575
1576 // if there's a NRPN script handler, run it ...
1577 if (pChannel->pScript->handlerNrpn) {
1578 const event_id_t eventID =
1579 pEventPool->getID(itControlChangeEvent);
1580 // run the NRPN script handler
1581 ProcessEventByScript(
1582 pChannel, itControlChangeEvent,
1583 pChannel->pScript->handlerNrpn
1584 );
1585 // if NRPN event was dropped by script, abort
1586 // here to avoid hard coded NRPN processing below
1587 if (!pEventPool->fromID(eventID))
1588 break;
1589 }
1590
1591 // do the actual (hard-coded) NRPN value change processing
1592 ProcessHardcodedNrpn(pEngineChannel, itControlChangeEvent);
1593 }
1594 break;
1595 }
1596 case 7: { // volume
1597 //TODO: not sample accurate yet
1598 pChannel->MidiVolume = VolumeCurve[itControlChangeEvent->Param.CC.Value];
1599 pChannel->bStatusChanged = true; // engine channel status has changed, so set notify flag
1600 break;
1601 }
1602 case 10: { // panpot
1603 //TODO: not sample accurate yet
1604 pChannel->iLastPanRequest = itControlChangeEvent->Param.CC.Value;
1605 break;
1606 }
1607 case 38: { // data entry (LSB)
1608 //dmsg(1,("DATA ENTRY LSB %d\n", itControlChangeEvent->Param.CC.Value));
1609 if (pChannel->GetMidiRpnParameter() >= 0) { // RPN parameter number was sent previously ...
1610 pChannel->SetMidiRpnDataLsb(
1611 itControlChangeEvent->Param.CC.Value
1612 );
1613 bIsRpn = true;
1614
1615 int ch = itControlChangeEvent->Param.CC.Channel;
1616 int param = pChannel->GetMidiRpnParameter();
1617 int value = pChannel->GetMidiRpnData();
1618
1619 // transform event type: CC event -> RPN event
1620 itControlChangeEvent->Type = Event::type_rpn;
1621 itControlChangeEvent->Param.RPN.Channel = ch;
1622 itControlChangeEvent->Param.RPN.Parameter = param;
1623 itControlChangeEvent->Param.RPN.Value = value;
1624
1625 // if there's a RPN script handler, run it ...
1626 if (pChannel->pScript->handlerRpn) {
1627 const event_id_t eventID =
1628 pEventPool->getID(itControlChangeEvent);
1629 // run the RPN script handler
1630 ProcessEventByScript(
1631 pChannel, itControlChangeEvent,
1632 pChannel->pScript->handlerRpn
1633 );
1634 // if RPN event was dropped by script, abort
1635 // here to avoid hard coded RPN processing below
1636 if (!pEventPool->fromID(eventID))
1637 break;
1638 }
1639
1640 // do the actual (hard-coded) RPN value change processing
1641 ProcessHardcodedRpn(pEngineChannel, itControlChangeEvent);
1642
1643 } else if (pChannel->GetMidiNrpnParameter() >= 0) { // NRPN parameter number was sent previously ...
1644 pChannel->SetMidiNrpnDataLsb(
1645 itControlChangeEvent->Param.CC.Value
1646 );
1647 bIsNrpn = true;
1648
1649 int ch = itControlChangeEvent->Param.CC.Channel;
1650 int param = pChannel->GetMidiNrpnParameter();
1651 int value = pChannel->GetMidiNrpnData();
1652
1653 // transform event type: CC event -> NRPN event
1654 itControlChangeEvent->Type = Event::type_nrpn;
1655 itControlChangeEvent->Param.NRPN.Channel = ch;
1656 itControlChangeEvent->Param.NRPN.Parameter = param;
1657 itControlChangeEvent->Param.NRPN.Value = value;
1658
1659 // if there's a NRPN script handler, run it ...
1660 if (pChannel->pScript->handlerNrpn) {
1661 const event_id_t eventID =
1662 pEventPool->getID(itControlChangeEvent);
1663 // run the NRPN script handler
1664 ProcessEventByScript(
1665 pChannel, itControlChangeEvent,
1666 pChannel->pScript->handlerNrpn
1667 );
1668 // if NRPN event was dropped by script, abort
1669 // here to avoid hard coded NRPN processing below
1670 if (!pEventPool->fromID(eventID))
1671 break;
1672 }
1673
1674 // do the actual (hard-coded) NRPN value change processing
1675 ProcessHardcodedNrpn(pEngineChannel, itControlChangeEvent);
1676 }
1677 break;
1678 }
1679 case 64: { // sustain
1680 if (itControlChangeEvent->Param.CC.Value >= 64 && !pChannel->SustainPedal) {
1681 dmsg(4,("DAMPER (RIGHT) PEDAL DOWN\n"));
1682 pChannel->SustainPedal = true;
1683 pChannel->listeners.PreProcessSustainPedalDown();
1684
1685 #if !CONFIG_PROCESS_MUTED_CHANNELS
1686 if (pEngineChannel->GetMute()) { // skip if sampler channel is muted
1687 pChannel->listeners.PostProcessSustainPedalDown();
1688 return;
1689 }
1690 #endif
1691
1692 pChannel->ProcessSustainPedalDown(itControlChangeEvent);
1693 pChannel->listeners.PostProcessSustainPedalDown();
1694 }
1695 if (itControlChangeEvent->Param.CC.Value < 64 && pChannel->SustainPedal) {
1696 dmsg(4,("DAMPER (RIGHT) PEDAL UP\n"));
1697 pChannel->SustainPedal = false;
1698 pChannel->listeners.PreProcessSustainPedalUp();
1699
1700 #if !CONFIG_PROCESS_MUTED_CHANNELS
1701 if (pChannel->GetMute()) { // skip if sampler channel is muted
1702 pChannel->listeners.PostProcessSustainPedalUp();
1703 return;
1704 }
1705 #endif
1706
1707 pChannel->ProcessSustainPedalUp(itControlChangeEvent);
1708 pChannel->listeners.PostProcessSustainPedalUp();
1709 }
1710 break;
1711 }
1712 case 65: { // portamento on / off
1713 const bool bPortamento = itControlChangeEvent->Param.CC.Value >= 64;
1714 if (bPortamento != pChannel->PortamentoMode)
1715 KillAllVoices(pChannel, itControlChangeEvent);
1716 pChannel->PortamentoMode = bPortamento;
1717 break;
1718 }
1719 case 66: { // sostenuto
1720 if (itControlChangeEvent->Param.CC.Value >= 64 && !pChannel->SostenutoPedal) {
1721 dmsg(4,("SOSTENUTO (CENTER) PEDAL DOWN\n"));
1722 pChannel->SostenutoPedal = true;
1723 pChannel->listeners.PreProcessSostenutoPedalDown();
1724
1725 #if !CONFIG_PROCESS_MUTED_CHANNELS
1726 if (pEngineChannel->GetMute()) { // skip if sampler channel is muted
1727 pChannel->listeners.PostProcessSostenutoPedalDown();
1728 return;
1729 }
1730 #endif
1731
1732 pChannel->ProcessSostenutoPedalDown();
1733 pChannel->listeners.PostProcessSostenutoPedalDown();
1734 }
1735 if (itControlChangeEvent->Param.CC.Value < 64 && pChannel->SostenutoPedal) {
1736 dmsg(4,("SOSTENUTO (CENTER) PEDAL UP\n"));
1737 pChannel->SostenutoPedal = false;
1738 pChannel->listeners.PreProcessSostenutoPedalUp();
1739
1740 #if !CONFIG_PROCESS_MUTED_CHANNELS
1741 if (pEngineChannel->GetMute()) { // skip if sampler channel is muted
1742 pChannel->listeners.PostProcessSostenutoPedalUp();
1743 return;
1744 }
1745 #endif
1746
1747 pChannel->ProcessSostenutoPedalUp(itControlChangeEvent);
1748 pChannel->listeners.PostProcessSostenutoPedalUp();
1749 }
1750 break;
1751 }
1752 case 96: { // data increment (data entry +1)
1753 //dmsg(1,("DATA INC\n"));
1754 if (pChannel->GetMidiRpnParameter() >= 0) { // RPN parameter number was sent previously ...
1755 pChannel->SetMidiRpnData(
1756 pChannel->GetMidiRpnData() + 1
1757 );
1758 bIsRpn = true;
1759
1760 int ch = itControlChangeEvent->Param.CC.Channel;
1761 int param = pChannel->GetMidiRpnParameter();
1762 int value = pChannel->GetMidiRpnData();
1763
1764 // transform event type: CC event -> RPN event
1765 itControlChangeEvent->Type = Event::type_rpn;
1766 itControlChangeEvent->Param.RPN.Channel = ch;
1767 itControlChangeEvent->Param.RPN.Parameter = param;
1768 itControlChangeEvent->Param.RPN.Value = value;
1769
1770 // if there's a RPN script handler, run it ...
1771 if (pChannel->pScript->handlerRpn) {
1772 const event_id_t eventID =
1773 pEventPool->getID(itControlChangeEvent);
1774 // run the RPN script handler
1775 ProcessEventByScript(
1776 pChannel, itControlChangeEvent,
1777 pChannel->pScript->handlerRpn
1778 );
1779 // if RPN event was dropped by script, abort
1780 // here to avoid hard coded RPN processing below
1781 if (!pEventPool->fromID(eventID))
1782 break;
1783 }
1784
1785 // do the actual (hard-coded) RPN value change processing
1786 ProcessHardcodedRpn(pEngineChannel, itControlChangeEvent);
1787
1788 } else if (pChannel->GetMidiNrpnParameter() >= 0) { // NRPN parameter number was sent previously ...
1789 pChannel->SetMidiNrpnData(
1790 pChannel->GetMidiNrpnData() + 1
1791 );
1792 bIsNrpn = true;
1793
1794 int ch = itControlChangeEvent->Param.CC.Channel;
1795 int param = pChannel->GetMidiNrpnParameter();
1796 int value = pChannel->GetMidiNrpnData();
1797
1798 // transform event type: CC event -> NRPN event
1799 itControlChangeEvent->Type = Event::type_nrpn;
1800 itControlChangeEvent->Param.NRPN.Channel = ch;
1801 itControlChangeEvent->Param.NRPN.Parameter = param;
1802 itControlChangeEvent->Param.NRPN.Value = value;
1803
1804 // if there's a NRPN script handler, run it ...
1805 if (pChannel->pScript->handlerNrpn) {
1806 const event_id_t eventID =
1807 pEventPool->getID(itControlChangeEvent);
1808 // run the NRPN script handler
1809 ProcessEventByScript(
1810 pChannel, itControlChangeEvent,
1811 pChannel->pScript->handlerNrpn
1812 );
1813 // if NRPN event was dropped by script, abort
1814 // here to avoid hard coded NRPN processing below
1815 if (!pEventPool->fromID(eventID))
1816 break;
1817 }
1818
1819 // do the actual (hard-coded) NRPN value change processing
1820 ProcessHardcodedNrpn(pEngineChannel, itControlChangeEvent);
1821 }
1822 break;
1823 }
1824 case 97: { // data decrement (data entry -1)
1825 //dmsg(1,("DATA DEC\n"));
1826 if (pChannel->GetMidiRpnParameter() >= 0) { // RPN parameter number was sent previously ...
1827 pChannel->SetMidiRpnData(
1828 pChannel->GetMidiRpnData() - 1
1829 );
1830 bIsRpn = true;
1831
1832 int ch = itControlChangeEvent->Param.CC.Channel;
1833 int param = pChannel->GetMidiRpnParameter();
1834 int value = pChannel->GetMidiRpnData();
1835
1836 // transform event type: CC event -> RPN event
1837 itControlChangeEvent->Type = Event::type_rpn;
1838 itControlChangeEvent->Param.RPN.Channel = ch;
1839 itControlChangeEvent->Param.RPN.Parameter = param;
1840 itControlChangeEvent->Param.RPN.Value = value;
1841
1842 // if there's a RPN script handler, run it ...
1843 if (pChannel->pScript->handlerRpn) {
1844 const event_id_t eventID =
1845 pEventPool->getID(itControlChangeEvent);
1846 // run the RPN script handler
1847 ProcessEventByScript(
1848 pChannel, itControlChangeEvent,
1849 pChannel->pScript->handlerRpn
1850 );
1851 // if RPN event was dropped by script, abort
1852 // here to avoid hard coded RPN processing below
1853 if (!pEventPool->fromID(eventID))
1854 break;
1855 }
1856
1857 // do the actual (hard-coded) RPN value change processing
1858 ProcessHardcodedRpn(pEngineChannel, itControlChangeEvent);
1859
1860 } else if (pChannel->GetMidiNrpnParameter() >= 0) { // NRPN parameter number was sent previously ...
1861 pChannel->SetMidiNrpnData(
1862 pChannel->GetMidiNrpnData() - 1
1863 );
1864 bIsNrpn = true;
1865
1866 int ch = itControlChangeEvent->Param.CC.Channel;
1867 int param = pChannel->GetMidiNrpnParameter();
1868 int value = pChannel->GetMidiNrpnData();
1869
1870 // transform event type: CC event -> NRPN event
1871 itControlChangeEvent->Type = Event::type_nrpn;
1872 itControlChangeEvent->Param.NRPN.Channel = ch;
1873 itControlChangeEvent->Param.NRPN.Parameter = param;
1874 itControlChangeEvent->Param.NRPN.Value = value;
1875
1876 // if there's a NRPN script handler, run it ...
1877 if (pChannel->pScript->handlerNrpn) {
1878 const event_id_t eventID =
1879 pEventPool->getID(itControlChangeEvent);
1880 // run the NRPN script handler
1881 ProcessEventByScript(
1882 pChannel, itControlChangeEvent,
1883 pChannel->pScript->handlerNrpn
1884 );
1885 // if NRPN event was dropped by script, abort
1886 // here to avoid hard coded NRPN processing below
1887 if (!pEventPool->fromID(eventID))
1888 break;
1889 }
1890
1891 // do the actual (hard-coded) NRPN value change processing
1892 ProcessHardcodedNrpn(pEngineChannel, itControlChangeEvent);
1893 }
1894 break;
1895 }
1896 case 98: { // NRPN parameter LSB
1897 dmsg(4,("NRPN LSB %d\n", itControlChangeEvent->Param.CC.Value));
1898 bIsNrpn = true;
1899 pEngineChannel->SetMidiNrpnParameterLsb(itControlChangeEvent->Param.CC.Value);
1900 break;
1901 }
1902 case 99: { // NRPN parameter MSB
1903 dmsg(4,("NRPN MSB %d\n", itControlChangeEvent->Param.CC.Value));
1904 bIsNrpn = true;
1905 pEngineChannel->SetMidiNrpnParameterMsb(itControlChangeEvent->Param.CC.Value);
1906 break;
1907 }
1908 case 100: { // RPN parameter LSB
1909 dmsg(4,("RPN LSB %d\n", itControlChangeEvent->Param.CC.Value));
1910 bIsRpn = true;
1911 pEngineChannel->SetMidiRpnParameterLsb(itControlChangeEvent->Param.CC.Value);
1912 break;
1913 }
1914 case 101: { // RPN parameter MSB
1915 dmsg(4,("RPN MSB %d\n", itControlChangeEvent->Param.CC.Value));
1916 bIsRpn = true;
1917 pEngineChannel->SetMidiRpnParameterMsb(itControlChangeEvent->Param.CC.Value);
1918 break;
1919 }
1920
1921
1922 // Channel Mode Messages
1923
1924 case 120: { // all sound off
1925 KillAllVoices(pEngineChannel, itControlChangeEvent);
1926 break;
1927 }
1928 case 121: { // reset all controllers
1929 pChannel->ResetControllers();
1930 break;
1931 }
1932 case 123: { // all notes off
1933 #if CONFIG_PROCESS_ALL_NOTES_OFF
1934 pChannel->ReleaseAllVoices(itControlChangeEvent);
1935 #endif // CONFIG_PROCESS_ALL_NOTES_OFF
1936 break;
1937 }
1938 case 126: { // mono mode on
1939 if (!pChannel->SoloMode)
1940 KillAllVoices(pEngineChannel, itControlChangeEvent);
1941 pChannel->SoloMode = true;
1942 break;
1943 }
1944 case 127: { // poly mode on
1945 if (pChannel->SoloMode)
1946 KillAllVoices(pEngineChannel, itControlChangeEvent);
1947 pChannel->SoloMode = false;
1948 break;
1949 }
1950 }
1951
1952 // reset cached RPN/NRPN parameter number and data in case this
1953 // CC event had nothing to do with RPN/NRPN
1954 if (!bIsRpn && pChannel->GetMidiRpnParameter() >= 0)
1955 pChannel->ResetMidiRpnParameter();
1956 if (!bIsNrpn && pChannel->GetMidiNrpnParameter() >= 0)
1957 pChannel->ResetMidiNrpnParameter();
1958 }
1959
1960 /**
1961 * Process MIDI RPN events with hard coded behavior.
1962 *
1963 * @param pEngineChannel - engine channel on which the MIDI RPN
1964 * event was received
1965 * @param itRpnEvent - the actual MIDI RPN event
1966 */
1967 void ProcessHardcodedRpn(EngineChannel* pEngineChannel,
1968 Pool<Event>::Iterator& itRpnEvent)
1969 {
1970 EngineChannelBase<V, R, I>* pChannel =
1971 static_cast<EngineChannelBase<V, R, I>*>(pEngineChannel);
1972
1973 if (itRpnEvent->Param.RPN.Parameter == 2) { // coarse tuning in half tones
1974 int transpose = (int) itRpnEvent->Param.RPN.ValueMSB() - 64;
1975 // limit to +- two octaves for now
1976 transpose = RTMath::Min(transpose, 24);
1977 transpose = RTMath::Max(transpose, -24);
1978 pChannel->GlobalTranspose = transpose;
1979 // workaround, so we won't have hanging notes
1980 pChannel->ReleaseAllVoices(itRpnEvent);
1981 } else if (itRpnEvent->Param.RPN.Parameter == 16383) { // null function RPN
1982 // disable subsequent data entry/increment/decrement processing
1983 pChannel->ResetMidiRpnParameter();
1984 }
1985 }
1986
1987 /**
1988 * Process MIDI NRPN events with hard coded behavior.
1989 *
1990 * @param pEngineChannel - engine channel on which the MIDI NRPN
1991 * event was received
1992 * @param itRpnEvent - the actual MIDI NRPN event
1993 */
1994 void ProcessHardcodedNrpn(EngineChannel* pEngineChannel,
1995 Pool<Event>::Iterator& itNrpnEvent)
1996 {
1997 EngineChannelBase<V, R, I>* pChannel =
1998 static_cast<EngineChannelBase<V, R, I>*>(pEngineChannel);
1999
2000 switch (itNrpnEvent->Param.NRPN.ParameterMSB()) {
2001 case 0x1a: { // volume level of note (Roland GS NRPN)
2002 const uint note = itNrpnEvent->Param.NRPN.ParameterLSB();
2003 const uint vol = itNrpnEvent->Param.NRPN.ValueMSB();
2004 dmsg(4,("Note Volume NRPN received (note=%d,vol=%d).\n", note, vol));
2005 if (note < 128 && vol < 128)
2006 pChannel->pMIDIKeyInfo[note].Volume = VolumeCurve[vol];
2007 break;
2008 }
2009 case 0x1c: { // panpot of note (Roland GS NRPN)
2010 const uint note = itNrpnEvent->Param.NRPN.ParameterLSB();
2011 const uint pan = itNrpnEvent->Param.NRPN.ValueMSB();
2012 dmsg(4,("Note Pan NRPN received (note=%d,pan=%d).\n", note, pan));
2013 if (note < 128 && pan < 128) {
2014 pChannel->pMIDIKeyInfo[note].PanLeft = PanCurve[128 - pan];
2015 pChannel->pMIDIKeyInfo[note].PanRight = PanCurve[pan];
2016 }
2017 break;
2018 }
2019 case 0x1d: { // reverb send of note (Roland GS NRPN)
2020 const uint note = itNrpnEvent->Param.NRPN.ParameterLSB();
2021 const float reverb = float(itNrpnEvent->Param.NRPN.Value) / 16383.f;
2022 dmsg(4,("Note Reverb Send NRPN received (note=%d,send=%f).\n", note, reverb));
2023 if (note < 128)
2024 pChannel->pMIDIKeyInfo[note].ReverbSend = reverb;
2025 break;
2026 }
2027 case 0x1e: { // chorus send of note (Roland GS NRPN)
2028 const uint note = itNrpnEvent->Param.NRPN.ParameterLSB();
2029 const float chorus = float(itNrpnEvent->Param.NRPN.Value) / 16383.f;
2030 dmsg(4,("Note Chorus Send NRPN received (note=%d,send=%f).\n", note, chorus));
2031 if (note < 128)
2032 pChannel->pMIDIKeyInfo[note].ChorusSend = chorus;
2033 break;
2034 }
2035 case 0x7f: {
2036 if (itNrpnEvent->Param.NRPN.ParameterLSB() == 0x7f) { // null function NRPN
2037 // disable subsequent data entry/increment/decrement processing
2038 pChannel->ResetMidiNrpnParameter();
2039 }
2040 break;
2041 }
2042 }
2043 }
2044
2045 virtual D* CreateDiskThread() = 0;
2046
2047 /**
2048 * Assigns and triggers a new voice for the respective MIDI key.
2049 *
2050 * @param pEngineChannel - engine channel on which this event occurred on
2051 * @param itNoteOnEvent - key, velocity and time stamp of the event
2052 */
2053 virtual void ProcessNoteOn(EngineChannel* pEngineChannel, Pool<Event>::Iterator& itNoteOnEvent) OVERRIDE {
2054 EngineChannelBase<V, R, I>* pChannel =
2055 static_cast<EngineChannelBase<V, R, I>*>(pEngineChannel);
2056
2057 const int key = itNoteOnEvent->Param.Note.Key;
2058 const int vel = itNoteOnEvent->Param.Note.Velocity;
2059 if (key < 0 || key > 127) return; // ignore event, key outside allowed key range
2060
2061 MidiKey* pKey = &pChannel->pMIDIKeyInfo[key];
2062
2063 // There are real MIDI note-on events (Event::type_note_on) and
2064 // programmatically spawned notes (Event::type_play_note). We have
2065 // to distinguish between them, since certain processing below
2066 // must only be done on real MIDI note-on events (i.e. for
2067 // correctly updating which MIDI keys are currently pressed down).
2068 const bool isRealMIDINoteOnEvent = itNoteOnEvent->Type == Event::type_note_on;
2069
2070 if (isRealMIDINoteOnEvent)
2071 pChannel->listeners.PreProcessNoteOn(key, vel);
2072
2073 #if !CONFIG_PROCESS_MUTED_CHANNELS
2074 if (pEngineChannel->GetMute()) { // skip if sampler channel is muted
2075 if (isRealMIDINoteOnEvent)
2076 pChannel->listeners.PostProcessNoteOn(key, vel);
2077 return;
2078 }
2079 #endif
2080
2081 if (!pChannel->pInstrument) {
2082 if (isRealMIDINoteOnEvent)
2083 pChannel->listeners.PostProcessNoteOn(key, vel);
2084 return; // ignore if no instrument loaded
2085 }
2086
2087 // move note on event to the key's own event list
2088 RTList<Event>::Iterator itNoteOnEventOnKeyList = itNoteOnEvent.moveToEndOf(pKey->pEvents);
2089
2090 // if Solo Mode then kill all already active voices
2091 if (pChannel->SoloMode && isRealMIDINoteOnEvent) {
2092 Pool<uint>::Iterator itYoungestKey = pChannel->pActiveKeys->last();
2093 if (itYoungestKey) {
2094 const int iYoungestKey = *itYoungestKey;
2095 const MidiKey* pOtherKey = &pChannel->pMIDIKeyInfo[iYoungestKey];
2096 if (pOtherKey->Active) {
2097 // get final portamento position of currently active voice
2098 if (pChannel->PortamentoMode) {
2099 NoteIterator itNote = pOtherKey->pActiveNotes->last();
2100 if (itNote) {
2101 VoiceIterator itVoice = itNote->pActiveVoices->last();
2102 if (itVoice) itVoice->UpdatePortamentoPos(itNoteOnEventOnKeyList);
2103 }
2104 }
2105 // kill all voices on the (other) key
2106 for (NoteIterator itNote = pOtherKey->pActiveNotes->first(); itNote; ++itNote) {
2107 VoiceIterator itVoiceToBeKilled = itNote->pActiveVoices->first();
2108 VoiceIterator end = itNote->pActiveVoices->end();
2109 for (; itVoiceToBeKilled != end; ++itVoiceToBeKilled) {
2110 if (!(itVoiceToBeKilled->Type & Voice::type_release_trigger))
2111 itVoiceToBeKilled->Kill(itNoteOnEventOnKeyList);
2112 }
2113 }
2114 }
2115 }
2116 // set this key as 'currently active solo key'
2117 pChannel->SoloKey = key;
2118 }
2119
2120 if (isRealMIDINoteOnEvent) {
2121 pChannel->ProcessKeySwitchChange(key);
2122
2123 pKey->KeyPressed = true; // the MIDI key was now pressed down
2124 pChannel->KeyDown[key] = true; // just used as built-in %KEY_DOWN script variable
2125 pKey->Velocity = itNoteOnEventOnKeyList->Param.Note.Velocity;
2126 pKey->NoteOnTime = FrameTime + itNoteOnEventOnKeyList->FragmentPos(); // will be used to calculate note length
2127 }
2128
2129 // cancel release process of voices on this key if needed
2130 if (pKey->Active && !pChannel->SustainPedal && isRealMIDINoteOnEvent) {
2131 RTList<Event>::Iterator itCancelReleaseEvent = pKey->pEvents->allocAppend();
2132 if (itCancelReleaseEvent) {
2133 *itCancelReleaseEvent = *itNoteOnEventOnKeyList; // copy event
2134 itCancelReleaseEvent->Type = Event::type_cancel_release_key; // transform event type
2135 }
2136 else dmsg(1,("Event pool emtpy!\n"));
2137 }
2138
2139 TriggerNewVoices(pEngineChannel, itNoteOnEventOnKeyList);
2140
2141 // if neither a voice was spawned or postponed then remove note on event from key again
2142 if (!pKey->Active && !pKey->VoiceTheftsQueued)
2143 pKey->pEvents->free(itNoteOnEventOnKeyList);
2144
2145 if (isRealMIDINoteOnEvent && (!pChannel->SoloMode || pChannel->PortamentoPos < 0.0f))
2146 pChannel->PortamentoPos = (float) key;
2147
2148 //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?
2149 if (pKey->pRoundRobinIndex) {
2150 (*pKey->pRoundRobinIndex)++; // counter specific for the key or region
2151 pChannel->RoundRobinIndex++; // common counter for the channel
2152 }
2153
2154 if (isRealMIDINoteOnEvent)
2155 pChannel->listeners.PostProcessNoteOn(key, vel);
2156 }
2157
2158 /**
2159 * Allocate and trigger new voice(s) for the key.
2160 */
2161 virtual void TriggerNewVoices (
2162 EngineChannel* pEngineChannel,
2163 RTList<Event>::Iterator& itNoteOnEvent,
2164 bool HandleKeyGroupConflicts = true
2165 ) = 0;
2166
2167 /**
2168 * Allocate and trigger release voice(s) for the key.
2169 */
2170 virtual void TriggerReleaseVoices (
2171 EngineChannel* pEngineChannel,
2172 RTList<Event>::Iterator& itNoteOffEvent
2173 ) = 0;
2174
2175 /**
2176 * Releases the voices on the given key if sustain pedal is not pressed.
2177 * If sustain is pressed, the release of the note will be postponed until
2178 * sustain pedal will be released or voice turned inactive by itself (e.g.
2179 * due to completion of sample playback).
2180 *
2181 * @param pEngineChannel - engine channel on which this event occurred on
2182 * @param itNoteOffEvent - key, velocity and time stamp of the event
2183 */
2184 virtual void ProcessNoteOff(EngineChannel* pEngineChannel, Pool<Event>::Iterator& itNoteOffEvent) OVERRIDE {
2185 EngineChannelBase<V, R, I>* pChannel = static_cast<EngineChannelBase<V, R, I>*>(pEngineChannel);
2186
2187 const int iKey = itNoteOffEvent->Param.Note.Key;
2188 const int vel = itNoteOffEvent->Param.Note.Velocity;
2189 if (iKey < 0 || iKey > 127) return; // ignore event, key outside allowed key range
2190
2191 MidiKey* pKey = &pChannel->pMIDIKeyInfo[iKey];
2192
2193 // There are real MIDI note-off events (Event::type_note_off) and
2194 // programmatically spawned notes (Event::type_stop_note). We have
2195 // to distinguish between them, since certain processing below
2196 // must only be done on real MIDI note-off events (i.e. for
2197 // correctly updating which MIDI keys are currently pressed down),
2198 // plus a stop-note event just releases voices of one particular
2199 // note, whereas a note-off event releases all voices on a
2200 // particular MIDI key instead.
2201 const bool isRealMIDINoteOffEvent = itNoteOffEvent->Type == Event::type_note_off;
2202
2203 if (isRealMIDINoteOffEvent)
2204 pChannel->listeners.PreProcessNoteOff(iKey, vel);
2205
2206 #if !CONFIG_PROCESS_MUTED_CHANNELS
2207 if (pEngineChannel->GetMute()) { // skip if sampler channel is muted
2208 if (isRealMIDINoteOffEvent)
2209 pChannel->listeners.PostProcessNoteOff(iKey, vel);
2210 return;
2211 }
2212 #endif
2213
2214 if (isRealMIDINoteOffEvent) {
2215 pKey->KeyPressed = false; // the MIDI key was now released
2216 pChannel->KeyDown[iKey] = false; // just used as built-in %KEY_DOWN script variable
2217 }
2218
2219 // move event to the key's own event list
2220 RTList<Event>::Iterator itNoteOffEventOnKeyList = itNoteOffEvent.moveToEndOf(pKey->pEvents);
2221
2222 if (isRealMIDINoteOffEvent) {
2223 bool bShouldRelease = pKey->Active && pChannel->ShouldReleaseVoice(itNoteOffEventOnKeyList->Param.Note.Key);
2224
2225 // in case Solo Mode is enabled, kill all voices on this key and respawn a voice on the highest pressed key (if any)
2226 if (pChannel->SoloMode && pChannel->pInstrument) { //TODO: this feels like too much code just for handling solo mode :P
2227 bool bOtherKeysPressed = false;
2228 if (iKey == pChannel->SoloKey) {
2229 pChannel->SoloKey = -1;
2230 // if there's still a key pressed down, respawn a voice (group) on the highest key
2231 for (int i = 127; i > 0; i--) {
2232 MidiKey* pOtherKey = &pChannel->pMIDIKeyInfo[i];
2233 if (pOtherKey->KeyPressed) {
2234 bOtherKeysPressed = true;
2235 // make the other key the new 'currently active solo key'
2236 pChannel->SoloKey = i;
2237 // get final portamento position of currently active voice
2238 if (pChannel->PortamentoMode) {
2239 NoteIterator itNote = pKey->pActiveNotes->first();
2240 VoiceIterator itVoice = itNote->pActiveVoices->first();
2241 if (itVoice) itVoice->UpdatePortamentoPos(itNoteOffEventOnKeyList);
2242 }
2243 // create a pseudo note on event
2244 RTList<Event>::Iterator itPseudoNoteOnEvent = pOtherKey->pEvents->allocAppend();
2245 if (itPseudoNoteOnEvent) {
2246 // copy event
2247 *itPseudoNoteOnEvent = *itNoteOffEventOnKeyList;
2248 // transform event to a note on event
2249 itPseudoNoteOnEvent->Type = Event::type_note_on; //FIXME: should probably use Event::type_play_note instead (to avoid i.e. hanging notes)
2250 itPseudoNoteOnEvent->Param.Note.Key = i;
2251 itPseudoNoteOnEvent->Param.Note.Velocity = pOtherKey->Velocity;
2252 // assign a new note to this note-on event
2253 if (LaunchNewNote(pChannel, itPseudoNoteOnEvent)) {
2254 // allocate and trigger new voice(s) for the other key
2255 TriggerNewVoices(pChannel, itPseudoNoteOnEvent, false);
2256 }
2257 // if neither a voice was spawned or postponed then remove note on event from key again
2258 if (!pOtherKey->Active && !pOtherKey->VoiceTheftsQueued)
2259 pOtherKey->pEvents->free(itPseudoNoteOnEvent);
2260
2261 } else dmsg(1,("Could not respawn voice, no free event left\n"));
2262 break; // done
2263 }
2264 }
2265 }
2266 if (bOtherKeysPressed) {
2267 if (pKey->Active) { // kill all voices on this key
2268 bShouldRelease = false; // no need to release, as we kill it here
2269 for (NoteIterator itNote = pKey->pActiveNotes->first(); itNote; ++itNote) {
2270 VoiceIterator itVoiceToBeKilled = itNote->pActiveVoices->first();
2271 VoiceIterator end = itNote->pActiveVoices->end();
2272 for (; itVoiceToBeKilled != end; ++itVoiceToBeKilled) {
2273 if (!(itVoiceToBeKilled->Type & Voice::type_release_trigger))
2274 itVoiceToBeKilled->Kill(itNoteOffEventOnKeyList);
2275 }
2276 }
2277 }
2278 } else pChannel->PortamentoPos = -1.0f;
2279 }
2280
2281 // if no solo mode (the usual case) or if solo mode and no other key pressed, then release voices on this key if needed
2282 if (bShouldRelease) {
2283 itNoteOffEventOnKeyList->Type = Event::type_release_key; // transform event type
2284 // spawn release triggered voice(s) if needed
2285 if (pKey->ReleaseTrigger & release_trigger_noteoff)
2286 ProcessReleaseTrigger(pChannel, itNoteOffEventOnKeyList, pKey);
2287 }
2288 } else if (itNoteOffEventOnKeyList->Type == Event::type_stop_note) {
2289 // This programmatically caused event is caused by a call to
2290 // the built-in instrument script function note_off(). In
2291 // contrast to a real MIDI note-off event the stop-note
2292 // event just intends to release voices of one particular note.
2293 NoteBase* pNote = pChannel->pEngine->NoteByID( itNoteOffEventOnKeyList->Param.Note.ID );
2294 if (pNote) { // the requested note is still alive ...
2295 itNoteOffEventOnKeyList->Type = Event::type_release_note; // transform event type
2296 } else { // note is dead and gone ..
2297 pKey->pEvents->free(itNoteOffEventOnKeyList); // remove stop-note event from key again
2298 return; // prevent event to be removed a 2nd time below
2299 }
2300 }
2301
2302 // if neither a voice was spawned or postponed on this key then remove note off event from key again
2303 if (!pKey->Active && !pKey->VoiceTheftsQueued)
2304 pKey->pEvents->free(itNoteOffEventOnKeyList);
2305
2306 if (isRealMIDINoteOffEvent)
2307 pChannel->listeners.PostProcessNoteOff(iKey, vel);
2308 }
2309
2310 /**
2311 * Called on sustain pedal up events to check and if required,
2312 * launch release trigger voices on the respective active key.
2313 *
2314 * @param pEngineChannel - engine channel on which this event occurred on
2315 * @param itEvent - release trigger event (contains note number)
2316 */
2317 virtual void ProcessReleaseTriggerBySustain(EngineChannel* pEngineChannel, RTList<Event>::Iterator& itEvent) OVERRIDE {
2318 EngineChannelBase<V, R, I>* pChannel = static_cast<EngineChannelBase<V, R, I>*>(pEngineChannel);
2319
2320 const int iKey = itEvent->Param.Note.Key;
2321 if (iKey < 0 || iKey > 127) return; // ignore event, key outside allowed key range
2322
2323 MidiKey* pKey = &pChannel->pMIDIKeyInfo[iKey];
2324
2325 ProcessReleaseTrigger(pChannel, itEvent, pKey);
2326 }
2327
2328 /**
2329 * Called on note-off and sustain pedal up events to check and if
2330 * required, launch release trigger voices on the respective active
2331 * key.
2332 *
2333 * @param pEngineChannel - engine channel on which this event occurred on
2334 * @param itEvent - note off event / release trigger event
2335 * @param pKey - key on which the release trigger voices shall be spawned
2336 */
2337 inline void ProcessReleaseTrigger(EngineChannelBase<V, R, I>* pChannel, RTList<Event>::Iterator& itEvent, MidiKey* pKey) {
2338 // spawn release triggered voice(s) if needed
2339 if (pKey->ReleaseTrigger && pChannel->pInstrument) {
2340 // assign a new note to this release event
2341 if (LaunchNewNote(pChannel, itEvent)) {
2342 // allocate and trigger new release voice(s)
2343 TriggerReleaseVoices(pChannel, itEvent);
2344 }
2345 pKey->ReleaseTrigger = release_trigger_none;
2346 }
2347 }
2348
2349 /**
2350 * Called on "kill note" events, which currently only happens on
2351 * built-in real-time instrument script function fade_out(). This
2352 * method only fulfills one task: moving the even to the Note's own
2353 * event list so that its voices can process the kill event sample
2354 * accurately.
2355 */
2356 void ProcessKillNote(EngineChannel* pEngineChannel, RTList<Event>::Iterator& itEvent) {
2357 EngineChannelBase<V, R, I>* pChannel = static_cast<EngineChannelBase<V, R, I>*>(pEngineChannel);
2358
2359 NoteBase* pNote = pChannel->pEngine->NoteByID( itEvent->Param.Note.ID );
2360 if (!pNote || pNote->hostKey < 0 || pNote->hostKey >= 128) return;
2361
2362 // move note kill event to its MIDI key
2363 MidiKey* pKey = &pChannel->pMIDIKeyInfo[pNote->hostKey];
2364 itEvent.moveToEndOf(pKey->pEvents);
2365 }
2366
2367 /**
2368 * Called on note synthesis parameter change events. These are
2369 * internal events caused by calling built-in real-time instrument
2370 * script functions like change_vol(), change_tune(), etc.
2371 *
2372 * This method performs two tasks:
2373 *
2374 * - It converts the event's relative values changes (Deltas) to
2375 * the respective final new synthesis parameter value (AbsValue),
2376 * for that particular moment of the event that is.
2377 *
2378 * - It moves the individual events to the Note's own event list
2379 * (or actually to the event list of the MIDI key), so that
2380 * voices can process those events sample accurately.
2381 *
2382 * @param pEngineChannel - engine channel on which this event occurred on
2383 * @param itEvent - note synthesis parameter change event
2384 */
2385 virtual void ProcessNoteSynthParam(EngineChannel* pEngineChannel, RTList<Event>::Iterator& itEvent) {
2386 EngineChannelBase<V, R, I>* pChannel = static_cast<EngineChannelBase<V, R, I>*>(pEngineChannel);
2387
2388 NoteBase* pNote = pChannel->pEngine->NoteByID( itEvent->Param.NoteSynthParam.NoteID );
2389 if (!pNote || pNote->hostKey < 0 || pNote->hostKey >= 128) return;
2390
2391 switch (itEvent->Param.NoteSynthParam.Type) {
2392 case Event::synth_param_volume:
2393 pNote->apply(itEvent, &NoteBase::_Override::Volume);
2394 break;
2395 case Event::synth_param_volume_time:
2396 pNote->Override.VolumeTime = itEvent->Param.NoteSynthParam.AbsValue = itEvent->Param.NoteSynthParam.Delta;
2397 break;
2398 case Event::synth_param_volume_curve:
2399 itEvent->Param.NoteSynthParam.AbsValue = itEvent->Param.NoteSynthParam.Delta;
2400 pNote->Override.VolumeCurve = (fade_curve_t) itEvent->Param.NoteSynthParam.AbsValue;
2401 break;
2402 case Event::synth_param_pitch:
2403 pNote->apply(itEvent, &NoteBase::_Override::Pitch);
2404 break;
2405 case Event::synth_param_pitch_time:
2406 pNote->Override.PitchTime = itEvent->Param.NoteSynthParam.AbsValue = itEvent->Param.NoteSynthParam.Delta;
2407 break;
2408 case Event::synth_param_pitch_curve:
2409 itEvent->Param.NoteSynthParam.AbsValue = itEvent->Param.NoteSynthParam.Delta;
2410 pNote->Override.PitchCurve = (fade_curve_t) itEvent->Param.NoteSynthParam.AbsValue;
2411 break;
2412 case Event::synth_param_pan:
2413 pNote->apply(itEvent, &NoteBase::_Override::Pan);
2414 break;
2415 case Event::synth_param_pan_time:
2416 pNote->Override.PanTime = itEvent->Param.NoteSynthParam.AbsValue = itEvent->Param.NoteSynthParam.Delta;
2417 break;
2418 case Event::synth_param_pan_curve:
2419 itEvent->Param.NoteSynthParam.AbsValue = itEvent->Param.NoteSynthParam.Delta;
2420 pNote->Override.PanCurve = (fade_curve_t) itEvent->Param.NoteSynthParam.AbsValue;
2421 break;
2422 case Event::synth_param_cutoff:
2423 pNote->apply(itEvent, &NoteBase::_Override::Cutoff);
2424 break;
2425 case Event::synth_param_resonance:
2426 pNote->apply(itEvent, &NoteBase::_Override::Resonance);
2427 break;
2428 case Event::synth_param_attack:
2429 pNote->apply(itEvent, &NoteBase::_Override::Attack);
2430 break;
2431 case Event::synth_param_decay:
2432 pNote->apply(itEvent, &NoteBase::_Override::Decay);
2433 break;
2434 case Event::synth_param_sustain:
2435 pNote->apply(itEvent, &NoteBase::_Override::Sustain);
2436 break;
2437 case Event::synth_param_release:
2438 pNote->apply(itEvent, &NoteBase::_Override::Release);
2439 break;
2440
2441 case Event::synth_param_cutoff_attack:
2442 pNote->apply(itEvent, &NoteBase::_Override::CutoffAttack);
2443 break;
2444 case Event::synth_param_cutoff_decay:
2445 pNote->apply(itEvent, &NoteBase::_Override::CutoffDecay);
2446 break;
2447 case Event::synth_param_cutoff_sustain:
2448 pNote->apply(itEvent, &NoteBase::_Override::CutoffSustain);
2449 break;
2450 case Event::synth_param_cutoff_release:
2451 pNote->apply(itEvent, &NoteBase::_Override::CutoffRelease);
2452 break;
2453
2454 case Event::synth_param_amp_lfo_depth:
2455 pNote->apply(itEvent, &NoteBase::_Override::AmpLFODepth);
2456 break;
2457 case Event::synth_param_amp_lfo_freq:
2458 pNote->apply(itEvent, &NoteBase::_Override::AmpLFOFreq);
2459 break;
2460 case Event::synth_param_cutoff_lfo_depth:
2461 pNote->apply(itEvent, &NoteBase::_Override::CutoffLFODepth);
2462 break;
2463 case Event::synth_param_cutoff_lfo_freq:
2464 pNote->apply(itEvent, &NoteBase::_Override::CutoffLFOFreq);
2465 break;
2466 case Event::synth_param_pitch_lfo_depth:
2467 pNote->apply(itEvent, &NoteBase::_Override::PitchLFODepth);
2468 break;
2469 case Event::synth_param_pitch_lfo_freq:
2470 pNote->apply(itEvent, &NoteBase::_Override::PitchLFOFreq);
2471 break;
2472 }
2473
2474 // move note parameter event to its MIDI key
2475 MidiKey* pKey = &pChannel->pMIDIKeyInfo[pNote->hostKey];
2476 itEvent.moveToEndOf(pKey->pEvents);
2477 }
2478
2479 /**
2480 * Reset all voices and disk thread and clear input event queue and all
2481 * control and status variables. This method is protected by a mutex.
2482 */
2483 virtual void ResetInternal() OVERRIDE {
2484 LockGuard lock(ResetInternalMutex);
2485
2486 // make sure that the engine does not get any sysex messages
2487 // while it's reseting
2488 bool sysexDisabled = MidiInputPort::RemoveSysexListener(this);
2489 SetVoiceCount(0);
2490 ActiveVoiceCountMax = 0;
2491
2492 // reset voice stealing parameters
2493 pVoiceStealingQueue->clear();
2494 itLastStolenVoice = VoiceIterator();
2495 itLastStolenVoiceGlobally = VoiceIterator();
2496 itLastStolenNote = NoteIterator();
2497 itLastStolenNoteGlobally = NoteIterator();
2498 iuiLastStolenKey = RTList<uint>::Iterator();
2499 iuiLastStolenKeyGlobally = RTList<uint>::Iterator();
2500 pLastStolenChannel = NULL;
2501
2502 // reset all notes
2503 pNotePool->clear();
2504 for (NoteIterator itNote = pNotePool->allocAppend(); itNote;
2505 itNote = pNotePool->allocAppend())
2506 {
2507 itNote->reset();
2508 }
2509 pNotePool->clear();
2510
2511 // reset all voices
2512 pVoicePool->clear();
2513 for (VoiceIterator iterVoice = pVoicePool->allocAppend(); iterVoice == pVoicePool->last(); iterVoice = pVoicePool->allocAppend()) {
2514 iterVoice->Reset();
2515 }
2516 pVoicePool->clear();
2517
2518 // reset all engine channels
2519 for (int i = 0; i < engineChannels.size(); i++) {
2520 AbstractEngineChannel* pEngineChannel =
2521 static_cast<AbstractEngineChannel*>(engineChannels[i]);
2522 pEngineChannel->ResetInternal(false/*don't reset engine*/);
2523 }
2524
2525 // reset disk thread
2526 if (pDiskThread) pDiskThread->Reset();
2527
2528 // delete all input events
2529 pEventQueue->init();
2530 pSysexBuffer->init();
2531 if (sysexDisabled) MidiInputPort::AddSysexListener(this);
2532 }
2533
2534 /**
2535 * Kills all voices on an engine channel as soon as possible. Voices
2536 * won't get into release state, their volume level will be ramped down
2537 * as fast as possible.
2538 *
2539 * @param pEngineChannel - engine channel on which all voices should be killed
2540 * @param itKillEvent - event which caused this killing of all voices
2541 */
2542 virtual void KillAllVoices(EngineChannel* pEngineChannel, Pool<Event>::Iterator& itKillEvent) OVERRIDE {
2543 EngineChannelBase<V, R, I>* pChannel = static_cast<EngineChannelBase<V, R, I>*>(pEngineChannel);
2544 int count = pChannel->KillAllVoices(itKillEvent);
2545 VoiceSpawnsLeft -= count; //FIXME: just a temporary workaround, we should check the cause in StealVoice() instead
2546 }
2547
2548 /**
2549 * Allocates and triggers a new voice. This method will usually be
2550 * called by the ProcessNoteOn() method and by the voices itself
2551 * (e.g. to spawn further voices on the same key for layered sounds).
2552 *
2553 * @param pEngineChannel - engine channel on which this event occurred on
2554 * @param itNoteOnEvent - key, velocity and time stamp of the event
2555 * @param iLayer - layer index for the new voice (optional - only
2556 * in case of layered sounds of course)
2557 * @param ReleaseTriggerVoice - if new voice is a release triggered voice
2558 * (optional, default = false)
2559 * @param VoiceStealing - if voice stealing should be performed
2560 * when there is no free voice
2561 * (optional, default = true)
2562 * @param HandleKeyGroupConflicts - if voices should be killed due to a
2563 * key group conflict
2564 * @returns pointer to new voice or NULL if there was no free voice or
2565 * if the voice wasn't triggered (for example when no region is
2566 * defined for the given key).
2567 */
2568 virtual PoolVoiceIterator LaunchVoice (
2569 EngineChannel* pEngineChannel,
2570 Pool<Event>::Iterator& itNoteOnEvent,
2571 int iLayer,
2572 bool ReleaseTriggerVoice,
2573 bool VoiceStealing,
2574 bool HandleKeyGroupConflicts
2575 ) = 0;
2576
2577 virtual int GetMinFadeOutSamples() OVERRIDE { return MinFadeOutSamples; }
2578
2579 int InitNewVoice (
2580 EngineChannelBase<V, R, I>* pChannel,
2581 R* pRegion,
2582 Pool<Event>::Iterator& itNoteOnEvent,
2583 Voice::type_t VoiceType,
2584 int iLayer,
2585 int iKeyGroup,
2586 bool ReleaseTriggerVoice,
2587 bool VoiceStealing,
2588 typename Pool<V>::Iterator& itNewVoice
2589 ) {
2590 int key = itNoteOnEvent->Param.Note.Key;
2591 typename MidiKeyboardManager<V>::MidiKey* pKey = &pChannel->pMIDIKeyInfo[key];
2592 if (itNewVoice) {
2593 // launch the new voice
2594 if (itNewVoice->Trigger(pChannel, itNoteOnEvent, pChannel->Pitch, pRegion, VoiceType, iKeyGroup) < 0) {
2595 dmsg(4,("Voice not triggered\n"));
2596 GetVoicePool()->free(itNewVoice);
2597 }
2598 else { // on success
2599 --VoiceSpawnsLeft;
2600
2601 // should actually be superfluous now, since this is
2602 // already done in LaunchNewNote()
2603 pChannel->markKeyAsActive(pKey);
2604
2605 if (itNewVoice->Type & Voice::type_release_trigger_required)
2606 pKey->ReleaseTrigger |= itNewVoice->GetReleaseTriggerFlags(); // mark key for the need of release triggered voice(s)
2607 return 0; // success
2608 }
2609 }
2610 else if (VoiceStealing) {
2611 // try to steal one voice
2612 int result = StealVoice(pChannel, itNoteOnEvent);
2613 if (!result) { // voice stolen successfully
2614 // put note-on event into voice-stealing queue, so it will be reprocessed after killed voice died
2615 RTList<Event>::Iterator itStealEvent = pVoiceStealingQueue->allocAppend();
2616 if (itStealEvent) {
2617 *itStealEvent = *itNoteOnEvent; // copy event
2618 itStealEvent->Param.Note.Layer = iLayer;
2619 itStealEvent->Param.Note.ReleaseTrigger = ReleaseTriggerVoice;
2620 pKey->VoiceTheftsQueued++;
2621 }
2622 else dmsg(1,("Voice stealing queue full!\n"));
2623 }
2624 }
2625
2626 return -1;
2627 }
2628
2629 /**
2630 * Checks whether scale tuning setting has been changed since last
2631 * time this method was called, if yes, it recalculates the pitch
2632 * for all active voices.
2633 */
2634 void ProcessScaleTuningChange() {
2635 const bool changed = ScaleTuningChanged.readAndReset();
2636 if (!changed) return;
2637
2638 for (int i = 0; i < engineChannels.size(); i++) {
2639 EngineChannelBase<V, R, I>* channel =
2640 static_cast<EngineChannelBase<V, R, I>*>(engineChannels[i]);
2641 channel->OnScaleTuningChanged();
2642 }
2643 }
2644
2645 private:
2646 Pool< Note<V> >* pNotePool;
2647 Pool<note_id_t> noteIDPool;
2648 Pool<V>* pVoicePool; ///< Contains all voices that can be activated.
2649 Pool<RR*> SuspendedRegions;
2650 Mutex SuspendedRegionsMutex;
2651 Condition SuspensionChangeOngoing;
2652 RR* pPendingRegionSuspension;
2653 RR* pPendingRegionResumption;
2654 int iPendingStreamDeletions;
2655 };
2656
2657 template <class V, class RR, class R, class D, class IM, class I>
2658 IM EngineBase<V, RR, R, D, IM, I>::instruments;
2659
2660 } // namespace LinuxSampler
2661
2662 #endif /* __LS_ENGINEBASE_H__ */
2663

  ViewVC Help
Powered by ViewVC