/[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 3706 - (show annotations) (download) (as text)
Wed Jan 8 20:39:59 2020 UTC (9 months, 1 week ago) by schoenebeck
File MIME type: text/x-c++hdr
File size: 147367 byte(s)
Fixed compiler warnings about implied type casts.

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

  ViewVC Help
Powered by ViewVC