/[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 2871 - (show annotations) (download) (as text)
Sun Apr 10 18:22:23 2016 UTC (7 years, 11 months ago) by schoenebeck
File MIME type: text/x-c++hdr
File size: 99098 byte(s)
* All engines: Implemented scheduler for delayed MIDI events and for
  suspended real-time instrument scripts.
* Real-Time instrument scripts: Implemented support for built-in "wait()"
  function's "duration-us" argument, thus scripts using this function are
  now correctly resumed after the requested amount of microseconds.
* Real-Time instrument scripts: Implemented support for built-in
  "play_note()" function's "duration-us" argument, thus notes triggered
  with this argument are now correctly released after the requested amount
  of microseconds.
* Real-Time instrument scripts: Fixed crash which happened when trying to
  reference an undeclared script variable.
* Real-Time instrument scripts: Script events were not cleared when
  engine channel was reset, potentially causing undefined behavior.
* All engines: Attempt to partly fix resetting engine channels vs.
  resetting engine, an overall cleanup of the Reset*(),
  ConnectAudioDevice(), DisconnectAudioDevice() API methods would still be
  desirable though, because the current situation is still inconsistent
  and error prone.
* Bumped version (2.0.0.svn2).

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

  ViewVC Help
Powered by ViewVC