/[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 2645 - (show annotations) (download) (as text)
Wed Jun 18 00:14:57 2014 UTC (9 years, 10 months ago) by schoenebeck
File MIME type: text/x-c++hdr
File size: 92602 byte(s)
* RT instrument scripts: Pass/preserve polyphonic variable data
  from respective "note" event handler to "release" event handler.
* Fixed theoretical memory leak regarding instrument scripts.
* Bumped version (1.0.0.svn54).

1 /***************************************************************************
2 * *
3 * LinuxSampler - modular, streaming capable sampler *
4 * *
5 * Copyright (C) 2003,2004 by Benno Senoner and Christian Schoenebeck *
6 * Copyright (C) 2005-2008 Christian Schoenebeck *
7 * Copyright (C) 2009-2013 Christian Schoenebeck and Grigor Iliev *
8 * *
9 * This program is free software; you can redistribute it and/or modify *
10 * it under the terms of the GNU General Public License as published by *
11 * the Free Software Foundation; either version 2 of the License, or *
12 * (at your option) any later version. *
13 * *
14 * This program is distributed in the hope that it will be useful, *
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
17 * GNU General Public License for more details. *
18 * *
19 * You should have received a copy of the GNU General Public License *
20 * along with this program; if not, write to the Free Software *
21 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, *
22 * MA 02111-1307 USA *
23 ***************************************************************************/
24
25 #ifndef __LS_ENGINEBASE_H__
26 #define __LS_ENGINEBASE_H__
27
28 #include "AbstractEngine.h"
29 #include "EngineChannelBase.h"
30 #include "common/DiskThreadBase.h"
31 #include "common/MidiKeyboardManager.h"
32 #include "InstrumentManager.h"
33 #include "../common/global_private.h"
34
35
36 namespace LinuxSampler {
37
38 class AbstractEngineChannel;
39
40 template <
41 class V /* Voice */,
42 class RR /* Root Region */,
43 class R /* Region */,
44 class D /* Disk Thread */,
45 class IM /* Instrument Manager */,
46 class I /* Instrument */
47 >
48 class EngineBase: public AbstractEngine, public RegionPools<R>, public VoicePool<V> {
49
50 public:
51 typedef typename RTList<V>::Iterator VoiceIterator;
52 typedef typename Pool<V>::Iterator PoolVoiceIterator;
53 typedef typename RTList<RR*>::Iterator RootRegionIterator;
54 typedef typename MidiKeyboardManager<V>::MidiKey MidiKey;
55
56 EngineBase() : SuspendedRegions(128) {
57 pDiskThread = NULL;
58 pVoicePool = new Pool<V>(GLOBAL_MAX_VOICES);
59 pRegionPool[0] = new Pool<R*>(GLOBAL_MAX_VOICES);
60 pRegionPool[1] = new Pool<R*>(GLOBAL_MAX_VOICES);
61 pVoiceStealingQueue = new RTList<Event>(pEventPool);
62 iMaxDiskStreams = GLOBAL_MAX_STREAMS;
63
64 for (VoiceIterator iterVoice = pVoicePool->allocAppend(); iterVoice == pVoicePool->last(); iterVoice = pVoicePool->allocAppend()) {
65 iterVoice->SetEngine(this);
66 }
67 pVoicePool->clear();
68
69 ResetInternal();
70 ResetScaleTuning();
71 ResetSuspendedRegions();
72 }
73
74 virtual ~EngineBase() {
75 if (pDiskThread) {
76 dmsg(1,("Stopping disk thread..."));
77 pDiskThread->StopThread();
78 delete pDiskThread;
79 dmsg(1,("OK\n"));
80 }
81
82 if (pVoicePool) {
83 pVoicePool->clear();
84 delete pVoicePool;
85 }
86
87 if (pVoiceStealingQueue) delete pVoiceStealingQueue;
88
89 if (pRegionPool[0]) delete pRegionPool[0];
90 if (pRegionPool[1]) delete pRegionPool[1];
91 ResetSuspendedRegions();
92 }
93
94 // implementation of abstract methods derived from class 'LinuxSampler::Engine'
95
96 /**
97 * Let this engine proceed to render the given amount of sample points.
98 * The engine will iterate through all engine channels and render audio
99 * for each engine channel independently. The calculated audio data of
100 * all voices of each engine channel will be placed into the audio sum
101 * buffers of the respective audio output device, connected to the
102 * respective engine channel.
103 *
104 * @param Samples - number of sample points to be rendered
105 * @returns 0 on success
106 */
107 virtual int RenderAudio(uint Samples) OVERRIDE {
108 dmsg(8,("RenderAudio(Samples=%d)\n", Samples));
109
110 // return if engine disabled
111 if (EngineDisabled.Pop()) {
112 dmsg(5,("EngineBase: engine disabled (val=%d)\n",EngineDisabled.GetUnsafe()));
113 EngineDisabled.RttDone();
114 return 0;
115 }
116
117 // process requests for suspending / resuming regions (i.e. to avoid
118 // crashes while these regions are modified by an instrument editor)
119 ProcessSuspensionsChanges();
120
121 // update time of start and end of this audio fragment (as events' time stamps relate to this)
122 pEventGenerator->UpdateFragmentTime(Samples);
123
124 // We only allow the given maximum number of voices to be spawned
125 // in each audio fragment. All subsequent request for spawning new
126 // voices in the same audio fragment will be ignored.
127 VoiceSpawnsLeft = MaxVoices();
128
129 // get all events from the engine's global input event queue which belong to the current fragment
130 // (these are usually just SysEx messages)
131 ImportEvents(Samples);
132
133 // process engine global events (these are currently only MIDI System Exclusive messages)
134 {
135 RTList<Event>::Iterator itEvent = pGlobalEvents->first();
136 RTList<Event>::Iterator end = pGlobalEvents->end();
137 for (; itEvent != end; ++itEvent) {
138 switch (itEvent->Type) {
139 case Event::type_sysex:
140 dmsg(5,("Engine: Sysex received\n"));
141 ProcessSysex(itEvent);
142 break;
143 }
144 }
145 }
146
147 // In case scale tuning has been changed, recalculate pitch for
148 // all active voices.
149 ProcessScaleTuningChange();
150
151 // reset internal voice counter (just for statistic of active voices)
152 ActiveVoiceCountTemp = 0;
153
154 HandleInstrumentChanges();
155
156 // handle events on all engine channels
157 for (int i = 0; i < engineChannels.size(); i++) {
158 ProcessEvents(engineChannels[i], Samples);
159 }
160
161 // render all 'normal', active voices on all engine channels
162 for (int i = 0; i < engineChannels.size(); i++) {
163 RenderActiveVoices(engineChannels[i], Samples);
164 }
165
166 // now that all ordinary voices on ALL engine channels are rendered, render new stolen voices
167 RenderStolenVoices(Samples);
168
169 // handle audio routing for engine channels with FX sends
170 for (int i = 0; i < engineChannels.size(); i++) {
171 AbstractEngineChannel* pChannel = static_cast<AbstractEngineChannel*>(engineChannels[i]);
172 if (pChannel->fxSends.empty()) continue; // ignore if no FX sends
173 RouteAudio(engineChannels[i], Samples);
174 }
175
176 // handle cleanup on all engine channels for the next audio fragment
177 for (int i = 0; i < engineChannels.size(); i++) {
178 PostProcess(engineChannels[i]);
179 }
180
181
182 // empty the engine's event list for the next audio fragment
183 ClearEventLists();
184
185 // reset voice stealing for the next audio fragment
186 pVoiceStealingQueue->clear();
187
188 // just some statistics about this engine instance
189 SetVoiceCount(ActiveVoiceCountTemp);
190 if (VoiceCount() > ActiveVoiceCountMax) ActiveVoiceCountMax = VoiceCount();
191
192 // in case regions were previously suspended and we killed voices
193 // with disk streams due to that, check if those streams have finally
194 // been deleted by the disk thread
195 if (iPendingStreamDeletions) ProcessPendingStreamDeletions();
196
197 // Release the instrument change command. (This has to
198 // be done after all voices have been rendered and not
199 // in HandleInstrumentChanges, as the RegionsInUse
200 // list has been built up by the voice renderers.)
201 for (int i = 0; i < engineChannels.size(); i++) {
202 EngineChannelBase<V, R, I>* channel =
203 static_cast<EngineChannelBase<V, R, I>*>(engineChannels[i]);
204 channel->InstrumentChangeCommandReader.Unlock();
205 }
206 FrameTime += Samples;
207
208 EngineDisabled.RttDone();
209 return 0;
210 }
211
212 virtual int MaxVoices() OVERRIDE { return pVoicePool->poolSize(); }
213
214 virtual void SetMaxVoices(int iVoices) throw (Exception) OVERRIDE {
215 if (iVoices < 1)
216 throw Exception("Maximum voices for an engine cannot be set lower than 1");
217
218 SuspendAll();
219
220 // NOTE: we need to clear pRegionsInUse before deleting pDimRegionPool,
221 // otherwise memory corruption will occur if there are active voices (see bug #118)
222 for (int iChannel = 0; iChannel < engineChannels.size(); iChannel++) {
223 EngineChannelBase<V, R, I>* pChannel = static_cast<EngineChannelBase<V, R, I>*>(engineChannels[iChannel]);
224 pChannel->ClearRegionsInUse();
225 }
226
227 if (pRegionPool[0]) delete pRegionPool[0];
228 if (pRegionPool[1]) delete pRegionPool[1];
229
230 pRegionPool[0] = new Pool<R*>(iVoices);
231 pRegionPool[1] = new Pool<R*>(iVoices);
232
233 for (int iChannel = 0; iChannel < engineChannels.size(); iChannel++) {
234 EngineChannelBase<V, R, I>* pChannel = static_cast<EngineChannelBase<V, R, I>*>(engineChannels[iChannel]);
235 pChannel->ResetRegionsInUse(pRegionPool);
236 }
237
238 try {
239 pVoicePool->resizePool(iVoices);
240 } catch (...) {
241 throw Exception("FATAL: Could not resize voice pool!");
242 }
243
244 for (VoiceIterator iterVoice = pVoicePool->allocAppend(); iterVoice == pVoicePool->last(); iterVoice = pVoicePool->allocAppend()) {
245 iterVoice->SetEngine(this);
246 iterVoice->pDiskThread = this->pDiskThread;
247 }
248 pVoicePool->clear();
249
250 PostSetMaxVoices(iVoices);
251 ResumeAll();
252 }
253
254 /** Called after the new max number of voices is set and before resuming the engine. */
255 virtual void PostSetMaxVoices(int iVoices) { }
256
257 virtual uint DiskStreamCount() OVERRIDE { return (pDiskThread) ? pDiskThread->GetActiveStreamCount() : 0; }
258 virtual uint DiskStreamCountMax() OVERRIDE { return (pDiskThread) ? pDiskThread->ActiveStreamCountMax : 0; }
259 virtual int MaxDiskStreams() OVERRIDE { return iMaxDiskStreams; }
260
261 virtual void SetMaxDiskStreams(int iStreams) throw (Exception) OVERRIDE {
262 if (iStreams < 0)
263 throw Exception("Maximum disk streams for an engine cannot be set lower than 0");
264
265 SuspendAll();
266
267 iMaxDiskStreams = iStreams;
268
269 // reconnect to audio output device, because that will automatically
270 // recreate the disk thread with the required amount of streams
271 if (pAudioOutputDevice) Connect(pAudioOutputDevice);
272
273 ResumeAll();
274 }
275
276 virtual String DiskStreamBufferFillBytes() OVERRIDE { return (pDiskThread) ? pDiskThread->GetBufferFillBytes() : ""; }
277 virtual String DiskStreamBufferFillPercentage() OVERRIDE { return (pDiskThread) ? pDiskThread->GetBufferFillPercentage() : ""; }
278 virtual InstrumentManager* GetInstrumentManager() OVERRIDE { return &instruments; }
279
280 /**
281 * Connect this engine instance with the given audio output device.
282 * This method will be called when an Engine instance is created.
283 * All of the engine's data structures which are dependant to the used
284 * audio output device / driver will be (re)allocated and / or
285 * adjusted appropriately.
286 *
287 * @param pAudioOut - audio output device to connect to
288 */
289 virtual void Connect(AudioOutputDevice* pAudioOut) OVERRIDE {
290 // caution: don't ignore if connecting to the same device here,
291 // because otherwise SetMaxDiskStreams() implementation won't work anymore!
292
293 pAudioOutputDevice = pAudioOut;
294
295 ResetInternal();
296
297 // inform audio driver for the need of two channels
298 try {
299 pAudioOutputDevice->AcquireChannels(2); // default stereo
300 }
301 catch (AudioOutputException e) {
302 String msg = "Audio output device unable to provide 2 audio channels, cause: " + e.Message();
303 throw Exception(msg);
304 }
305
306 this->MaxSamplesPerCycle = pAudioOutputDevice->MaxSamplesPerCycle();
307 this->SampleRate = pAudioOutputDevice->SampleRate();
308
309 MinFadeOutSamples = int(double(SampleRate) * CONFIG_EG_MIN_RELEASE_TIME) - 1;
310 if (MaxSamplesPerCycle < MinFadeOutSamples) {
311 std::cerr << "EngineBase: WARNING, CONFIG_EG_MIN_RELEASE_TIME "
312 << "too big for current audio fragment size & sampling rate! "
313 << "May lead to click sounds if voice stealing chimes in!\n" << std::flush;
314 // force volume ramp downs at the beginning of each fragment
315 MinFadeOutSamples = MaxSamplesPerCycle;
316 // lower minimum release time
317 const float minReleaseTime = (float) MaxSamplesPerCycle / (float) SampleRate;
318 for (VoiceIterator iterVoice = pVoicePool->allocAppend(); iterVoice == pVoicePool->last(); iterVoice = pVoicePool->allocAppend()) {
319 iterVoice->CalculateFadeOutCoeff(minReleaseTime, SampleRate);
320 }
321 pVoicePool->clear();
322 }
323
324 // (re)create disk thread
325 if (this->pDiskThread) {
326 dmsg(1,("Stopping disk thread..."));
327 this->pDiskThread->StopThread();
328 delete this->pDiskThread;
329 dmsg(1,("OK\n"));
330 }
331 this->pDiskThread = CreateDiskThread();
332
333 if (!pDiskThread) {
334 dmsg(0,("EngineBase new diskthread = NULL\n"));
335 exit(EXIT_FAILURE);
336 }
337
338 for (VoiceIterator iterVoice = pVoicePool->allocAppend(); iterVoice == pVoicePool->last(); iterVoice = pVoicePool->allocAppend()) {
339 iterVoice->pDiskThread = this->pDiskThread;
340 dmsg(3,("d"));
341 }
342 pVoicePool->clear();
343
344 // (re)create event generator
345 if (pEventGenerator) delete pEventGenerator;
346 pEventGenerator = new EventGenerator(pAudioOut->SampleRate());
347
348 dmsg(1,("Starting disk thread..."));
349 pDiskThread->StartThread();
350 dmsg(1,("OK\n"));
351
352 bool printEqInfo = true;
353 for (VoiceIterator iterVoice = pVoicePool->allocAppend(); iterVoice == pVoicePool->last(); iterVoice = pVoicePool->allocAppend()) {
354 if (!iterVoice->pDiskThread) {
355 dmsg(0,("Engine -> voice::trigger: !pDiskThread\n"));
356 exit(EXIT_FAILURE);
357 }
358
359 iterVoice->CreateEq();
360
361 if(printEqInfo) {
362 iterVoice->PrintEqInfo();
363 printEqInfo = false;
364 }
365 }
366 pVoicePool->clear();
367
368 // (re)create dedicated voice audio buffers
369 //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
370 if (pDedicatedVoiceChannelLeft) delete pDedicatedVoiceChannelLeft;
371 if (pDedicatedVoiceChannelRight) delete pDedicatedVoiceChannelRight;
372 pDedicatedVoiceChannelLeft = new AudioChannel(0, MaxSamplesPerCycle);
373 pDedicatedVoiceChannelRight = new AudioChannel(1, MaxSamplesPerCycle);
374 }
375
376 // Implementattion for abstract method derived from Engine.
377 virtual void ReconnectAudioOutputDevice() OVERRIDE {
378 SuspendAll();
379 if (pAudioOutputDevice) Connect(pAudioOutputDevice);
380 ResumeAll();
381 }
382
383 /**
384 * Similar to @c Disable() but this method additionally kills all voices
385 * and disk streams and blocks until all voices and disk streams are actually
386 * killed / deleted.
387 *
388 * @e Note: only the original calling thread is able to re-enable the
389 * engine afterwards by calling @c ResumeAll() later on!
390 */
391 virtual void SuspendAll() {
392 dmsg(2,("Engine: Suspending all ...\n"));
393 // stop the engine, so we can safely modify the engine's
394 // data structures from this foreign thread
395 DisableAndLock();
396 // we could also use the respective class member variable here,
397 // but this is probably safer and cleaner
398 int iPendingStreamDeletions = 0;
399 // kill all voices on all engine channels the *die hard* way
400 for (int iChannel = 0; iChannel < engineChannels.size(); iChannel++) {
401 EngineChannelBase<V, R, I>* pEngineChannel =
402 static_cast<EngineChannelBase<V, R, I>*>(engineChannels[iChannel]);
403
404 iPendingStreamDeletions += pEngineChannel->KillAllVoicesImmediately();
405 }
406 // wait until all streams were actually deleted by the disk thread
407 while (iPendingStreamDeletions) {
408 while (
409 iPendingStreamDeletions &&
410 pDiskThread->AskForDeletedStream() != Stream::INVALID_HANDLE
411 ) iPendingStreamDeletions--;
412 if (!iPendingStreamDeletions) break;
413 usleep(10000); // sleep for 10ms
414 }
415 dmsg(2,("EngineBase: Everything suspended.\n"));
416 }
417
418 /**
419 * At the moment same as calling @c Enable() directly, but this might
420 * change in future, so better call this method as counterpart to
421 * @c SuspendAll() instead of @c Enable() !
422 */
423 virtual void ResumeAll() { Enable(); }
424
425 /**
426 * Order the engine to stop rendering audio for the given region.
427 * Additionally this method will block until all voices and their disk
428 * streams associated with that region are actually killed / deleted, so
429 * one can i.e. safely modify the region with an instrument editor after
430 * returning from this method.
431 *
432 * @param pRegion - region the engine shall stop using
433 */
434 virtual void Suspend(RR* pRegion) {
435 dmsg(2,("EngineBase: Suspending Region %x ...\n",pRegion));
436 {
437 LockGuard lock(SuspendedRegionsMutex);
438 SuspensionChangeOngoing.Set(true);
439 pPendingRegionSuspension = pRegion;
440 SuspensionChangeOngoing.WaitAndUnlockIf(true);
441 }
442 dmsg(2,("EngineBase: Region %x suspended.",pRegion));
443 }
444
445 /**
446 * Orders the engine to resume playing back the given region, previously
447 * suspended with @c Suspend() .
448 *
449 * @param pRegion - region the engine shall be allowed to use again
450 */
451 virtual void Resume(RR* pRegion) {
452 dmsg(2,("EngineBase: Resuming Region %x ...\n",pRegion));
453 {
454 LockGuard lock(SuspendedRegionsMutex);
455 SuspensionChangeOngoing.Set(true);
456 pPendingRegionResumption = pRegion;
457 SuspensionChangeOngoing.WaitAndUnlockIf(true);
458 }
459 dmsg(2,("EngineBase: Region %x resumed.\n",pRegion));
460 }
461
462 virtual void ResetSuspendedRegions() {
463 SuspendedRegions.clear();
464 iPendingStreamDeletions = 0;
465 pPendingRegionSuspension = pPendingRegionResumption = NULL;
466 SuspensionChangeOngoing.Set(false);
467 }
468
469 /**
470 * Called by the engine's (audio) thread once per cycle to process requests
471 * from the outer world to suspend or resume a given @c gig::Region .
472 */
473 virtual void ProcessSuspensionsChanges() {
474 // process request for suspending one region
475 if (pPendingRegionSuspension) {
476 // kill all voices on all engine channels that use this region
477 for (int iChannel = 0; iChannel < engineChannels.size(); iChannel++) {
478 EngineChannelBase<V, R, I>* pEngineChannel =
479 static_cast<EngineChannelBase<V, R, I>*>(engineChannels[iChannel]);
480 SuspensionVoiceHandler handler(pPendingRegionSuspension);
481 pEngineChannel->ProcessActiveVoices(&handler);
482 iPendingStreamDeletions += handler.PendingStreamDeletions;
483 }
484 // make sure the region is not yet on the list
485 bool bAlreadySuspended = false;
486 RootRegionIterator iter = SuspendedRegions.first();
487 RootRegionIterator end = SuspendedRegions.end();
488 for (; iter != end; ++iter) { // iterate through all suspended regions
489 if (*iter == pPendingRegionSuspension) { // found
490 bAlreadySuspended = true;
491 dmsg(1,("EngineBase: attempt to suspend an already suspended region !!!\n"));
492 break;
493 }
494 }
495 if (!bAlreadySuspended) {
496 // put the region on the list of suspended regions
497 RootRegionIterator iter = SuspendedRegions.allocAppend();
498 if (iter) {
499 *iter = pPendingRegionSuspension;
500 } else std::cerr << "EngineBase: Could not suspend Region, list is full. This is a bug!!!\n" << std::flush;
501 }
502 // free request slot for next caller (and to make sure that
503 // we're not going to process the same request in the next cycle)
504 pPendingRegionSuspension = NULL;
505 // if no disk stream deletions are pending, awaken other side, as
506 // we're done in this case
507 if (!iPendingStreamDeletions) SuspensionChangeOngoing.Set(false);
508 }
509
510 // process request for resuming one region
511 if (pPendingRegionResumption) {
512 // remove region from the list of suspended regions
513 RootRegionIterator iter = SuspendedRegions.first();
514 RootRegionIterator end = SuspendedRegions.end();
515 for (; iter != end; ++iter) { // iterate through all suspended regions
516 if (*iter == pPendingRegionResumption) { // found
517 SuspendedRegions.free(iter);
518 break; // done
519 }
520 }
521 // free request slot for next caller
522 pPendingRegionResumption = NULL;
523 // awake other side as we're done
524 SuspensionChangeOngoing.Set(false);
525 }
526 }
527
528 /**
529 * Called by the engine's (audio) thread once per cycle to check if
530 * streams of voices that were killed due to suspension request have
531 * finally really been deleted by the disk thread.
532 */
533 virtual void ProcessPendingStreamDeletions() {
534 if (!iPendingStreamDeletions) return;
535 //TODO: or shall we better store a list with stream handles instead of a scalar amount of streams to be deleted? might be safer
536 while (
537 iPendingStreamDeletions &&
538 pDiskThread->AskForDeletedStream() != Stream::INVALID_HANDLE
539 ) iPendingStreamDeletions--;
540 // just for safety ...
541 while (pDiskThread->AskForDeletedStream() != Stream::INVALID_HANDLE);
542 // now that all disk streams are deleted, awake other side as
543 // we're finally done with suspending the requested region
544 if (!iPendingStreamDeletions) SuspensionChangeOngoing.Set(false);
545 }
546
547 /**
548 * Returns @c true if the given region is currently set to be suspended
549 * from being used, @c false otherwise.
550 */
551 virtual bool RegionSuspended(RR* pRegion) {
552 if (SuspendedRegions.isEmpty()) return false;
553 //TODO: or shall we use a sorted container instead of the RTList? might be faster ... or trivial ;-)
554 RootRegionIterator iter = SuspendedRegions.first();
555 RootRegionIterator end = SuspendedRegions.end();
556 for (; iter != end; ++iter) // iterate through all suspended regions
557 if (*iter == pRegion) return true;
558 return false;
559 }
560
561 // implementation of abstract method derived from class 'LinuxSampler::RegionPools'
562 virtual Pool<R*>* GetRegionPool(int index) {
563 if (index < 0 || index > 1) throw Exception("Index out of bounds");
564 return pRegionPool[index];
565 }
566
567 // implementation of abstract method derived from class 'LinuxSampler::VoicePool'
568 virtual Pool<V>* GetVoicePool() { return pVoicePool; }
569
570 D* GetDiskThread() { return pDiskThread; }
571
572 //friend class EngineChannelBase<V, R, I>;
573
574 static IM instruments;
575
576 protected:
577 class SuspensionVoiceHandler : public MidiKeyboardManager<V>::VoiceHandler {
578 public:
579 int PendingStreamDeletions;
580 RR* pPendingRegionSuspension;
581
582 SuspensionVoiceHandler(RR* pPendingRegionSuspension) {
583 PendingStreamDeletions = 0;
584 this->pPendingRegionSuspension = pPendingRegionSuspension;
585 }
586
587 virtual bool Process(MidiKey* pMidiKey) OVERRIDE {
588 VoiceIterator itVoice = pMidiKey->pActiveVoices->first();
589 // if current key is not associated with this region, skip this key
590 if (itVoice->GetRegion()->GetParent() != pPendingRegionSuspension) return false;
591
592 return true;
593 }
594
595 virtual void Process(VoiceIterator& itVoice) OVERRIDE {
596 // request a notification from disk thread side for stream deletion
597 const Stream::Handle hStream = itVoice->KillImmediately(true);
598 if (hStream != Stream::INVALID_HANDLE) { // voice actually used a stream
599 PendingStreamDeletions++;
600 }
601 //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
602 }
603 };
604
605 Pool<R*>* pRegionPool[2]; ///< Double buffered pool, used by the engine channels to keep track of regions in use.
606 int MinFadeOutSamples; ///< The number of samples needed to make an instant fade out (e.g. for voice stealing) without leading to clicks.
607 D* pDiskThread;
608
609 int ActiveVoiceCountTemp; ///< number of currently active voices (for internal usage, will be used for incrementation)
610 VoiceIterator itLastStolenVoice; ///< Only for voice stealing: points to the last voice which was theft in current audio fragment, NULL otherwise.
611 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.
612 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.
613 VoiceIterator itLastStolenVoiceGlobally; ///< Same as itLastStolenVoice, but engine globally
614 RTList<uint>::Iterator iuiLastStolenKeyGlobally; ///< Same as iuiLastStolenKey, but engine globally
615 RTList<Event>* pVoiceStealingQueue; ///< All voice-launching events which had to be postponed due to free voice shortage.
616 Mutex ResetInternalMutex; ///< Mutex to protect the ResetInternal function for concurrent usage (e.g. by the lscp and instrument loader threads).
617 int iMaxDiskStreams;
618
619 /**
620 * Dispatch and handle all events in this audio fragment for the given
621 * engine channel.
622 *
623 * @param pEngineChannel - engine channel on which events should be
624 * processed
625 * @param Samples - amount of sample points to be processed in
626 * this audio fragment cycle
627 */
628 void ProcessEvents(EngineChannel* pEngineChannel, uint Samples) {
629 // get all events from the engine channels's input event queue which belong to the current fragment
630 // (these are the common events like NoteOn, NoteOff, ControlChange, etc.)
631 AbstractEngineChannel* pChannel = static_cast<AbstractEngineChannel*>(pEngineChannel);
632 pChannel->ImportEvents(Samples);
633
634 // if a valid real-time instrument script is loaded, pre-process
635 // the event list by running the script now, since the script
636 // might filter events or add new ones for this cycle
637 if (pChannel->pScript && pChannel->pScript->bHasValidScript) {
638 // resume any suspended script executions still hanging
639 // around of previous audio fragment cycles
640 for (RTList<ScriptEvent>::Iterator itEvent = pChannel->pScript->pEvents->first(),
641 end = pChannel->pScript->pEvents->end(); itEvent != end; ++itEvent)
642 {
643 ResumeScriptEvent(pChannel, itEvent); //TODO: implement support for actual suspension time (i.e. passed to a script's wait() function call)
644 }
645
646 // spawn new script executions for the new MIDI events of
647 // this audio fragment cycle
648 for (RTList<Event>::Iterator itEvent = pChannel->pEvents->first(),
649 end = pChannel->pEvents->end(); itEvent != end; ++itEvent)
650 {
651 switch (itEvent->Type) {
652 case Event::type_note_on:
653 if (pChannel->pScript->handlerNote)
654 ProcessEventByScript(pChannel, itEvent, pChannel->pScript->handlerNote);
655 break;
656 case Event::type_note_off:
657 if (pChannel->pScript->handlerRelease)
658 ProcessEventByScript(pChannel, itEvent, pChannel->pScript->handlerRelease);
659 break;
660 case Event::type_control_change:
661 case Event::type_channel_pressure:
662 case Event::type_pitchbend:
663 if (pChannel->pScript->handlerController)
664 ProcessEventByScript(pChannel, itEvent, pChannel->pScript->handlerController);
665 break;
666 case Event::type_note_pressure:
667 //TODO: ...
668 break;
669 }
670 }
671 }
672
673 // now process all events regularly
674 {
675 RTList<Event>::Iterator itEvent = pChannel->pEvents->first();
676 RTList<Event>::Iterator end = pChannel->pEvents->end();
677 for (; itEvent != end; ++itEvent) {
678 switch (itEvent->Type) {
679 case Event::type_note_on:
680 dmsg(5,("Engine: Note on received\n"));
681 ProcessNoteOn((EngineChannel*)itEvent->pEngineChannel, itEvent);
682 break;
683 case Event::type_note_off:
684 dmsg(5,("Engine: Note off received\n"));
685 ProcessNoteOff((EngineChannel*)itEvent->pEngineChannel, itEvent);
686 break;
687 case Event::type_control_change:
688 dmsg(5,("Engine: MIDI CC received\n"));
689 ProcessControlChange((EngineChannel*)itEvent->pEngineChannel, itEvent);
690 break;
691 case Event::type_channel_pressure:
692 dmsg(5,("Engine: MIDI Chan. Pressure received\n"));
693 ProcessChannelPressure((EngineChannel*)itEvent->pEngineChannel, itEvent);
694 break;
695 case Event::type_note_pressure:
696 dmsg(5,("Engine: MIDI Note Pressure received\n"));
697 ProcessPolyphonicKeyPressure((EngineChannel*)itEvent->pEngineChannel, itEvent);
698 break;
699 case Event::type_pitchbend:
700 dmsg(5,("Engine: Pitchbend received\n"));
701 ProcessPitchbend(static_cast<AbstractEngineChannel*>(itEvent->pEngineChannel), itEvent);
702 break;
703 }
704 }
705 }
706
707 // reset voice stealing for the next engine channel (or next audio fragment)
708 itLastStolenVoice = VoiceIterator();
709 itLastStolenVoiceGlobally = VoiceIterator();
710 iuiLastStolenKey = RTList<uint>::Iterator();
711 iuiLastStolenKeyGlobally = RTList<uint>::Iterator();
712 pLastStolenChannel = NULL;
713 }
714
715 /** @brief Call instrument script's event handler for this event.
716 *
717 * Causes a new execution instance of the currently loaded real-time
718 * instrument script's event handler (callback) to be spawned for
719 * the given MIDI event.
720 *
721 * @param pChannel - engine channel on which the MIDI event occured
722 * @param itEvent - MIDI event that causes this new script execution
723 * @param pEventHandler - script's event handler to be executed
724 */
725 void ProcessEventByScript(AbstractEngineChannel* pChannel, RTList<Event>::Iterator& itEvent, VMEventHandler* pEventHandler) {
726 const int key = itEvent->Param.Note.Key; // even if this is not a note on/off event, accessing it does not mean any harm
727 // check if polyphonic data is passed from "note" to "release"
728 // script event handlers
729 if (pEventHandler == pChannel->pScript->handlerRelease &&
730 pChannel->pScript->handlerNote &&
731 pChannel->pScript->handlerNote->isPolyphonic() &&
732 pChannel->pScript->handlerRelease->isPolyphonic() &&
733 !pChannel->pScript->pKeyEvents[key]->isEmpty())
734 {
735 // polyphonic variable data is used/passed from "note" to
736 // "release" script callback, so we have to recycle the
737 // original "note on" script event(s)
738 RTList<ScriptEvent>::Iterator it = pChannel->pScript->pKeyEvents[key]->first();
739 RTList<ScriptEvent>::Iterator end = pChannel->pScript->pKeyEvents[key]->end();
740 for (; it != end; ++it) {
741 ProcessScriptEvent(
742 pChannel, itEvent, pEventHandler, it
743 );
744 }
745 } else {
746 // no polyphonic data is used/passed from "note" to
747 // "release" script callback, so just use a new fresh
748 // script event object
749 RTList<ScriptEvent>::Iterator itScriptEvent =
750 pChannel->pScript->pEvents->allocAppend();
751 ProcessScriptEvent(
752 pChannel, itEvent, pEventHandler, itScriptEvent
753 );
754 }
755 }
756
757 void ProcessScriptEvent(AbstractEngineChannel* pChannel, RTList<Event>::Iterator& itEvent, VMEventHandler* pEventHandler, RTList<ScriptEvent>::Iterator& itScriptEvent) {
758 if (!itScriptEvent) return; // not a valid script event (i.e. because no free script event was left in the script event pool)
759
760 // fill the list of script handlers to be executed by this event
761 int i = 0;
762 itScriptEvent->handlers[i++] = pEventHandler; // actual event handler (i.e. note, controller)
763 itScriptEvent->handlers[i] = NULL; // NULL termination of list
764
765 // initialize/reset other members
766 itScriptEvent->cause = *itEvent;
767 itScriptEvent->id = pEventPool->getID(itEvent);
768 itScriptEvent->currentHandler = 0;
769 itScriptEvent->executionSlices = 0;
770
771 // run script handler(s)
772 VMExecStatus_t res = pScriptVM->exec(
773 pChannel->pScript->parserContext, &*itScriptEvent
774 );
775
776 // in case the script was suspended, keep it on the allocated
777 // ScriptEvent list to be continued on the next audio cycle
778 if (!(res & VM_EXEC_SUSPENDED)) { // script execution has finished without 'suspended' status ...
779 // if "polyphonic" variable data is passed from script's
780 // "note" event handler to its "release" event handler, then
781 // the script event must be kept and recycled for the later
782 // occuring "release" script event ...
783 if (pEventHandler == pChannel->pScript->handlerNote &&
784 pChannel->pScript->handlerRelease &&
785 pChannel->pScript->handlerNote->isPolyphonic() &&
786 pChannel->pScript->handlerRelease->isPolyphonic())
787 {
788 const int key = itEvent->Param.Note.Key;
789 itScriptEvent.moveToEndOf(pChannel->pScript->pKeyEvents[key & 127]);
790 } else {
791 // ... otherwise if no polyphonic data is passed and
792 // script's execution has finished without suspension
793 // status, then free the script event for a new future
794 // script event to be triggered from start
795 pChannel->pScript->pEvents->free(itScriptEvent);
796 }
797 }
798 }
799
800 /** @brief Resume execution of instrument script.
801 *
802 * Will be called to resume execution of a real-time instrument
803 * script event which has been suspended in a previous audio
804 * fragment cycle.
805 *
806 * Script execution might be suspended for various reasons. Usually
807 * a script will be suspended if the script called the built-in
808 * "wait()" function, but it might also be suspended automatically
809 * if the script took too much execution time in an audio fragment
810 * cycle. So in the latter case automatic suspension is performed in
811 * order to avoid harm for the sampler's overall real-time
812 * requirements.
813 *
814 * @param pChannel - engine channel this script is running for
815 * @param itScriptEvent - script execution that shall be resumed
816 */
817 void ResumeScriptEvent(AbstractEngineChannel* pChannel, RTList<ScriptEvent>::Iterator& itScriptEvent) {
818 VMEventHandler* handler = itScriptEvent->handlers[itScriptEvent->currentHandler];
819
820 // run script
821 VMExecStatus_t res = pScriptVM->exec(
822 pChannel->pScript->parserContext, &*itScriptEvent
823 );
824
825 // in case the script was suspended, keep it on the allocated
826 // ScriptEvent list to be continued on the next audio cycle
827 if (!(res & VM_EXEC_SUSPENDED)) { // script execution has finished without 'suspended' status ...
828 // if "polyphonic" variable data is passed from script's
829 // "note" event handler to its "release" event handler, then
830 // the script event must be kept and recycled for the later
831 // occuring "release" script event ...
832 if (handler && handler == pChannel->pScript->handlerNote &&
833 pChannel->pScript->handlerRelease &&
834 pChannel->pScript->handlerNote->isPolyphonic() &&
835 pChannel->pScript->handlerRelease->isPolyphonic())
836 {
837 const int key = itScriptEvent->cause.Param.Note.Key;
838 itScriptEvent.moveToEndOf(pChannel->pScript->pKeyEvents[key & 127]);
839 } else {
840 // ... otherwise if no polyphonic data is passed and
841 // script's execution has finished without suspension
842 // status, then free the script event for a new future
843 // script event to be triggered from start
844 pChannel->pScript->pEvents->free(itScriptEvent);
845 }
846 }
847 }
848
849 /**
850 * Will be called by LaunchVoice() method in case there are no free
851 * voices left. This method will select and kill one old voice for
852 * voice stealing and postpone the note-on event until the selected
853 * voice actually died.
854 *
855 * @param pEngineChannel - engine channel on which this event occured on
856 * @param itNoteOnEvent - key, velocity and time stamp of the event
857 * @returns 0 on success, a value < 0 if no active voice could be picked for voice stealing
858 */
859 int StealVoice(EngineChannel* pEngineChannel, Pool<Event>::Iterator& itNoteOnEvent) {
860 if (VoiceSpawnsLeft <= 0) {
861 dmsg(1,("Max. voice thefts per audio fragment reached (you may raise CONFIG_MAX_VOICES).\n"));
862 return -1;
863 }
864
865 EngineChannelBase<V, R, I>* pEngineChn = static_cast<EngineChannelBase<V, R, I>*>(pEngineChannel);
866
867 if (!pEventPool->poolIsEmpty()) {
868
869 if(!pEngineChn->StealVoice(itNoteOnEvent, &itLastStolenVoice, &iuiLastStolenKey)) {
870 --VoiceSpawnsLeft;
871 return 0;
872 }
873
874 // if we couldn't steal a voice from the same engine channel then
875 // steal oldest voice on the oldest key from any other engine channel
876 // (the smaller engine channel number, the higher priority)
877 EngineChannelBase<V, R, I>* pSelectedChannel;
878 int iChannelIndex;
879 VoiceIterator itSelectedVoice;
880
881 // select engine channel
882 if (pLastStolenChannel) {
883 pSelectedChannel = pLastStolenChannel;
884 iChannelIndex = pSelectedChannel->iEngineIndexSelf;
885 } else { // pick the engine channel followed by this engine channel
886 iChannelIndex = (pEngineChn->iEngineIndexSelf + 1) % engineChannels.size();
887 pSelectedChannel = static_cast<EngineChannelBase<V, R, I>*>(engineChannels[iChannelIndex]);
888 }
889
890 // if we already stole in this fragment, try to proceed on same key
891 if (this->itLastStolenVoiceGlobally) {
892 itSelectedVoice = this->itLastStolenVoiceGlobally;
893 do {
894 ++itSelectedVoice;
895 } while (itSelectedVoice && !itSelectedVoice->IsStealable()); // proceed iterating if voice was created in this fragment cycle
896 }
897
898 #if CONFIG_DEVMODE
899 EngineChannel* pBegin = pSelectedChannel; // to detect endless loop
900 #endif // CONFIG_DEVMODE
901
902 // did we find a 'stealable' voice?
903 if (itSelectedVoice && itSelectedVoice->IsStealable()) {
904 // remember which voice we stole, so we can simply proceed on next voice stealing
905 this->itLastStolenVoiceGlobally = itSelectedVoice;
906 } else while (true) { // iterate through engine channels
907 // get (next) oldest key
908 RTList<uint>::Iterator iuiSelectedKey = (this->iuiLastStolenKeyGlobally) ? ++this->iuiLastStolenKeyGlobally : pSelectedChannel->pActiveKeys->first();
909 this->iuiLastStolenKeyGlobally = RTList<uint>::Iterator(); // to prevent endless loop (see line above)
910 while (iuiSelectedKey) {
911 MidiKey* pSelectedKey = &pSelectedChannel->pMIDIKeyInfo[*iuiSelectedKey];
912 itSelectedVoice = pSelectedKey->pActiveVoices->first();
913 // proceed iterating if voice was created in this fragment cycle
914 while (itSelectedVoice && !itSelectedVoice->IsStealable()) ++itSelectedVoice;
915 // found a "stealable" voice ?
916 if (itSelectedVoice && itSelectedVoice->IsStealable()) {
917 // remember which voice on which key on which engine channel we stole, so we can simply proceed on next voice stealing
918 this->iuiLastStolenKeyGlobally = iuiSelectedKey;
919 this->itLastStolenVoiceGlobally = itSelectedVoice;
920 this->pLastStolenChannel = pSelectedChannel;
921 goto stealable_voice_found; // selection succeeded
922 }
923 ++iuiSelectedKey; // get next key on current engine channel
924 }
925 // get next engine channel
926 iChannelIndex = (iChannelIndex + 1) % engineChannels.size();
927 pSelectedChannel = static_cast<EngineChannelBase<V, R, I>*>(engineChannels[iChannelIndex]);
928
929 #if CONFIG_DEVMODE
930 if (pSelectedChannel == pBegin) {
931 dmsg(1,("FATAL ERROR: voice stealing endless loop!\n"));
932 dmsg(1,("VoiceSpawnsLeft=%d.\n", VoiceSpawnsLeft));
933 dmsg(1,("Exiting.\n"));
934 exit(-1);
935 }
936 #endif // CONFIG_DEVMODE
937 }
938
939 // jump point if a 'stealable' voice was found
940 stealable_voice_found:
941
942 #if CONFIG_DEVMODE
943 if (!itSelectedVoice->IsActive()) {
944 dmsg(1,("EngineBase: ERROR, tried to steal a voice which was not active !!!\n"));
945 return -1;
946 }
947 #endif // CONFIG_DEVMODE
948
949 // now kill the selected voice
950 itSelectedVoice->Kill(itNoteOnEvent);
951
952 --VoiceSpawnsLeft;
953
954 return 0; // success
955 }
956 else {
957 dmsg(1,("Event pool emtpy!\n"));
958 return -1;
959 }
960 }
961
962 void HandleInstrumentChanges() {
963 bool instrumentChanged = false;
964 for (int i = 0; i < engineChannels.size(); i++) {
965 EngineChannelBase<V, R, I>* pEngineChannel =
966 static_cast<EngineChannelBase<V, R, I>*>(engineChannels[i]);
967
968 // as we're going to (carefully) write some status to the
969 // synchronized struct, we cast away the const
970 InstrumentChangeCmd<R, I>& cmd =
971 const_cast<InstrumentChangeCmd<R, I>&>(pEngineChannel->InstrumentChangeCommandReader.Lock());
972
973 pEngineChannel->pRegionsInUse = cmd.pRegionsInUse;
974 pEngineChannel->pRegionsInUse->clear();
975
976 if (cmd.bChangeInstrument) {
977 // change instrument
978 dmsg(5,("Engine: instrument change command received\n"));
979 cmd.bChangeInstrument = false;
980 pEngineChannel->pInstrument = cmd.pInstrument;
981 pEngineChannel->pScript = cmd.pScript;
982 instrumentChanged = true;
983
984 pEngineChannel->MarkAllActiveVoicesAsOrphans();
985
986 // the script's "init" event handler is only executed
987 // once (when the script is loaded or reloaded)
988 if (pEngineChannel->pScript && pEngineChannel->pScript->handlerInit) {
989 RTList<ScriptEvent>::Iterator itScriptEvent =
990 pEngineChannel->pScript->pEvents->allocAppend();
991
992 itScriptEvent->cause.pEngineChannel = pEngineChannel;
993 itScriptEvent->handlers[0] = pEngineChannel->pScript->handlerInit;
994 itScriptEvent->handlers[1] = NULL;
995
996 VMExecStatus_t res = pScriptVM->exec(
997 pEngineChannel->pScript->parserContext, &*itScriptEvent
998 );
999
1000 pEngineChannel->pScript->pEvents->free(itScriptEvent);
1001 }
1002 }
1003 }
1004
1005 if (instrumentChanged) {
1006 //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
1007 ResetSuspendedRegions();
1008 }
1009 }
1010
1011 /**
1012 * Render all 'normal' voices (that is voices which were not stolen in
1013 * this fragment) on the given engine channel.
1014 *
1015 * @param pEngineChannel - engine channel on which audio should be
1016 * rendered
1017 * @param Samples - amount of sample points to be rendered in
1018 * this audio fragment cycle
1019 */
1020 void RenderActiveVoices(EngineChannel* pEngineChannel, uint Samples) {
1021 #if !CONFIG_PROCESS_MUTED_CHANNELS
1022 if (pEngineChannel->GetMute()) return; // skip if sampler channel is muted
1023 #endif
1024
1025 EngineChannelBase<V, R, I>* pChannel =
1026 static_cast<EngineChannelBase<V, R, I>*>(pEngineChannel);
1027 pChannel->RenderActiveVoices(Samples);
1028
1029 ActiveVoiceCountTemp += pEngineChannel->GetVoiceCount();
1030 }
1031
1032 /**
1033 * Render all stolen voices (only voices which were stolen in this
1034 * fragment) on the given engine channel. Stolen voices are rendered
1035 * after all normal voices have been rendered; this is needed to render
1036 * audio of those voices which were selected for voice stealing until
1037 * the point were the stealing (that is the take over of the voice)
1038 * actually happened.
1039 *
1040 * @param pEngineChannel - engine channel on which audio should be
1041 * rendered
1042 * @param Samples - amount of sample points to be rendered in
1043 * this audio fragment cycle
1044 */
1045 void RenderStolenVoices(uint Samples) {
1046 RTList<Event>::Iterator itVoiceStealEvent = pVoiceStealingQueue->first();
1047 RTList<Event>::Iterator end = pVoiceStealingQueue->end();
1048 for (; itVoiceStealEvent != end; ++itVoiceStealEvent) {
1049 EngineChannelBase<V, R, I>* pEngineChannel =
1050 static_cast<EngineChannelBase<V, R, I>*>(itVoiceStealEvent->pEngineChannel);;
1051 if (!pEngineChannel->pInstrument) continue; // ignore if no instrument loaded
1052 PoolVoiceIterator itNewVoice =
1053 LaunchVoice(pEngineChannel, itVoiceStealEvent, itVoiceStealEvent->Param.Note.Layer, itVoiceStealEvent->Param.Note.ReleaseTrigger, false, false);
1054 if (itNewVoice) {
1055 itNewVoice->Render(Samples);
1056 if (itNewVoice->IsActive()) { // still active
1057 *(pEngineChannel->pRegionsInUse->allocAppend()) = itNewVoice->GetRegion();
1058 ActiveVoiceCountTemp++;
1059 pEngineChannel->SetVoiceCount(pEngineChannel->GetVoiceCount() + 1);
1060
1061 if (itNewVoice->PlaybackState == Voice::playback_state_disk) {
1062 if (itNewVoice->DiskStreamRef.State != Stream::state_unused) {
1063 pEngineChannel->SetDiskStreamCount(pEngineChannel->GetDiskStreamCount() + 1);
1064 }
1065 }
1066 } else { // voice reached end, is now inactive
1067 pEngineChannel->FreeVoice(itNewVoice); // remove voice from the list of active voices
1068 }
1069 }
1070 else dmsg(1,("EngineBase: ERROR, voice stealing didn't work out!\n"));
1071
1072 // we need to clear the key's event list explicitly here in case key was never active
1073 MidiKey* pKey = &pEngineChannel->pMIDIKeyInfo[itVoiceStealEvent->Param.Note.Key];
1074 pKey->VoiceTheftsQueued--;
1075 if (!pKey->Active && !pKey->VoiceTheftsQueued) pKey->pEvents->clear();
1076 }
1077 }
1078
1079 /**
1080 * Free all keys which have turned inactive in this audio fragment, from
1081 * the list of active keys and clear all event lists on that engine
1082 * channel.
1083 *
1084 * @param pEngineChannel - engine channel to cleanup
1085 */
1086 void PostProcess(EngineChannel* pEngineChannel) {
1087 EngineChannelBase<V, R, I>* pChannel =
1088 static_cast<EngineChannelBase<V, R, I>*>(pEngineChannel);
1089 pChannel->FreeAllInactiveKyes();
1090
1091 // empty the engine channel's own event lists
1092 pChannel->ClearEventLists();
1093 }
1094
1095 /**
1096 * Process MIDI control change events with hard coded behavior,
1097 * that is controllers whose behavior is defined independently
1098 * of the actual sampler engine type and instrument.
1099 *
1100 * @param pEngineChannel - engine channel on which the MIDI CC event was received
1101 * @param itControlChangeEvent - the actual MIDI CC event
1102 */
1103 void ProcessHardcodedControllers (
1104 EngineChannel* pEngineChannel,
1105 Pool<Event>::Iterator& itControlChangeEvent
1106 ) {
1107 EngineChannelBase<V, R, I>* pChannel =
1108 static_cast<EngineChannelBase<V, R, I>*>(pEngineChannel);
1109
1110 switch (itControlChangeEvent->Param.CC.Controller) {
1111 case 5: { // portamento time
1112 pChannel->PortamentoTime = (float) itControlChangeEvent->Param.CC.Value / 127.0f * (float) CONFIG_PORTAMENTO_TIME_MAX + (float) CONFIG_PORTAMENTO_TIME_MIN;
1113 break;
1114 }
1115 case 6: { // data entry (currently only used for RPN and NRPN controllers)
1116 //dmsg(1,("DATA ENTRY %d\n", itControlChangeEvent->Param.CC.Value));
1117 if (pChannel->GetMidiRpnController() >= 0) { // RPN controller number was sent previously ...
1118 dmsg(4,("Guess it's an RPN ...\n"));
1119 if (pChannel->GetMidiRpnController() == 2) { // coarse tuning in half tones
1120 int transpose = (int) itControlChangeEvent->Param.CC.Value - 64;
1121 // limit to +- two octaves for now
1122 transpose = RTMath::Min(transpose, 24);
1123 transpose = RTMath::Max(transpose, -24);
1124 pChannel->GlobalTranspose = transpose;
1125 // workaround, so we won't have hanging notes
1126 pChannel->ReleaseAllVoices(itControlChangeEvent);
1127 }
1128 // to prevent other MIDI CC #6 messages to be misenterpreted as RPN controller data
1129 pChannel->ResetMidiRpnController();
1130 } else if (pChannel->GetMidiNrpnController() >= 0) { // NRPN controller number was sent previously ...
1131 dmsg(4,("Guess it's an NRPN ...\n"));
1132 const int NrpnCtrlMSB = pChannel->GetMidiNrpnController() >> 8;
1133 const int NrpnCtrlLSB = pChannel->GetMidiNrpnController() & 0xff;
1134 dmsg(4,("NRPN MSB=%d LSB=%d Data=%d\n", NrpnCtrlMSB, NrpnCtrlLSB, itControlChangeEvent->Param.CC.Value));
1135 switch (NrpnCtrlMSB) {
1136 case 0x1a: { // volume level of note (Roland GS NRPN)
1137 const uint note = NrpnCtrlLSB;
1138 const uint vol = itControlChangeEvent->Param.CC.Value;
1139 dmsg(4,("Note Volume NRPN received (note=%d,vol=%d).\n", note, vol));
1140 if (note < 128 && vol < 128)
1141 pChannel->pMIDIKeyInfo[note].Volume = VolumeCurve[vol];
1142 break;
1143 }
1144 case 0x1c: { // panpot of note (Roland GS NRPN)
1145 const uint note = NrpnCtrlLSB;
1146 const uint pan = itControlChangeEvent->Param.CC.Value;
1147 dmsg(4,("Note Pan NRPN received (note=%d,pan=%d).\n", note, pan));
1148 if (note < 128 && pan < 128) {
1149 pChannel->pMIDIKeyInfo[note].PanLeft = PanCurve[128 - pan];
1150 pChannel->pMIDIKeyInfo[note].PanRight = PanCurve[pan];
1151 }
1152 break;
1153 }
1154 case 0x1d: { // reverb send of note (Roland GS NRPN)
1155 const uint note = NrpnCtrlLSB;
1156 const float reverb = float(itControlChangeEvent->Param.CC.Value) / 127.0f;
1157 dmsg(4,("Note Reverb Send NRPN received (note=%d,send=%d).\n", note, reverb));
1158 if (note < 128)
1159 pChannel->pMIDIKeyInfo[note].ReverbSend = reverb;
1160 break;
1161 }
1162 case 0x1e: { // chorus send of note (Roland GS NRPN)
1163 const uint note = NrpnCtrlLSB;
1164 const float chorus = float(itControlChangeEvent->Param.CC.Value) / 127.0f;
1165 dmsg(4,("Note Chorus Send NRPN received (note=%d,send=%d).\n", note, chorus));
1166 if (note < 128)
1167 pChannel->pMIDIKeyInfo[note].ChorusSend = chorus;
1168 break;
1169 }
1170 }
1171 // to prevent other MIDI CC #6 messages to be misenterpreted as NRPN controller data
1172 pChannel->ResetMidiNrpnController();
1173 }
1174 break;
1175 }
1176 case 7: { // volume
1177 //TODO: not sample accurate yet
1178 pChannel->MidiVolume = VolumeCurve[itControlChangeEvent->Param.CC.Value];
1179 pChannel->bStatusChanged = true; // engine channel status has changed, so set notify flag
1180 break;
1181 }
1182 case 10: { // panpot
1183 //TODO: not sample accurate yet
1184 pChannel->iLastPanRequest = itControlChangeEvent->Param.CC.Value;
1185 break;
1186 }
1187 case 64: { // sustain
1188 if (itControlChangeEvent->Param.CC.Value >= 64 && !pChannel->SustainPedal) {
1189 dmsg(4,("DAMPER (RIGHT) PEDAL DOWN\n"));
1190 pChannel->SustainPedal = true;
1191 pChannel->listeners.PreProcessSustainPedalDown();
1192
1193 #if !CONFIG_PROCESS_MUTED_CHANNELS
1194 if (pEngineChannel->GetMute()) { // skip if sampler channel is muted
1195 pChannel->listeners.PostProcessSustainPedalDown();
1196 return;
1197 }
1198 #endif
1199
1200 pChannel->ProcessSustainPedalDown(itControlChangeEvent);
1201 pChannel->listeners.PostProcessSustainPedalDown();
1202 }
1203 if (itControlChangeEvent->Param.CC.Value < 64 && pChannel->SustainPedal) {
1204 dmsg(4,("DAMPER (RIGHT) PEDAL UP\n"));
1205 pChannel->SustainPedal = false;
1206 pChannel->listeners.PreProcessSustainPedalUp();
1207
1208 #if !CONFIG_PROCESS_MUTED_CHANNELS
1209 if (pChannel->GetMute()) { // skip if sampler channel is muted
1210 pChannel->listeners.PostProcessSustainPedalUp();
1211 return;
1212 }
1213 #endif
1214
1215 pChannel->ProcessSustainPedalUp(itControlChangeEvent);
1216 pChannel->listeners.PostProcessSustainPedalUp();
1217 }
1218 break;
1219 }
1220 case 65: { // portamento on / off
1221 const bool bPortamento = itControlChangeEvent->Param.CC.Value >= 64;
1222 if (bPortamento != pChannel->PortamentoMode)
1223 KillAllVoices(pChannel, itControlChangeEvent);
1224 pChannel->PortamentoMode = bPortamento;
1225 break;
1226 }
1227 case 66: { // sostenuto
1228 if (itControlChangeEvent->Param.CC.Value >= 64 && !pChannel->SostenutoPedal) {
1229 dmsg(4,("SOSTENUTO (CENTER) PEDAL DOWN\n"));
1230 pChannel->SostenutoPedal = true;
1231 pChannel->listeners.PreProcessSostenutoPedalDown();
1232
1233 #if !CONFIG_PROCESS_MUTED_CHANNELS
1234 if (pEngineChannel->GetMute()) { // skip if sampler channel is muted
1235 pChannel->listeners.PostProcessSostenutoPedalDown();
1236 return;
1237 }
1238 #endif
1239
1240 pChannel->ProcessSostenutoPedalDown();
1241 pChannel->listeners.PostProcessSostenutoPedalDown();
1242 }
1243 if (itControlChangeEvent->Param.CC.Value < 64 && pChannel->SostenutoPedal) {
1244 dmsg(4,("SOSTENUTO (CENTER) PEDAL UP\n"));
1245 pChannel->SostenutoPedal = false;
1246 pChannel->listeners.PreProcessSostenutoPedalUp();
1247
1248 #if !CONFIG_PROCESS_MUTED_CHANNELS
1249 if (pEngineChannel->GetMute()) { // skip if sampler channel is muted
1250 pChannel->listeners.PostProcessSostenutoPedalUp();
1251 return;
1252 }
1253 #endif
1254
1255 pChannel->ProcessSostenutoPedalUp(itControlChangeEvent);
1256 pChannel->listeners.PostProcessSostenutoPedalUp();
1257 }
1258 break;
1259 }
1260 case 98: { // NRPN controller LSB
1261 dmsg(4,("NRPN LSB %d\n", itControlChangeEvent->Param.CC.Value));
1262 pEngineChannel->SetMidiNrpnControllerLsb(itControlChangeEvent->Param.CC.Value);
1263 break;
1264 }
1265 case 99: { // NRPN controller MSB
1266 dmsg(4,("NRPN MSB %d\n", itControlChangeEvent->Param.CC.Value));
1267 pEngineChannel->SetMidiNrpnControllerMsb(itControlChangeEvent->Param.CC.Value);
1268 break;
1269 }
1270 case 100: { // RPN controller LSB
1271 dmsg(4,("RPN LSB %d\n", itControlChangeEvent->Param.CC.Value));
1272 pEngineChannel->SetMidiRpnControllerLsb(itControlChangeEvent->Param.CC.Value);
1273 break;
1274 }
1275 case 101: { // RPN controller MSB
1276 dmsg(4,("RPN MSB %d\n", itControlChangeEvent->Param.CC.Value));
1277 pEngineChannel->SetMidiRpnControllerMsb(itControlChangeEvent->Param.CC.Value);
1278 break;
1279 }
1280
1281
1282 // Channel Mode Messages
1283
1284 case 120: { // all sound off
1285 KillAllVoices(pEngineChannel, itControlChangeEvent);
1286 break;
1287 }
1288 case 121: { // reset all controllers
1289 pChannel->ResetControllers();
1290 break;
1291 }
1292 case 123: { // all notes off
1293 #if CONFIG_PROCESS_ALL_NOTES_OFF
1294 pChannel->ReleaseAllVoices(itControlChangeEvent);
1295 #endif // CONFIG_PROCESS_ALL_NOTES_OFF
1296 break;
1297 }
1298 case 126: { // mono mode on
1299 if (!pChannel->SoloMode)
1300 KillAllVoices(pEngineChannel, itControlChangeEvent);
1301 pChannel->SoloMode = true;
1302 break;
1303 }
1304 case 127: { // poly mode on
1305 if (pChannel->SoloMode)
1306 KillAllVoices(pEngineChannel, itControlChangeEvent);
1307 pChannel->SoloMode = false;
1308 break;
1309 }
1310 }
1311 }
1312
1313 virtual D* CreateDiskThread() = 0;
1314
1315 /**
1316 * Assigns and triggers a new voice for the respective MIDI key.
1317 *
1318 * @param pEngineChannel - engine channel on which this event occured on
1319 * @param itNoteOnEvent - key, velocity and time stamp of the event
1320 */
1321 virtual void ProcessNoteOn(EngineChannel* pEngineChannel, Pool<Event>::Iterator& itNoteOnEvent) {
1322 EngineChannelBase<V, R, I>* pChannel =
1323 static_cast<EngineChannelBase<V, R, I>*>(pEngineChannel);
1324
1325 //HACK: we should better add the transpose value only to the most mandatory places (like for retrieving the region and calculating the tuning), because otherwise voices will unintendedly survive when changing transpose while playing
1326 int k = itNoteOnEvent->Param.Note.Key + pChannel->GlobalTranspose;
1327 if (k < 0 || k > 127) return; //ignore keys outside the key range
1328
1329 itNoteOnEvent->Param.Note.Key += pChannel->GlobalTranspose;
1330 int vel = itNoteOnEvent->Param.Note.Velocity;
1331
1332 const int key = itNoteOnEvent->Param.Note.Key;
1333 MidiKey* pKey = &pChannel->pMIDIKeyInfo[key];
1334
1335 pChannel->listeners.PreProcessNoteOn(key, vel);
1336 #if !CONFIG_PROCESS_MUTED_CHANNELS
1337 if (pEngineChannel->GetMute()) { // skip if sampler channel is muted
1338 pChannel->listeners.PostProcessNoteOn(key, vel);
1339 return;
1340 }
1341 #endif
1342
1343 if (!pChannel->pInstrument) {
1344 pChannel->listeners.PostProcessNoteOn(key, vel);
1345 return; // ignore if no instrument loaded
1346 }
1347
1348 // move note on event to the key's own event list
1349 RTList<Event>::Iterator itNoteOnEventOnKeyList = itNoteOnEvent.moveToEndOf(pKey->pEvents);
1350
1351 // if Solo Mode then kill all already active voices
1352 if (pChannel->SoloMode) {
1353 Pool<uint>::Iterator itYoungestKey = pChannel->pActiveKeys->last();
1354 if (itYoungestKey) {
1355 const int iYoungestKey = *itYoungestKey;
1356 const MidiKey* pOtherKey = &pChannel->pMIDIKeyInfo[iYoungestKey];
1357 if (pOtherKey->Active) {
1358 // get final portamento position of currently active voice
1359 if (pChannel->PortamentoMode) {
1360 VoiceIterator itVoice = pOtherKey->pActiveVoices->last();
1361 if (itVoice) itVoice->UpdatePortamentoPos(itNoteOnEventOnKeyList);
1362 }
1363 // kill all voices on the (other) key
1364 VoiceIterator itVoiceToBeKilled = pOtherKey->pActiveVoices->first();
1365 VoiceIterator end = pOtherKey->pActiveVoices->end();
1366 for (; itVoiceToBeKilled != end; ++itVoiceToBeKilled) {
1367 if (!(itVoiceToBeKilled->Type & Voice::type_release_trigger))
1368 itVoiceToBeKilled->Kill(itNoteOnEventOnKeyList);
1369 }
1370 }
1371 }
1372 // set this key as 'currently active solo key'
1373 pChannel->SoloKey = key;
1374 }
1375
1376 pChannel->ProcessKeySwitchChange(key);
1377
1378 pKey->KeyPressed = true; // the MIDI key was now pressed down
1379 pChannel->KeyDown[key] = true; // just used as built-in %KEY_DOWN script variable
1380 pKey->Velocity = itNoteOnEventOnKeyList->Param.Note.Velocity;
1381 pKey->NoteOnTime = FrameTime + itNoteOnEventOnKeyList->FragmentPos(); // will be used to calculate note length
1382
1383 // cancel release process of voices on this key if needed
1384 if (pKey->Active && !pChannel->SustainPedal) {
1385 RTList<Event>::Iterator itCancelReleaseEvent = pKey->pEvents->allocAppend();
1386 if (itCancelReleaseEvent) {
1387 *itCancelReleaseEvent = *itNoteOnEventOnKeyList; // copy event
1388 itCancelReleaseEvent->Type = Event::type_cancel_release; // transform event type
1389 }
1390 else dmsg(1,("Event pool emtpy!\n"));
1391 }
1392
1393 TriggerNewVoices(pEngineChannel, itNoteOnEventOnKeyList);
1394
1395 // if neither a voice was spawned or postponed then remove note on event from key again
1396 if (!pKey->Active && !pKey->VoiceTheftsQueued)
1397 pKey->pEvents->free(itNoteOnEventOnKeyList);
1398
1399 if (!pChannel->SoloMode || pChannel->PortamentoPos < 0.0f) pChannel->PortamentoPos = (float) key;
1400 if (pKey->pRoundRobinIndex) {
1401 (*pKey->pRoundRobinIndex)++; // counter specific for the key or region
1402 pChannel->RoundRobinIndex++; // common counter for the channel
1403 }
1404 pChannel->listeners.PostProcessNoteOn(key, vel);
1405 }
1406
1407 /**
1408 * Allocate and trigger new voice(s) for the key.
1409 */
1410 virtual void TriggerNewVoices (
1411 EngineChannel* pEngineChannel,
1412 RTList<Event>::Iterator& itNoteOnEvent,
1413 bool HandleKeyGroupConflicts = true
1414 ) = 0;
1415
1416 /**
1417 * Allocate and trigger release voice(s) for the key.
1418 */
1419 virtual void TriggerReleaseVoices (
1420 EngineChannel* pEngineChannel,
1421 RTList<Event>::Iterator& itNoteOffEvent
1422 ) = 0;
1423
1424 /**
1425 * Releases the voices on the given key if sustain pedal is not pressed.
1426 * If sustain is pressed, the release of the note will be postponed until
1427 * sustain pedal will be released or voice turned inactive by itself (e.g.
1428 * due to completion of sample playback).
1429 *
1430 * @param pEngineChannel - engine channel on which this event occured on
1431 * @param itNoteOffEvent - key, velocity and time stamp of the event
1432 */
1433 virtual void ProcessNoteOff(EngineChannel* pEngineChannel, Pool<Event>::Iterator& itNoteOffEvent) {
1434 EngineChannelBase<V, R, I>* pChannel = static_cast<EngineChannelBase<V, R, I>*>(pEngineChannel);
1435
1436 int k = itNoteOffEvent->Param.Note.Key + pChannel->GlobalTranspose;
1437 if (k < 0 || k > 127) return; //ignore keys outside the key range
1438
1439 //HACK: we should better add the transpose value only to the most mandatory places (like for retrieving the region and calculating the tuning), because otherwise voices will unintendedly survive when changing transpose while playing
1440 itNoteOffEvent->Param.Note.Key += pChannel->GlobalTranspose;
1441 int vel = itNoteOffEvent->Param.Note.Velocity;
1442
1443 const int iKey = itNoteOffEvent->Param.Note.Key;
1444 MidiKey* pKey = &pChannel->pMIDIKeyInfo[iKey];
1445
1446 pChannel->listeners.PreProcessNoteOff(iKey, vel);
1447
1448 #if !CONFIG_PROCESS_MUTED_CHANNELS
1449 if (pEngineChannel->GetMute()) { // skip if sampler channel is muted
1450 pChannel->listeners.PostProcessNoteOff(iKey, vel);
1451 return;
1452 }
1453 #endif
1454
1455 pKey->KeyPressed = false; // the MIDI key was now released
1456 pChannel->KeyDown[iKey] = false; // just used as built-in %KEY_DOWN script variable
1457
1458 // move event to the key's own event list
1459 RTList<Event>::Iterator itNoteOffEventOnKeyList = itNoteOffEvent.moveToEndOf(pKey->pEvents);
1460
1461 bool bShouldRelease = pKey->Active && pChannel->ShouldReleaseVoice(itNoteOffEventOnKeyList->Param.Note.Key);
1462
1463 // in case Solo Mode is enabled, kill all voices on this key and respawn a voice on the highest pressed key (if any)
1464 if (pChannel->SoloMode && pChannel->pInstrument) { //TODO: this feels like too much code just for handling solo mode :P
1465 bool bOtherKeysPressed = false;
1466 if (iKey == pChannel->SoloKey) {
1467 pChannel->SoloKey = -1;
1468 // if there's still a key pressed down, respawn a voice (group) on the highest key
1469 for (int i = 127; i > 0; i--) {
1470 MidiKey* pOtherKey = &pChannel->pMIDIKeyInfo[i];
1471 if (pOtherKey->KeyPressed) {
1472 bOtherKeysPressed = true;
1473 // make the other key the new 'currently active solo key'
1474 pChannel->SoloKey = i;
1475 // get final portamento position of currently active voice
1476 if (pChannel->PortamentoMode) {
1477 VoiceIterator itVoice = pKey->pActiveVoices->first();
1478 if (itVoice) itVoice->UpdatePortamentoPos(itNoteOffEventOnKeyList);
1479 }
1480 // create a pseudo note on event
1481 RTList<Event>::Iterator itPseudoNoteOnEvent = pOtherKey->pEvents->allocAppend();
1482 if (itPseudoNoteOnEvent) {
1483 // copy event
1484 *itPseudoNoteOnEvent = *itNoteOffEventOnKeyList;
1485 // transform event to a note on event
1486 itPseudoNoteOnEvent->Type = Event::type_note_on;
1487 itPseudoNoteOnEvent->Param.Note.Key = i;
1488 itPseudoNoteOnEvent->Param.Note.Velocity = pOtherKey->Velocity;
1489 // allocate and trigger new voice(s) for the other key
1490 TriggerNewVoices(pChannel, itPseudoNoteOnEvent, false);
1491 // if neither a voice was spawned or postponed then remove note on event from key again
1492 if (!pOtherKey->Active && !pOtherKey->VoiceTheftsQueued)
1493 pOtherKey->pEvents->free(itPseudoNoteOnEvent);
1494
1495 } else dmsg(1,("Could not respawn voice, no free event left\n"));
1496 break; // done
1497 }
1498 }
1499 }
1500 if (bOtherKeysPressed) {
1501 if (pKey->Active) { // kill all voices on this key
1502 bShouldRelease = false; // no need to release, as we kill it here
1503 VoiceIterator itVoiceToBeKilled = pKey->pActiveVoices->first();
1504 VoiceIterator end = pKey->pActiveVoices->end();
1505 for (; itVoiceToBeKilled != end; ++itVoiceToBeKilled) {
1506 if (!(itVoiceToBeKilled->Type & Voice::type_release_trigger))
1507 itVoiceToBeKilled->Kill(itNoteOffEventOnKeyList);
1508 }
1509 }
1510 } else pChannel->PortamentoPos = -1.0f;
1511 }
1512
1513 // if no solo mode (the usual case) or if solo mode and no other key pressed, then release voices on this key if needed
1514 if (bShouldRelease) {
1515 itNoteOffEventOnKeyList->Type = Event::type_release; // transform event type
1516
1517 // spawn release triggered voice(s) if needed
1518 if (pKey->ReleaseTrigger && pChannel->pInstrument) {
1519 TriggerReleaseVoices(pChannel, itNoteOffEventOnKeyList);
1520 pKey->ReleaseTrigger = false;
1521 }
1522 }
1523
1524 // if neither a voice was spawned or postponed on this key then remove note off event from key again
1525 if (!pKey->Active && !pKey->VoiceTheftsQueued)
1526 pKey->pEvents->free(itNoteOffEventOnKeyList);
1527
1528 pChannel->listeners.PostProcessNoteOff(iKey, vel);
1529 }
1530
1531 /**
1532 * Reset all voices and disk thread and clear input event queue and all
1533 * control and status variables. This method is protected by a mutex.
1534 */
1535 virtual void ResetInternal() {
1536 LockGuard lock(ResetInternalMutex);
1537
1538 // make sure that the engine does not get any sysex messages
1539 // while it's reseting
1540 bool sysexDisabled = MidiInputPort::RemoveSysexListener(this);
1541 SetVoiceCount(0);
1542 ActiveVoiceCountMax = 0;
1543
1544 // reset voice stealing parameters
1545 pVoiceStealingQueue->clear();
1546 itLastStolenVoice = VoiceIterator();
1547 itLastStolenVoiceGlobally = VoiceIterator();
1548 iuiLastStolenKey = RTList<uint>::Iterator();
1549 iuiLastStolenKeyGlobally = RTList<uint>::Iterator();
1550 pLastStolenChannel = NULL;
1551
1552 // reset all voices
1553 for (VoiceIterator iterVoice = pVoicePool->allocAppend(); iterVoice == pVoicePool->last(); iterVoice = pVoicePool->allocAppend()) {
1554 iterVoice->Reset();
1555 }
1556 pVoicePool->clear();
1557
1558 // reset disk thread
1559 if (pDiskThread) pDiskThread->Reset();
1560
1561 // delete all input events
1562 pEventQueue->init();
1563 pSysexBuffer->init();
1564 if (sysexDisabled) MidiInputPort::AddSysexListener(this);
1565 }
1566
1567 /**
1568 * Kills all voices on an engine channel as soon as possible. Voices
1569 * won't get into release state, their volume level will be ramped down
1570 * as fast as possible.
1571 *
1572 * @param pEngineChannel - engine channel on which all voices should be killed
1573 * @param itKillEvent - event which caused this killing of all voices
1574 */
1575 virtual void KillAllVoices(EngineChannel* pEngineChannel, Pool<Event>::Iterator& itKillEvent) {
1576 EngineChannelBase<V, R, I>* pChannel = static_cast<EngineChannelBase<V, R, I>*>(pEngineChannel);
1577 int count = pChannel->KillAllVoices(itKillEvent);
1578 VoiceSpawnsLeft -= count; //FIXME: just a temporary workaround, we should check the cause in StealVoice() instead
1579 }
1580
1581 /**
1582 * Allocates and triggers a new voice. This method will usually be
1583 * called by the ProcessNoteOn() method and by the voices itself
1584 * (e.g. to spawn further voices on the same key for layered sounds).
1585 *
1586 * @param pEngineChannel - engine channel on which this event occured on
1587 * @param itNoteOnEvent - key, velocity and time stamp of the event
1588 * @param iLayer - layer index for the new voice (optional - only
1589 * in case of layered sounds of course)
1590 * @param ReleaseTriggerVoice - if new voice is a release triggered voice
1591 * (optional, default = false)
1592 * @param VoiceStealing - if voice stealing should be performed
1593 * when there is no free voice
1594 * (optional, default = true)
1595 * @param HandleKeyGroupConflicts - if voices should be killed due to a
1596 * key group conflict
1597 * @returns pointer to new voice or NULL if there was no free voice or
1598 * if the voice wasn't triggered (for example when no region is
1599 * defined for the given key).
1600 */
1601 virtual PoolVoiceIterator LaunchVoice (
1602 EngineChannel* pEngineChannel,
1603 Pool<Event>::Iterator& itNoteOnEvent,
1604 int iLayer,
1605 bool ReleaseTriggerVoice,
1606 bool VoiceStealing,
1607 bool HandleKeyGroupConflicts
1608 ) = 0;
1609
1610 virtual int GetMinFadeOutSamples() { return MinFadeOutSamples; }
1611
1612 int InitNewVoice (
1613 EngineChannelBase<V, R, I>* pChannel,
1614 R* pRegion,
1615 Pool<Event>::Iterator& itNoteOnEvent,
1616 Voice::type_t VoiceType,
1617 int iLayer,
1618 int iKeyGroup,
1619 bool ReleaseTriggerVoice,
1620 bool VoiceStealing,
1621 typename Pool<V>::Iterator& itNewVoice
1622 ) {
1623 int key = itNoteOnEvent->Param.Note.Key;
1624 typename MidiKeyboardManager<V>::MidiKey* pKey = &pChannel->pMIDIKeyInfo[key];
1625 if (itNewVoice) {
1626 // launch the new voice
1627 if (itNewVoice->Trigger(pChannel, itNoteOnEvent, pChannel->Pitch, pRegion, VoiceType, iKeyGroup) < 0) {
1628 dmsg(4,("Voice not triggered\n"));
1629 pKey->pActiveVoices->free(itNewVoice);
1630 }
1631 else { // on success
1632 --VoiceSpawnsLeft;
1633 if (!pKey->Active) { // mark as active key
1634 pKey->Active = true;
1635 pKey->itSelf = pChannel->pActiveKeys->allocAppend();
1636 *pKey->itSelf = itNoteOnEvent->Param.Note.Key;
1637 }
1638 if (itNewVoice->Type & Voice::type_release_trigger_required) pKey->ReleaseTrigger = true; // mark key for the need of release triggered voice(s)
1639 return 0; // success
1640 }
1641 }
1642 else if (VoiceStealing) {
1643 // try to steal one voice
1644 int result = StealVoice(pChannel, itNoteOnEvent);
1645 if (!result) { // voice stolen successfully
1646 // put note-on event into voice-stealing queue, so it will be reprocessed after killed voice died
1647 RTList<Event>::Iterator itStealEvent = pVoiceStealingQueue->allocAppend();
1648 if (itStealEvent) {
1649 *itStealEvent = *itNoteOnEvent; // copy event
1650 itStealEvent->Param.Note.Layer = iLayer;
1651 itStealEvent->Param.Note.ReleaseTrigger = ReleaseTriggerVoice;
1652 pKey->VoiceTheftsQueued++;
1653 }
1654 else dmsg(1,("Voice stealing queue full!\n"));
1655 }
1656 }
1657
1658 return -1;
1659 }
1660
1661 /**
1662 * Checks whether scale tuning setting has been changed since last
1663 * time this method was called, if yes, it recalculates the pitch
1664 * for all active voices.
1665 */
1666 void ProcessScaleTuningChange() {
1667 const bool changed = ScaleTuningChanged.readAndReset();
1668 if (!changed) return;
1669
1670 for (int i = 0; i < engineChannels.size(); i++) {
1671 EngineChannelBase<V, R, I>* channel =
1672 static_cast<EngineChannelBase<V, R, I>*>(engineChannels[i]);
1673 channel->OnScaleTuningChanged();
1674 }
1675 }
1676
1677 private:
1678 Pool<V>* pVoicePool; ///< Contains all voices that can be activated.
1679 Pool<RR*> SuspendedRegions;
1680 Mutex SuspendedRegionsMutex;
1681 Condition SuspensionChangeOngoing;
1682 RR* pPendingRegionSuspension;
1683 RR* pPendingRegionResumption;
1684 int iPendingStreamDeletions;
1685 };
1686
1687 template <class V, class RR, class R, class D, class IM, class I>
1688 IM EngineBase<V, RR, R, D, IM, I>::instruments;
1689
1690 } // namespace LinuxSampler
1691
1692 #endif /* __LS_ENGINEBASE_H__ */
1693

  ViewVC Help
Powered by ViewVC