3 |
* LinuxSampler - modular, streaming capable sampler * |
* LinuxSampler - modular, streaming capable sampler * |
4 |
* * |
* * |
5 |
* Copyright (C) 2003,2004 by Benno Senoner and Christian Schoenebeck * |
* Copyright (C) 2003,2004 by Benno Senoner and Christian Schoenebeck * |
6 |
* Copyright (C) 2005-2009 Christian Schoenebeck * |
* Copyright (C) 2005-2008 Christian Schoenebeck * |
7 |
* Copyright (C) 2009 Grigor Iliev * |
* Copyright (C) 2009-2012 Christian Schoenebeck and Grigor Iliev * |
8 |
|
* Copyright (C) 2012-2017 Christian Schoenebeck and Andreas Persson * |
9 |
* * |
* * |
10 |
* This program is free software; you can redistribute it and/or modify * |
* 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 * |
* it under the terms of the GNU General Public License as published by * |
33 |
#include "InstrumentManager.h" |
#include "InstrumentManager.h" |
34 |
#include "../common/global_private.h" |
#include "../common/global_private.h" |
35 |
|
|
36 |
|
// a bit headroom over CONFIG_MAX_VOICES to avoid minor complications i.e. under voice stealing conditions |
37 |
|
#define MAX_NOTES_HEADROOM 3 |
38 |
|
#define GLOBAL_MAX_NOTES (GLOBAL_MAX_VOICES * MAX_NOTES_HEADROOM) |
39 |
|
|
40 |
namespace LinuxSampler { |
namespace LinuxSampler { |
41 |
|
|
49 |
class IM /* Instrument Manager */, |
class IM /* Instrument Manager */, |
50 |
class I /* Instrument */ |
class I /* Instrument */ |
51 |
> |
> |
52 |
class EngineBase: public AbstractEngine, public RegionPools<R>, public VoicePool<V> { |
class EngineBase: public AbstractEngine, public RegionPools<R>, public NotePool<V> { |
53 |
|
|
54 |
public: |
public: |
55 |
|
typedef typename RTList< Note<V> >::Iterator NoteIterator; |
56 |
typedef typename RTList<V>::Iterator VoiceIterator; |
typedef typename RTList<V>::Iterator VoiceIterator; |
57 |
typedef typename Pool<V>::Iterator PoolVoiceIterator; |
typedef typename Pool<V>::Iterator PoolVoiceIterator; |
58 |
typedef typename RTList<RR*>::Iterator RootRegionIterator; |
typedef typename RTList<RR*>::Iterator RootRegionIterator; |
59 |
typedef typename MidiKeyboardManager<V>::MidiKey MidiKey; |
typedef typename MidiKeyboardManager<V>::MidiKey MidiKey; |
60 |
|
|
61 |
EngineBase() : SuspendedRegions(128) { |
EngineBase() : noteIDPool(GLOBAL_MAX_NOTES), SuspendedRegions(128) { |
62 |
pDiskThread = NULL; |
pDiskThread = NULL; |
63 |
|
pNotePool = new Pool< Note<V> >(GLOBAL_MAX_NOTES); |
64 |
|
pNotePool->setPoolElementIDsReservedBits(INSTR_SCRIPT_EVENT_ID_RESERVED_BITS); |
65 |
pVoicePool = new Pool<V>(GLOBAL_MAX_VOICES); |
pVoicePool = new Pool<V>(GLOBAL_MAX_VOICES); |
66 |
pRegionPool[0] = new Pool<R*>(GLOBAL_MAX_VOICES); |
pRegionPool[0] = new Pool<R*>(GLOBAL_MAX_VOICES); |
67 |
pRegionPool[1] = new Pool<R*>(GLOBAL_MAX_VOICES); |
pRegionPool[1] = new Pool<R*>(GLOBAL_MAX_VOICES); |
68 |
pVoiceStealingQueue = new RTList<Event>(pEventPool); |
pVoiceStealingQueue = new RTList<Event>(pEventPool); |
69 |
iMaxDiskStreams = GLOBAL_MAX_STREAMS; |
iMaxDiskStreams = GLOBAL_MAX_STREAMS; |
70 |
|
|
71 |
for (VoiceIterator iterVoice = pVoicePool->allocAppend(); iterVoice == pVoicePool->last(); iterVoice = pVoicePool->allocAppend()) { |
// init all Voice objects in voice pool |
72 |
|
for (VoiceIterator iterVoice = pVoicePool->allocAppend(); |
73 |
|
iterVoice; iterVoice = pVoicePool->allocAppend()) |
74 |
|
{ |
75 |
iterVoice->SetEngine(this); |
iterVoice->SetEngine(this); |
76 |
} |
} |
77 |
pVoicePool->clear(); |
pVoicePool->clear(); |
78 |
|
|
79 |
|
// init all Note objects in note pool |
80 |
|
for (NoteIterator itNote = pNotePool->allocAppend(); itNote; |
81 |
|
itNote = pNotePool->allocAppend()) |
82 |
|
{ |
83 |
|
itNote->init(pVoicePool, ¬eIDPool); |
84 |
|
} |
85 |
|
pNotePool->clear(); |
86 |
|
|
87 |
ResetInternal(); |
ResetInternal(); |
88 |
ResetScaleTuning(); |
ResetScaleTuning(); |
89 |
ResetSuspendedRegions(); |
ResetSuspendedRegions(); |
97 |
dmsg(1,("OK\n")); |
dmsg(1,("OK\n")); |
98 |
} |
} |
99 |
|
|
100 |
|
if (pNotePool) { |
101 |
|
pNotePool->clear(); |
102 |
|
delete pNotePool; |
103 |
|
} |
104 |
|
|
105 |
if (pVoicePool) { |
if (pVoicePool) { |
106 |
pVoicePool->clear(); |
pVoicePool->clear(); |
107 |
delete pVoicePool; |
delete pVoicePool; |
127 |
* @param Samples - number of sample points to be rendered |
* @param Samples - number of sample points to be rendered |
128 |
* @returns 0 on success |
* @returns 0 on success |
129 |
*/ |
*/ |
130 |
virtual int RenderAudio(uint Samples) { |
virtual int RenderAudio(uint Samples) OVERRIDE { |
131 |
dmsg(8,("RenderAudio(Samples=%d)\n", Samples)); |
dmsg(8,("RenderAudio(Samples=%d)\n", Samples)); |
132 |
|
|
133 |
// return if engine disabled |
// return if engine disabled |
163 |
dmsg(5,("Engine: Sysex received\n")); |
dmsg(5,("Engine: Sysex received\n")); |
164 |
ProcessSysex(itEvent); |
ProcessSysex(itEvent); |
165 |
break; |
break; |
166 |
|
default: ; // noop |
167 |
} |
} |
168 |
} |
} |
169 |
} |
} |
170 |
|
|
171 |
|
// In case scale tuning has been changed, recalculate pitch for |
172 |
|
// all active voices. |
173 |
|
ProcessScaleTuningChange(); |
174 |
|
|
175 |
// reset internal voice counter (just for statistic of active voices) |
// reset internal voice counter (just for statistic of active voices) |
176 |
ActiveVoiceCountTemp = 0; |
ActiveVoiceCountTemp = 0; |
202 |
PostProcess(engineChannels[i]); |
PostProcess(engineChannels[i]); |
203 |
} |
} |
204 |
|
|
205 |
|
// Just for debugging: dump the amount of free Note objects to |
206 |
|
// the terminal (note due to the static variables being used, |
207 |
|
// this is currently just intended for debugging with only one |
208 |
|
// engine channel). |
209 |
|
#if (CONFIG_DEBUG_LEVEL >= 3) |
210 |
|
{ |
211 |
|
static int slice = 0; |
212 |
|
static int noteCount = -1; |
213 |
|
if (slice++ % 10 == 0) { |
214 |
|
int n = pNotePool->countFreeElements(); |
215 |
|
if (n != noteCount) { |
216 |
|
noteCount = n; |
217 |
|
dmsg(1,("[%d] free Note objects count = %d\n", slice / 10, n)); |
218 |
|
} |
219 |
|
} |
220 |
|
} |
221 |
|
#endif |
222 |
|
|
223 |
// empty the engine's event list for the next audio fragment |
// empty the engine's event list for the next audio fragment |
224 |
ClearEventLists(); |
ClearEventLists(); |
235 |
// been deleted by the disk thread |
// been deleted by the disk thread |
236 |
if (iPendingStreamDeletions) ProcessPendingStreamDeletions(); |
if (iPendingStreamDeletions) ProcessPendingStreamDeletions(); |
237 |
|
|
238 |
|
// Release the instrument change command. (This has to |
239 |
|
// be done after all voices have been rendered and not |
240 |
|
// in HandleInstrumentChanges, as the RegionsInUse |
241 |
|
// list has been built up by the voice renderers.) |
242 |
|
for (int i = 0; i < engineChannels.size(); i++) { |
243 |
|
EngineChannelBase<V, R, I>* channel = |
244 |
|
static_cast<EngineChannelBase<V, R, I>*>(engineChannels[i]); |
245 |
|
channel->InstrumentChangeCommandReader.Unlock(); |
246 |
|
} |
247 |
FrameTime += Samples; |
FrameTime += Samples; |
248 |
|
|
249 |
EngineDisabled.RttDone(); |
EngineDisabled.RttDone(); |
250 |
return 0; |
return 0; |
251 |
} |
} |
252 |
|
|
253 |
virtual int MaxVoices() { return pVoicePool->poolSize(); } |
virtual int MaxVoices() OVERRIDE { return pVoicePool->poolSize(); } |
254 |
|
|
255 |
virtual void SetMaxVoices(int iVoices) throw (Exception) { |
virtual void SetMaxVoices(int iVoices) throw (Exception) OVERRIDE { |
256 |
if (iVoices < 1) |
if (iVoices < 1) |
257 |
throw Exception("Maximum voices for an engine cannot be set lower than 1"); |
throw Exception("Maximum voices for an engine cannot be set lower than 1"); |
258 |
|
|
276 |
pChannel->ResetRegionsInUse(pRegionPool); |
pChannel->ResetRegionsInUse(pRegionPool); |
277 |
} |
} |
278 |
|
|
279 |
|
// FIXME: Shouldn't all those pool elements be freed before resizing the pools? |
280 |
try { |
try { |
281 |
pVoicePool->resizePool(iVoices); |
pVoicePool->resizePool(iVoices); |
282 |
|
pNotePool->resizePool(iVoices * MAX_NOTES_HEADROOM); |
283 |
|
noteIDPool.resizePool(iVoices * MAX_NOTES_HEADROOM); |
284 |
} catch (...) { |
} catch (...) { |
285 |
throw Exception("FATAL: Could not resize voice pool!"); |
throw Exception("FATAL: Could not resize voice pool!"); |
286 |
} |
} |
287 |
|
|
288 |
for (VoiceIterator iterVoice = pVoicePool->allocAppend(); iterVoice == pVoicePool->last(); iterVoice = pVoicePool->allocAppend()) { |
for (VoiceIterator iterVoice = pVoicePool->allocAppend(); |
289 |
|
iterVoice; iterVoice = pVoicePool->allocAppend()) |
290 |
|
{ |
291 |
iterVoice->SetEngine(this); |
iterVoice->SetEngine(this); |
292 |
iterVoice->pDiskThread = this->pDiskThread; |
iterVoice->pDiskThread = this->pDiskThread; |
293 |
} |
} |
294 |
pVoicePool->clear(); |
pVoicePool->clear(); |
295 |
|
|
296 |
|
for (NoteIterator itNote = pNotePool->allocAppend(); itNote; |
297 |
|
itNote = pNotePool->allocAppend()) |
298 |
|
{ |
299 |
|
itNote->init(pVoicePool, ¬eIDPool); |
300 |
|
} |
301 |
|
pNotePool->clear(); |
302 |
|
|
303 |
|
PostSetMaxVoices(iVoices); |
304 |
ResumeAll(); |
ResumeAll(); |
305 |
} |
} |
306 |
|
|
307 |
|
/** Called after the new max number of voices is set and before resuming the engine. */ |
308 |
|
virtual void PostSetMaxVoices(int iVoices) { } |
309 |
|
|
310 |
virtual uint DiskStreamCount() { return (pDiskThread) ? pDiskThread->GetActiveStreamCount() : 0; } |
virtual uint DiskStreamCount() OVERRIDE { return (pDiskThread) ? pDiskThread->GetActiveStreamCount() : 0; } |
311 |
virtual uint DiskStreamCountMax() { return (pDiskThread) ? pDiskThread->ActiveStreamCountMax : 0; } |
virtual uint DiskStreamCountMax() OVERRIDE { return (pDiskThread) ? pDiskThread->ActiveStreamCountMax : 0; } |
312 |
virtual int MaxDiskStreams() { return iMaxDiskStreams; } |
virtual int MaxDiskStreams() OVERRIDE { return iMaxDiskStreams; } |
313 |
|
|
314 |
virtual void SetMaxDiskStreams(int iStreams) throw (Exception) { |
virtual void SetMaxDiskStreams(int iStreams) throw (Exception) OVERRIDE { |
315 |
if (iStreams < 0) |
if (iStreams < 0) |
316 |
throw Exception("Maximum disk streams for an engine cannot be set lower than 0"); |
throw Exception("Maximum disk streams for an engine cannot be set lower than 0"); |
317 |
|
|
326 |
ResumeAll(); |
ResumeAll(); |
327 |
} |
} |
328 |
|
|
329 |
virtual String DiskStreamBufferFillBytes() { return (pDiskThread) ? pDiskThread->GetBufferFillBytes() : ""; } |
virtual String DiskStreamBufferFillBytes() OVERRIDE { return (pDiskThread) ? pDiskThread->GetBufferFillBytes() : ""; } |
330 |
virtual String DiskStreamBufferFillPercentage() { return (pDiskThread) ? pDiskThread->GetBufferFillPercentage() : ""; } |
virtual String DiskStreamBufferFillPercentage() OVERRIDE { return (pDiskThread) ? pDiskThread->GetBufferFillPercentage() : ""; } |
331 |
virtual InstrumentManager* GetInstrumentManager() { return &instruments; } |
virtual InstrumentManager* GetInstrumentManager() OVERRIDE { return &instruments; } |
332 |
|
|
333 |
/** |
/** |
334 |
* Connect this engine instance with the given audio output device. |
* Connect this engine instance with the given audio output device. |
339 |
* |
* |
340 |
* @param pAudioOut - audio output device to connect to |
* @param pAudioOut - audio output device to connect to |
341 |
*/ |
*/ |
342 |
virtual void Connect(AudioOutputDevice* pAudioOut) { |
virtual void Connect(AudioOutputDevice* pAudioOut) OVERRIDE { |
343 |
// caution: don't ignore if connecting to the same device here, |
// caution: don't ignore if connecting to the same device here, |
344 |
// because otherwise SetMaxDiskStreams() implementation won't work anymore! |
// because otherwise SetMaxDiskStreams() implementation won't work anymore! |
345 |
|
|
368 |
MinFadeOutSamples = MaxSamplesPerCycle; |
MinFadeOutSamples = MaxSamplesPerCycle; |
369 |
// lower minimum release time |
// lower minimum release time |
370 |
const float minReleaseTime = (float) MaxSamplesPerCycle / (float) SampleRate; |
const float minReleaseTime = (float) MaxSamplesPerCycle / (float) SampleRate; |
371 |
|
pVoicePool->clear(); |
372 |
for (VoiceIterator iterVoice = pVoicePool->allocAppend(); iterVoice == pVoicePool->last(); iterVoice = pVoicePool->allocAppend()) { |
for (VoiceIterator iterVoice = pVoicePool->allocAppend(); iterVoice == pVoicePool->last(); iterVoice = pVoicePool->allocAppend()) { |
373 |
iterVoice->EG1.CalculateFadeOutCoeff(minReleaseTime, SampleRate); |
iterVoice->CalculateFadeOutCoeff(minReleaseTime, SampleRate); |
374 |
} |
} |
375 |
pVoicePool->clear(); |
pVoicePool->clear(); |
376 |
} |
} |
389 |
exit(EXIT_FAILURE); |
exit(EXIT_FAILURE); |
390 |
} |
} |
391 |
|
|
392 |
|
pVoicePool->clear(); |
393 |
for (VoiceIterator iterVoice = pVoicePool->allocAppend(); iterVoice == pVoicePool->last(); iterVoice = pVoicePool->allocAppend()) { |
for (VoiceIterator iterVoice = pVoicePool->allocAppend(); iterVoice == pVoicePool->last(); iterVoice = pVoicePool->allocAppend()) { |
394 |
iterVoice->pDiskThread = this->pDiskThread; |
iterVoice->pDiskThread = this->pDiskThread; |
395 |
dmsg(3,("d")); |
dmsg(3,("d")); |
396 |
} |
} |
397 |
pVoicePool->clear(); |
pVoicePool->clear(); |
398 |
|
|
399 |
// (re)create event generator |
// update event generator |
400 |
if (pEventGenerator) delete pEventGenerator; |
pEventGenerator->SetSampleRate(pAudioOut->SampleRate()); |
|
pEventGenerator = new EventGenerator(pAudioOut->SampleRate()); |
|
401 |
|
|
402 |
dmsg(1,("Starting disk thread...")); |
dmsg(1,("Starting disk thread...")); |
403 |
pDiskThread->StartThread(); |
pDiskThread->StartThread(); |
404 |
dmsg(1,("OK\n")); |
dmsg(1,("OK\n")); |
405 |
|
|
406 |
|
bool printEqInfo = true; |
407 |
for (VoiceIterator iterVoice = pVoicePool->allocAppend(); iterVoice == pVoicePool->last(); iterVoice = pVoicePool->allocAppend()) { |
for (VoiceIterator iterVoice = pVoicePool->allocAppend(); iterVoice == pVoicePool->last(); iterVoice = pVoicePool->allocAppend()) { |
408 |
if (!iterVoice->pDiskThread) { |
if (!iterVoice->pDiskThread) { |
409 |
dmsg(0,("Engine -> voice::trigger: !pDiskThread\n")); |
dmsg(0,("Engine -> voice::trigger: !pDiskThread\n")); |
410 |
exit(EXIT_FAILURE); |
exit(EXIT_FAILURE); |
411 |
} |
} |
412 |
|
|
413 |
|
iterVoice->CreateEq(); |
414 |
|
|
415 |
|
if(printEqInfo) { |
416 |
|
iterVoice->PrintEqInfo(); |
417 |
|
printEqInfo = false; |
418 |
|
} |
419 |
} |
} |
420 |
pVoicePool->clear(); |
pVoicePool->clear(); |
421 |
|
|
422 |
|
// (re)create dedicated voice audio buffers |
423 |
|
//TODO: we could optimize resource usage a bit by just allocating these dedicated voice buffers when there is at least one engine channel with FX sends, because only in this case those special buffers are used actually, but since it would usually only save couple bytes in total, its probably not worth it |
424 |
|
if (pDedicatedVoiceChannelLeft) delete pDedicatedVoiceChannelLeft; |
425 |
|
if (pDedicatedVoiceChannelRight) delete pDedicatedVoiceChannelRight; |
426 |
|
pDedicatedVoiceChannelLeft = new AudioChannel(0, MaxSamplesPerCycle); |
427 |
|
pDedicatedVoiceChannelRight = new AudioChannel(1, MaxSamplesPerCycle); |
428 |
|
} |
429 |
|
|
430 |
|
// Implementattion for abstract method derived from Engine. |
431 |
|
virtual void ReconnectAudioOutputDevice() OVERRIDE { |
432 |
|
SuspendAll(); |
433 |
|
if (pAudioOutputDevice) Connect(pAudioOutputDevice); |
434 |
|
ResumeAll(); |
435 |
} |
} |
436 |
|
|
437 |
/** |
/** |
486 |
* @param pRegion - region the engine shall stop using |
* @param pRegion - region the engine shall stop using |
487 |
*/ |
*/ |
488 |
virtual void Suspend(RR* pRegion) { |
virtual void Suspend(RR* pRegion) { |
489 |
dmsg(2,("EngineBase: Suspending Region %x ...\n",pRegion)); |
dmsg(2,("EngineBase: Suspending Region %p ...\n",(void*)pRegion)); |
490 |
SuspendedRegionsMutex.Lock(); |
{ |
491 |
SuspensionChangeOngoing.Set(true); |
LockGuard lock(SuspendedRegionsMutex); |
492 |
pPendingRegionSuspension = pRegion; |
SuspensionChangeOngoing.Set(true); |
493 |
SuspensionChangeOngoing.WaitAndUnlockIf(true); |
pPendingRegionSuspension = pRegion; |
494 |
SuspendedRegionsMutex.Unlock(); |
SuspensionChangeOngoing.WaitAndUnlockIf(true); |
495 |
dmsg(2,("EngineBase: Region %x suspended.",pRegion)); |
} |
496 |
|
dmsg(2,("EngineBase: Region %p suspended.",(void*)pRegion)); |
497 |
} |
} |
498 |
|
|
499 |
/** |
/** |
503 |
* @param pRegion - region the engine shall be allowed to use again |
* @param pRegion - region the engine shall be allowed to use again |
504 |
*/ |
*/ |
505 |
virtual void Resume(RR* pRegion) { |
virtual void Resume(RR* pRegion) { |
506 |
dmsg(2,("EngineBase: Resuming Region %x ...\n",pRegion)); |
dmsg(2,("EngineBase: Resuming Region %p ...\n",(void*)pRegion)); |
507 |
SuspendedRegionsMutex.Lock(); |
{ |
508 |
SuspensionChangeOngoing.Set(true); |
LockGuard lock(SuspendedRegionsMutex); |
509 |
pPendingRegionResumption = pRegion; |
SuspensionChangeOngoing.Set(true); |
510 |
SuspensionChangeOngoing.WaitAndUnlockIf(true); |
pPendingRegionResumption = pRegion; |
511 |
SuspendedRegionsMutex.Unlock(); |
SuspensionChangeOngoing.WaitAndUnlockIf(true); |
512 |
dmsg(2,("EngineBase: Region %x resumed.\n",pRegion)); |
} |
513 |
|
dmsg(2,("EngineBase: Region %p resumed.\n",(void*)pRegion)); |
514 |
} |
} |
515 |
|
|
516 |
virtual void ResetSuspendedRegions() { |
virtual void ResetSuspendedRegions() { |
613 |
} |
} |
614 |
|
|
615 |
// implementation of abstract method derived from class 'LinuxSampler::RegionPools' |
// implementation of abstract method derived from class 'LinuxSampler::RegionPools' |
616 |
virtual Pool<R*>* GetRegionPool(int index) { |
virtual Pool<R*>* GetRegionPool(int index) OVERRIDE { |
617 |
if (index < 0 || index > 1) throw Exception("Index out of bounds"); |
if (index < 0 || index > 1) throw Exception("Index out of bounds"); |
618 |
return pRegionPool[index]; |
return pRegionPool[index]; |
619 |
} |
} |
620 |
|
|
621 |
// implementation of abstract method derived from class 'LinuxSampler::VoicePool' |
// implementation of abstract methods derived from class 'LinuxSampler::NotePool' |
622 |
virtual Pool<V>* GetVoicePool() { return pVoicePool; } |
virtual Pool<V>* GetVoicePool() OVERRIDE { return pVoicePool; } |
623 |
|
virtual Pool< Note<V> >* GetNotePool() OVERRIDE { return pNotePool; } |
624 |
|
virtual Pool<note_id_t>* GetNoteIDPool() OVERRIDE { return ¬eIDPool; } |
625 |
|
|
626 |
D* GetDiskThread() { return pDiskThread; } |
D* GetDiskThread() { return pDiskThread; } |
627 |
|
|
628 |
//friend class EngineChannelBase<V, R, I>; |
//friend class EngineChannelBase<V, R, I>; |
629 |
|
|
630 |
|
static IM instruments; |
631 |
|
|
632 |
protected: |
protected: |
633 |
class SuspensionVoiceHandler : public MidiKeyboardManager<V>::VoiceHandler { |
class SuspensionVoiceHandler : public MidiKeyboardManager<V>::VoiceHandler { |
634 |
public: |
public: |
635 |
int PendingStreamDeletions; |
int PendingStreamDeletions; |
636 |
RR* pPendingRegionSuspension; |
RR* pPendingRegionSuspension; |
637 |
|
|
638 |
SuspensionVoiceHandler(RR* pPendingRegionSuspension) { |
SuspensionVoiceHandler(RR* pPendingRegionSuspension) { |
639 |
PendingStreamDeletions = 0; |
PendingStreamDeletions = 0; |
640 |
this->pPendingRegionSuspension = pPendingRegionSuspension; |
this->pPendingRegionSuspension = pPendingRegionSuspension; |
641 |
} |
} |
642 |
|
|
643 |
virtual bool Process(MidiKey* pMidiKey) { |
virtual bool Process(MidiKey* pMidiKey) OVERRIDE { |
644 |
VoiceIterator itVoice = pMidiKey->pActiveVoices->first(); |
NoteIterator itNote = pMidiKey->pActiveNotes->first(); |
645 |
|
VoiceIterator itVoice = itNote->pActiveVoices->first(); |
646 |
// if current key is not associated with this region, skip this key |
// if current key is not associated with this region, skip this key |
647 |
if (itVoice->GetRegion()->GetParent() != pPendingRegionSuspension) return false; |
if (itVoice->GetRegion()->GetParent() != pPendingRegionSuspension) return false; |
648 |
|
|
649 |
return true; |
return true; |
650 |
} |
} |
651 |
|
|
652 |
virtual void Process(VoiceIterator& itVoice) { |
virtual void Process(VoiceIterator& itVoice) OVERRIDE { |
653 |
// request a notification from disk thread side for stream deletion |
// request a notification from disk thread side for stream deletion |
654 |
const Stream::Handle hStream = itVoice->KillImmediately(true); |
const Stream::Handle hStream = itVoice->KillImmediately(true); |
655 |
if (hStream != Stream::INVALID_HANDLE) { // voice actually used a stream |
if (hStream != Stream::INVALID_HANDLE) { // voice actually used a stream |
659 |
} |
} |
660 |
}; |
}; |
661 |
|
|
|
static IM instruments; |
|
|
|
|
662 |
Pool<R*>* pRegionPool[2]; ///< Double buffered pool, used by the engine channels to keep track of regions in use. |
Pool<R*>* pRegionPool[2]; ///< Double buffered pool, used by the engine channels to keep track of regions in use. |
663 |
int MinFadeOutSamples; ///< The number of samples needed to make an instant fade out (e.g. for voice stealing) without leading to clicks. |
int MinFadeOutSamples; ///< The number of samples needed to make an instant fade out (e.g. for voice stealing) without leading to clicks. |
664 |
D* pDiskThread; |
D* pDiskThread; |
665 |
|
|
|
int VoiceSpawnsLeft; ///< We only allow CONFIG_MAX_VOICES voices to be spawned per audio fragment, we use this variable to ensure this limit. |
|
666 |
int ActiveVoiceCountTemp; ///< number of currently active voices (for internal usage, will be used for incrementation) |
int ActiveVoiceCountTemp; ///< number of currently active voices (for internal usage, will be used for incrementation) |
667 |
VoiceIterator itLastStolenVoice; ///< Only for voice stealing: points to the last voice which was theft in current audio fragment, NULL otherwise. |
VoiceIterator itLastStolenVoice; ///< Only for voice stealing: points to the last voice which was theft in current audio fragment, NULL otherwise. |
668 |
|
NoteIterator itLastStolenNote; ///< Only for voice stealing: points to the last note from which was theft in current audio fragment, NULL otherwise. |
669 |
RTList<uint>::Iterator iuiLastStolenKey; ///< Only for voice stealing: key number of last key on which the last voice was theft in current audio fragment, NULL otherwise. |
RTList<uint>::Iterator iuiLastStolenKey; ///< Only for voice stealing: key number of last key on which the last voice was theft in current audio fragment, NULL otherwise. |
670 |
EngineChannelBase<V, R, I>* pLastStolenChannel; ///< Only for voice stealing: points to the engine channel on which the previous voice was stolen in this audio fragment. |
EngineChannelBase<V, R, I>* pLastStolenChannel; ///< Only for voice stealing: points to the engine channel on which the previous voice was stolen in this audio fragment. |
671 |
VoiceIterator itLastStolenVoiceGlobally; ///< Same as itLastStolenVoice, but engine globally |
VoiceIterator itLastStolenVoiceGlobally; ///< Same as itLastStolenVoice, but engine globally |
672 |
|
NoteIterator itLastStolenNoteGlobally; ///< Same as itLastStolenNote, but engine globally |
673 |
RTList<uint>::Iterator iuiLastStolenKeyGlobally; ///< Same as iuiLastStolenKey, but engine globally |
RTList<uint>::Iterator iuiLastStolenKeyGlobally; ///< Same as iuiLastStolenKey, but engine globally |
674 |
RTList<Event>* pVoiceStealingQueue; ///< All voice-launching events which had to be postponed due to free voice shortage. |
RTList<Event>* pVoiceStealingQueue; ///< All voice-launching events which had to be postponed due to free voice shortage. |
675 |
Mutex ResetInternalMutex; ///< Mutex to protect the ResetInternal function for concurrent usage (e.g. by the lscp and instrument loader threads). |
Mutex ResetInternalMutex; ///< Mutex to protect the ResetInternal function for concurrent usage (e.g. by the lscp and instrument loader threads). |
676 |
int iMaxDiskStreams; |
int iMaxDiskStreams; |
677 |
|
|
678 |
|
NoteBase* NoteByID(note_id_t id) OVERRIDE { |
679 |
|
NoteIterator itNote = GetNotePool()->fromID(id); |
680 |
|
if (!itNote) return NULL; |
681 |
|
return &*itNote; |
682 |
|
} |
683 |
|
|
684 |
|
/** |
685 |
|
* Gets a new @c Note object from the note pool, initializes it |
686 |
|
* appropriately, links it with requested parent note (if |
687 |
|
* requested), moves it to the appropriate key's list of active |
688 |
|
* notes it, and sticks the new note's unique ID to the |
689 |
|
* passed @a pNoteOnEvent. |
690 |
|
* |
691 |
|
* @param pEngineChannel - engine channel on which this event happened |
692 |
|
* @param pNoteOnEvent - event which caused this |
693 |
|
* @returns new note's unique ID (or zero on error) |
694 |
|
*/ |
695 |
|
note_id_t LaunchNewNote(LinuxSampler::EngineChannel* pEngineChannel, Pool<Event>::Iterator& itNoteOnEvent) OVERRIDE { |
696 |
|
EngineChannelBase<V, R, I>* pChannel = static_cast<EngineChannelBase<V, R, I>*>(pEngineChannel); |
697 |
|
Pool< Note<V> >* pNotePool = GetNotePool(); |
698 |
|
|
699 |
|
if (pNotePool->poolIsEmpty()) { |
700 |
|
dmsg(1,("Engine: Could not launch new note; Note pool empty!\n")); |
701 |
|
return 0; // error |
702 |
|
} |
703 |
|
|
704 |
|
// create a new note (for new voices to be assigned to) |
705 |
|
//NoteIterator itNewNote = pKey->pActiveNotes->allocAppend(); |
706 |
|
NoteIterator itNewNote = pNotePool->allocAppend(); |
707 |
|
const note_id_t newNoteID = pNotePool->getID(itNewNote); |
708 |
|
|
709 |
|
// remember the engine's time when this note was triggered exactly |
710 |
|
itNewNote->triggerSchedTime = itNoteOnEvent->SchedTime(); |
711 |
|
|
712 |
|
// usually the new note (and its subsequent voices) will be |
713 |
|
// allocated on the key provided by the event's note number, |
714 |
|
// however if this new note is requested not to be a regular |
715 |
|
// note, but rather a child note, then this new note will be |
716 |
|
// allocated on the parent note's key instead in order to |
717 |
|
// release the child note simultaniously with its parent note |
718 |
|
itNewNote->hostKey = itNoteOnEvent->Param.Note.Key; |
719 |
|
|
720 |
|
// in case this new note was requested to be a child note, |
721 |
|
// then retrieve its parent note and link them with each other |
722 |
|
const note_id_t parentNoteID = itNoteOnEvent->Param.Note.ParentNoteID; |
723 |
|
if (parentNoteID) { |
724 |
|
NoteIterator itParentNote = pNotePool->fromID(parentNoteID); |
725 |
|
if (itParentNote) { |
726 |
|
RTList<note_id_t>::Iterator itChildNoteID = itParentNote->pChildNotes->allocAppend(); |
727 |
|
if (itChildNoteID) { |
728 |
|
// link parent and child note with each other |
729 |
|
*itChildNoteID = newNoteID; |
730 |
|
itNewNote->parentNoteID = parentNoteID; |
731 |
|
itNewNote->hostKey = itParentNote->hostKey; |
732 |
|
} else { |
733 |
|
dmsg(1,("Engine: Could not assign new note as child note; Note ID pool empty!\n")); |
734 |
|
pNotePool->free(itNewNote); |
735 |
|
return 0; // error |
736 |
|
} |
737 |
|
} else { |
738 |
|
// the parent note was apparently released already, so |
739 |
|
// free the new note again and inform caller that it |
740 |
|
// should drop the event |
741 |
|
dmsg(3,("Engine: Could not assign new note as child note; Parent note is gone!\n")); |
742 |
|
pNotePool->free(itNewNote); |
743 |
|
return 0; // error |
744 |
|
} |
745 |
|
} |
746 |
|
|
747 |
|
dmsg(2,("Launched new note on host key %d\n", itNewNote->hostKey)); |
748 |
|
|
749 |
|
// copy event which caused this note |
750 |
|
itNewNote->cause = *itNoteOnEvent; |
751 |
|
itNewNote->eventID = pEventPool->getID(itNoteOnEvent); |
752 |
|
if (!itNewNote->eventID) { |
753 |
|
dmsg(0,("Engine: No valid event ID resolved for note. This is a bug!!!\n")); |
754 |
|
} |
755 |
|
|
756 |
|
// move new note to its host key |
757 |
|
MidiKey* pKey = &pChannel->pMIDIKeyInfo[itNewNote->hostKey]; |
758 |
|
itNewNote.moveToEndOf(pKey->pActiveNotes); |
759 |
|
pChannel->markKeyAsActive(pKey); |
760 |
|
|
761 |
|
// assign unique note ID of this new note to the original note on event |
762 |
|
itNoteOnEvent->Param.Note.ID = newNoteID; |
763 |
|
|
764 |
|
return newNoteID; // success |
765 |
|
} |
766 |
|
|
767 |
/** |
/** |
768 |
* Dispatch and handle all events in this audio fragment for the given |
* Dispatch and handle all events in this audio fragment for the given |
769 |
* engine channel. |
* engine channel. |
779 |
AbstractEngineChannel* pChannel = static_cast<AbstractEngineChannel*>(pEngineChannel); |
AbstractEngineChannel* pChannel = static_cast<AbstractEngineChannel*>(pEngineChannel); |
780 |
pChannel->ImportEvents(Samples); |
pChannel->ImportEvents(Samples); |
781 |
|
|
782 |
// process events |
// if a valid real-time instrument script is loaded, pre-process |
783 |
|
// the event list by running the script now, since the script |
784 |
|
// might filter events or add new ones for this cycle |
785 |
|
if (pChannel->pScript) { |
786 |
|
const sched_time_t fragmentEndTime = pEventGenerator->schedTimeAtCurrentFragmentEnd(); |
787 |
|
|
788 |
|
// resume suspended script executions been scheduled for |
789 |
|
// this audio fragment cycle (which were suspended in a |
790 |
|
// previous audio fragment cycle) |
791 |
|
ProcessSuspendedScriptEvents(pChannel, fragmentEndTime); |
792 |
|
|
793 |
|
// spawn new script executions for the new MIDI events of |
794 |
|
// this audio fragment cycle |
795 |
|
// |
796 |
|
// FIXME: it would probably be better to just schedule newly spawned script executions here and then execute them altogether with already suspended ones all at once in order of all their scheduled timing |
797 |
|
for (RTList<Event>::Iterator itEvent = pChannel->pEvents->first(), |
798 |
|
end = pChannel->pEvents->end(); itEvent != end; ) |
799 |
|
{ |
800 |
|
//HACK: avoids iterator invalidation which might happen below since an instrument script might drop an event by direct raw pointer access (it would be considerable to extend the Iterator class to detect and circumvent this case by checking the "reincarnation" member variable). |
801 |
|
RTList<Event>::Iterator itNext = itEvent; |
802 |
|
++itNext; |
803 |
|
|
804 |
|
switch (itEvent->Type) { |
805 |
|
case Event::type_note_on: |
806 |
|
if (pChannel->pScript->handlerNote) |
807 |
|
ProcessEventByScript(pChannel, itEvent, pChannel->pScript->handlerNote); |
808 |
|
break; |
809 |
|
case Event::type_note_off: |
810 |
|
if (pChannel->pScript->handlerRelease) |
811 |
|
ProcessEventByScript(pChannel, itEvent, pChannel->pScript->handlerRelease); |
812 |
|
break; |
813 |
|
case Event::type_control_change: |
814 |
|
case Event::type_channel_pressure: |
815 |
|
case Event::type_pitchbend: |
816 |
|
if (pChannel->pScript->handlerController) |
817 |
|
ProcessEventByScript(pChannel, itEvent, pChannel->pScript->handlerController); |
818 |
|
break; |
819 |
|
case Event::type_note_pressure: |
820 |
|
//TODO: ... |
821 |
|
break; |
822 |
|
|
823 |
|
case Event::type_sysex: |
824 |
|
//TODO: ... |
825 |
|
break; |
826 |
|
|
827 |
|
case Event::type_cancel_release_key: |
828 |
|
case Event::type_release_key: |
829 |
|
case Event::type_release_note: |
830 |
|
case Event::type_play_note: |
831 |
|
case Event::type_stop_note: |
832 |
|
case Event::type_kill_note: |
833 |
|
case Event::type_note_synth_param: |
834 |
|
break; // noop |
835 |
|
} |
836 |
|
|
837 |
|
// see HACK comment above |
838 |
|
itEvent = itNext; |
839 |
|
} |
840 |
|
|
841 |
|
// this has to be run again, since the newly spawned scripts |
842 |
|
// above may have cause suspended scripts that must be |
843 |
|
// resumed within this same audio fragment cycle |
844 |
|
// |
845 |
|
// FIXME: see FIXME comment above |
846 |
|
ProcessSuspendedScriptEvents(pChannel, fragmentEndTime); |
847 |
|
} |
848 |
|
|
849 |
|
// if there are any delayed events scheduled for the current |
850 |
|
// audio fragment cycle, then move and sort them into the main |
851 |
|
// event list |
852 |
|
if (!pChannel->delayedEvents.queue.isEmpty()) { |
853 |
|
dmsg(5,("Engine: There are delayed MIDI events (total queue size: %d) ...\n", pChannel->delayedEvents.queue.size())); |
854 |
|
const sched_time_t fragmentEndTime = pEventGenerator->schedTimeAtCurrentFragmentEnd(); |
855 |
|
RTList<Event>::Iterator itEvent = pChannel->pEvents->first(); |
856 |
|
while (true) { |
857 |
|
RTList<ScheduledEvent>::Iterator itDelayedEventNode = |
858 |
|
pEventGenerator->popNextScheduledEvent( |
859 |
|
pChannel->delayedEvents.queue, |
860 |
|
pChannel->delayedEvents.schedulerNodes, |
861 |
|
fragmentEndTime |
862 |
|
); |
863 |
|
if (!itDelayedEventNode) break; |
864 |
|
// get the actual delayed event object and free the used scheduler node |
865 |
|
RTList<Event>::Iterator itDelayedEvent = itDelayedEventNode->itEvent; |
866 |
|
pChannel->delayedEvents.schedulerNodes.free(itDelayedEventNode); |
867 |
|
if (!itDelayedEvent) { // should never happen, but just to be sure ... |
868 |
|
dmsg(1,("Engine: Oops, invalid delayed event!\n")); |
869 |
|
continue; |
870 |
|
} |
871 |
|
// skip all events on main event list which have a time |
872 |
|
// before (or equal to) the delayed event to be inserted |
873 |
|
for (; itEvent && itEvent->FragmentPos() <= itDelayedEvent->FragmentPos(); |
874 |
|
++itEvent); |
875 |
|
// now move delayed event from delayedEvents.pList to |
876 |
|
// the current position on the main event list |
877 |
|
itEvent = itDelayedEvent.moveBefore(itEvent); |
878 |
|
dmsg(5,("Engine: Inserted event of type %d into main event list (queue size: %d).\n", itEvent->Type, pChannel->delayedEvents.queue.size())); |
879 |
|
} |
880 |
|
dmsg(5,("Engine: End of delayed events (total queue size: %d).\n", pChannel->delayedEvents.queue.size())); |
881 |
|
} |
882 |
|
|
883 |
|
// now process all events regularly |
884 |
{ |
{ |
885 |
RTList<Event>::Iterator itEvent = pChannel->pEvents->first(); |
RTList<Event>::Iterator itEvent = pChannel->pEvents->first(); |
886 |
RTList<Event>::Iterator end = pChannel->pEvents->end(); |
RTList<Event>::Iterator end = pChannel->pEvents->end(); |
890 |
dmsg(5,("Engine: Note on received\n")); |
dmsg(5,("Engine: Note on received\n")); |
891 |
ProcessNoteOn((EngineChannel*)itEvent->pEngineChannel, itEvent); |
ProcessNoteOn((EngineChannel*)itEvent->pEngineChannel, itEvent); |
892 |
break; |
break; |
893 |
|
case Event::type_play_note: |
894 |
|
dmsg(5,("Engine: Play Note received\n")); |
895 |
|
ProcessNoteOn((EngineChannel*)itEvent->pEngineChannel, itEvent); |
896 |
|
break; |
897 |
case Event::type_note_off: |
case Event::type_note_off: |
898 |
dmsg(5,("Engine: Note off received\n")); |
dmsg(5,("Engine: Note off received\n")); |
899 |
ProcessNoteOff((EngineChannel*)itEvent->pEngineChannel, itEvent); |
ProcessNoteOff((EngineChannel*)itEvent->pEngineChannel, itEvent); |
900 |
break; |
break; |
901 |
|
case Event::type_stop_note: |
902 |
|
dmsg(5,("Engine: Stop Note received\n")); |
903 |
|
ProcessNoteOff((EngineChannel*)itEvent->pEngineChannel, itEvent); |
904 |
|
break; |
905 |
|
case Event::type_kill_note: |
906 |
|
dmsg(5,("Engine: Kill Note received\n")); |
907 |
|
ProcessKillNote((EngineChannel*)itEvent->pEngineChannel, itEvent); |
908 |
|
break; |
909 |
case Event::type_control_change: |
case Event::type_control_change: |
910 |
dmsg(5,("Engine: MIDI CC received\n")); |
dmsg(5,("Engine: MIDI CC received\n")); |
911 |
ProcessControlChange((EngineChannel*)itEvent->pEngineChannel, itEvent); |
ProcessControlChange((EngineChannel*)itEvent->pEngineChannel, itEvent); |
912 |
break; |
break; |
913 |
|
case Event::type_channel_pressure: |
914 |
|
dmsg(5,("Engine: MIDI Chan. Pressure received\n")); |
915 |
|
ProcessChannelPressure((EngineChannel*)itEvent->pEngineChannel, itEvent); |
916 |
|
break; |
917 |
|
case Event::type_note_pressure: |
918 |
|
dmsg(5,("Engine: MIDI Note Pressure received\n")); |
919 |
|
ProcessPolyphonicKeyPressure((EngineChannel*)itEvent->pEngineChannel, itEvent); |
920 |
|
break; |
921 |
case Event::type_pitchbend: |
case Event::type_pitchbend: |
922 |
dmsg(5,("Engine: Pitchbend received\n")); |
dmsg(5,("Engine: Pitchbend received\n")); |
923 |
ProcessPitchbend(static_cast<AbstractEngineChannel*>(itEvent->pEngineChannel), itEvent); |
ProcessPitchbend(static_cast<AbstractEngineChannel*>(itEvent->pEngineChannel), itEvent); |
924 |
break; |
break; |
925 |
|
case Event::type_note_synth_param: |
926 |
|
dmsg(5,("Engine: Note Synth Param received\n")); |
927 |
|
ProcessNoteSynthParam(itEvent->pEngineChannel, itEvent); |
928 |
|
break; |
929 |
|
case Event::type_sysex: |
930 |
|
break; // TODO ... |
931 |
|
|
932 |
|
case Event::type_cancel_release_key: |
933 |
|
case Event::type_release_key: |
934 |
|
case Event::type_release_note: |
935 |
|
break; // noop |
936 |
} |
} |
937 |
} |
} |
938 |
} |
} |
940 |
// reset voice stealing for the next engine channel (or next audio fragment) |
// reset voice stealing for the next engine channel (or next audio fragment) |
941 |
itLastStolenVoice = VoiceIterator(); |
itLastStolenVoice = VoiceIterator(); |
942 |
itLastStolenVoiceGlobally = VoiceIterator(); |
itLastStolenVoiceGlobally = VoiceIterator(); |
943 |
|
itLastStolenNote = NoteIterator(); |
944 |
|
itLastStolenNoteGlobally = NoteIterator(); |
945 |
iuiLastStolenKey = RTList<uint>::Iterator(); |
iuiLastStolenKey = RTList<uint>::Iterator(); |
946 |
iuiLastStolenKeyGlobally = RTList<uint>::Iterator(); |
iuiLastStolenKeyGlobally = RTList<uint>::Iterator(); |
947 |
pLastStolenChannel = NULL; |
pLastStolenChannel = NULL; |
948 |
} |
} |
949 |
|
|
950 |
/** |
/** |
951 |
|
* Run all suspended script execution instances which are scheduled |
952 |
|
* to be resumed for the current audio fragment cycle. |
953 |
|
* |
954 |
|
* @param pChannel - engine channel on which suspended events occurred |
955 |
|
*/ |
956 |
|
void ProcessSuspendedScriptEvents(AbstractEngineChannel* pChannel, const sched_time_t fragmentEndTime) { |
957 |
|
while (true) { |
958 |
|
RTList<ScriptEvent>::Iterator itEvent = |
959 |
|
pEventGenerator->popNextScheduledScriptEvent( |
960 |
|
pChannel->pScript->suspendedEvents, |
961 |
|
*pChannel->pScript->pEvents, fragmentEndTime |
962 |
|
); |
963 |
|
if (!itEvent) break; |
964 |
|
ResumeScriptEvent(pChannel, itEvent); |
965 |
|
} |
966 |
|
} |
967 |
|
|
968 |
|
/** @brief Call instrument script's event handler for this event. |
969 |
|
* |
970 |
|
* Causes a new execution instance of the currently loaded real-time |
971 |
|
* instrument script's event handler (callback) to be spawned for |
972 |
|
* the given MIDI event. |
973 |
|
* |
974 |
|
* @param pChannel - engine channel on which the MIDI event occurred |
975 |
|
* @param itEvent - MIDI event that causes this new script execution |
976 |
|
* @param pEventHandler - script's event handler to be executed |
977 |
|
*/ |
978 |
|
void ProcessEventByScript(AbstractEngineChannel* pChannel, RTList<Event>::Iterator& itEvent, VMEventHandler* pEventHandler) { |
979 |
|
const int key = itEvent->Param.Note.Key; // even if this is not a note on/off event, accessing it does not mean any harm |
980 |
|
// check if polyphonic data is passed from "note" to "release" |
981 |
|
// script event handlers |
982 |
|
if (pEventHandler == pChannel->pScript->handlerRelease && |
983 |
|
pChannel->pScript->handlerNote && |
984 |
|
pChannel->pScript->handlerNote->isPolyphonic() && |
985 |
|
pChannel->pScript->handlerRelease->isPolyphonic() && |
986 |
|
!pChannel->pScript->pKeyEvents[key]->isEmpty()) |
987 |
|
{ |
988 |
|
// polyphonic variable data is used/passed from "note" to |
989 |
|
// "release" script callback, so we have to recycle the |
990 |
|
// original "note on" script event(s) |
991 |
|
RTList<ScriptEvent>::Iterator it = pChannel->pScript->pKeyEvents[key]->first(); |
992 |
|
RTList<ScriptEvent>::Iterator end = pChannel->pScript->pKeyEvents[key]->end(); |
993 |
|
for (; it != end; ++it) { |
994 |
|
ProcessScriptEvent( |
995 |
|
pChannel, itEvent, pEventHandler, it |
996 |
|
); |
997 |
|
} |
998 |
|
} else { |
999 |
|
// no polyphonic data is used/passed from "note" to |
1000 |
|
// "release" script callback, so just use a new fresh |
1001 |
|
// script event object |
1002 |
|
RTList<ScriptEvent>::Iterator itScriptEvent = |
1003 |
|
pChannel->pScript->pEvents->allocAppend(); |
1004 |
|
// if event handler uses polyphonic variables, reset them |
1005 |
|
// to zero values before starting to execute the handler |
1006 |
|
if (pEventHandler->isPolyphonic()) |
1007 |
|
itScriptEvent->execCtx->resetPolyphonicData(); |
1008 |
|
ProcessScriptEvent( |
1009 |
|
pChannel, itEvent, pEventHandler, itScriptEvent |
1010 |
|
); |
1011 |
|
} |
1012 |
|
} |
1013 |
|
|
1014 |
|
/** @brief Spawn new execution instance of an instrument script handler. |
1015 |
|
* |
1016 |
|
* Will be called to initiate a new execution of a real-time |
1017 |
|
* instrument script event right from the start of the script's |
1018 |
|
* respective handler. If script execution did not complete after |
1019 |
|
* calling this method, the respective script exeuction is then |
1020 |
|
* suspended and a call to ResumeScriptEvent() will be used next |
1021 |
|
* time to continue its execution. |
1022 |
|
* |
1023 |
|
* @param pChannel - engine channel this script is running for |
1024 |
|
* @param itEvent - event which caused execution of this script |
1025 |
|
* event handler |
1026 |
|
* @param pEventHandler - VM representation of event handler to be |
1027 |
|
* executed |
1028 |
|
* @param itScriptEvent - script event that shall be processed |
1029 |
|
*/ |
1030 |
|
void ProcessScriptEvent(AbstractEngineChannel* pChannel, RTList<Event>::Iterator& itEvent, VMEventHandler* pEventHandler, RTList<ScriptEvent>::Iterator& itScriptEvent) { |
1031 |
|
if (!itScriptEvent) return; // not a valid script event (i.e. because no free script event was left in the script event pool) |
1032 |
|
|
1033 |
|
// fill the list of script handlers to be executed by this event |
1034 |
|
int i = 0; |
1035 |
|
itScriptEvent->handlers[i++] = pEventHandler; // actual event handler (i.e. note, controller) |
1036 |
|
itScriptEvent->handlers[i] = NULL; // NULL termination of list |
1037 |
|
|
1038 |
|
// initialize/reset other members |
1039 |
|
itScriptEvent->cause = *itEvent; |
1040 |
|
itScriptEvent->scheduleTime = itEvent->SchedTime(); |
1041 |
|
itScriptEvent->currentHandler = 0; |
1042 |
|
itScriptEvent->executionSlices = 0; |
1043 |
|
itScriptEvent->ignoreAllWaitCalls = false; |
1044 |
|
itScriptEvent->handlerType = pEventHandler->eventHandlerType(); |
1045 |
|
itScriptEvent->parentHandlerID = 0; |
1046 |
|
itScriptEvent->childHandlerID[0] = 0; |
1047 |
|
itScriptEvent->autoAbortByParent = false; |
1048 |
|
itScriptEvent->forkIndex = 0; |
1049 |
|
// this is the native representation of the $EVENT_ID script variable |
1050 |
|
itScriptEvent->id = |
1051 |
|
(itEvent->Type == Event::type_note_on) |
1052 |
|
? ScriptID::fromNoteID( itEvent->Param.Note.ID ) |
1053 |
|
: ScriptID::fromEventID( pEventPool->getID(itEvent) ); |
1054 |
|
|
1055 |
|
// run script handler(s) |
1056 |
|
VMExecStatus_t res = pScriptVM->exec( |
1057 |
|
pChannel->pScript->parserContext, &*itScriptEvent |
1058 |
|
); |
1059 |
|
|
1060 |
|
// was the script suspended? |
1061 |
|
if (res & VM_EXEC_SUSPENDED) { // script was suspended ... |
1062 |
|
// in case the script was suspended, keep it on the allocated |
1063 |
|
// ScriptEvent list to be resume at the scheduled time in future, |
1064 |
|
// additionally insert it into a sorted time queue |
1065 |
|
pEventGenerator->scheduleAheadMicroSec( |
1066 |
|
pChannel->pScript->suspendedEvents, // scheduler queue |
1067 |
|
*itScriptEvent, // script event |
1068 |
|
itScriptEvent->cause.FragmentPos(), // current time of script event (basis for its next execution) |
1069 |
|
itScriptEvent->execCtx->suspensionTimeMicroseconds() // how long shall it be suspended |
1070 |
|
); |
1071 |
|
} else { // script execution has finished without 'suspended' status ... |
1072 |
|
// if "polyphonic" variable data is passed from script's |
1073 |
|
// "note" event handler to its "release" event handler, then |
1074 |
|
// the script event must be kept and recycled for the later |
1075 |
|
// occuring "release" script event ... |
1076 |
|
if (pEventHandler == pChannel->pScript->handlerNote && |
1077 |
|
pChannel->pScript->handlerRelease && |
1078 |
|
pChannel->pScript->handlerNote->isPolyphonic() && |
1079 |
|
pChannel->pScript->handlerRelease->isPolyphonic()) |
1080 |
|
{ |
1081 |
|
const int key = itEvent->Param.Note.Key; |
1082 |
|
itScriptEvent.moveToEndOf(pChannel->pScript->pKeyEvents[key & 127]); |
1083 |
|
} else { |
1084 |
|
// ... otherwise if no polyphonic data is passed and |
1085 |
|
// script's execution has finished without suspension |
1086 |
|
// status, then free the script event for a new future |
1087 |
|
// script event to be triggered from start |
1088 |
|
pChannel->pScript->pEvents->free(itScriptEvent); |
1089 |
|
} |
1090 |
|
} |
1091 |
|
} |
1092 |
|
|
1093 |
|
/** @brief Resume execution of instrument script. |
1094 |
|
* |
1095 |
|
* Will be called to resume execution of a real-time instrument |
1096 |
|
* script event which has been suspended previously. |
1097 |
|
* |
1098 |
|
* Script execution might be suspended for various reasons. Usually |
1099 |
|
* a script will be suspended if the script called the built-in |
1100 |
|
* "wait()" function, but it might also be suspended automatically |
1101 |
|
* if the script took too much execution time in an audio fragment |
1102 |
|
* cycle. So in the latter case automatic suspension is performed in |
1103 |
|
* order to avoid harm for the sampler's overall real-time |
1104 |
|
* requirements. |
1105 |
|
* |
1106 |
|
* @param pChannel - engine channel this script is running for |
1107 |
|
* @param itScriptEvent - script execution that shall be resumed |
1108 |
|
*/ |
1109 |
|
void ResumeScriptEvent(AbstractEngineChannel* pChannel, RTList<ScriptEvent>::Iterator& itScriptEvent) { |
1110 |
|
VMEventHandler* handler = itScriptEvent->handlers[itScriptEvent->currentHandler]; |
1111 |
|
|
1112 |
|
// run script |
1113 |
|
VMExecStatus_t res = pScriptVM->exec( |
1114 |
|
pChannel->pScript->parserContext, &*itScriptEvent |
1115 |
|
); |
1116 |
|
|
1117 |
|
// was the script suspended? |
1118 |
|
if (res & VM_EXEC_SUSPENDED) { |
1119 |
|
// in case the script was suspended, keep it on the allocated |
1120 |
|
// ScriptEvent list to be resume at the scheduled time in future, |
1121 |
|
// additionally insert it into a sorted time queue |
1122 |
|
pEventGenerator->scheduleAheadMicroSec( |
1123 |
|
pChannel->pScript->suspendedEvents, // scheduler queue |
1124 |
|
*itScriptEvent, // script event |
1125 |
|
itScriptEvent->cause.FragmentPos(), // current time of script event (basis for its next execution) |
1126 |
|
itScriptEvent->execCtx->suspensionTimeMicroseconds() // how long shall it be suspended |
1127 |
|
); |
1128 |
|
} else { // script execution has finished without 'suspended' status ... |
1129 |
|
// if "polyphonic" variable data is passed from script's |
1130 |
|
// "note" event handler to its "release" event handler, then |
1131 |
|
// the script event must be kept and recycled for the later |
1132 |
|
// occuring "release" script event ... |
1133 |
|
if (handler && handler == pChannel->pScript->handlerNote && |
1134 |
|
pChannel->pScript->handlerRelease && |
1135 |
|
pChannel->pScript->handlerNote->isPolyphonic() && |
1136 |
|
pChannel->pScript->handlerRelease->isPolyphonic()) |
1137 |
|
{ |
1138 |
|
const int key = itScriptEvent->cause.Param.Note.Key; |
1139 |
|
itScriptEvent.moveToEndOf(pChannel->pScript->pKeyEvents[key & 127]); |
1140 |
|
} else { |
1141 |
|
// ... otherwise if no polyphonic data is passed and |
1142 |
|
// script's execution has finished without suspension |
1143 |
|
// status, then free the script event for a new future |
1144 |
|
// script event to be triggered from start |
1145 |
|
pChannel->pScript->pEvents->free(itScriptEvent); |
1146 |
|
} |
1147 |
|
} |
1148 |
|
} |
1149 |
|
|
1150 |
|
/** |
1151 |
* Will be called by LaunchVoice() method in case there are no free |
* Will be called by LaunchVoice() method in case there are no free |
1152 |
* voices left. This method will select and kill one old voice for |
* voices left. This method will select and kill one old voice for |
1153 |
* voice stealing and postpone the note-on event until the selected |
* voice stealing and postpone the note-on event until the selected |
1154 |
* voice actually died. |
* voice actually died. |
1155 |
* |
* |
1156 |
* @param pEngineChannel - engine channel on which this event occured on |
* @param pEngineChannel - engine channel on which this event occurred on |
1157 |
* @param itNoteOnEvent - key, velocity and time stamp of the event |
* @param itNoteOnEvent - key, velocity and time stamp of the event |
1158 |
* @returns 0 on success, a value < 0 if no active voice could be picked for voice stealing |
* @returns 0 on success, a value < 0 if no active voice could be picked for voice stealing |
1159 |
*/ |
*/ |
1160 |
int StealVoice(EngineChannel* pEngineChannel, Pool<Event>::Iterator& itNoteOnEvent) { |
int StealVoice(EngineChannel* pEngineChannel, Pool<Event>::Iterator& itNoteOnEvent) { |
1161 |
|
dmsg(3,("StealVoice()\n")); |
1162 |
if (VoiceSpawnsLeft <= 0) { |
if (VoiceSpawnsLeft <= 0) { |
1163 |
dmsg(1,("Max. voice thefts per audio fragment reached (you may raise CONFIG_MAX_VOICES).\n")); |
dmsg(1,("Max. voice thefts per audio fragment reached (you may raise CONFIG_MAX_VOICES).\n")); |
1164 |
return -1; |
return -1; |
1166 |
|
|
1167 |
EngineChannelBase<V, R, I>* pEngineChn = static_cast<EngineChannelBase<V, R, I>*>(pEngineChannel); |
EngineChannelBase<V, R, I>* pEngineChn = static_cast<EngineChannelBase<V, R, I>*>(pEngineChannel); |
1168 |
|
|
1169 |
if (!pEventPool->poolIsEmpty()) { |
if (pEventPool->poolIsEmpty()) { |
1170 |
|
dmsg(1,("Event pool emtpy!\n")); |
1171 |
|
return -1; |
1172 |
|
} |
1173 |
|
|
1174 |
if(!pEngineChn->StealVoice(itNoteOnEvent, &itLastStolenVoice, &iuiLastStolenKey)) { |
if (!pEngineChn->StealVoice(itNoteOnEvent, &itLastStolenVoice, &itLastStolenNote, &iuiLastStolenKey)) { |
1175 |
--VoiceSpawnsLeft; |
--VoiceSpawnsLeft; |
1176 |
return 0; |
return 0; |
1177 |
} |
} |
1178 |
|
|
1179 |
// if we couldn't steal a voice from the same engine channel then |
// if we couldn't steal a voice from the same engine channel then |
1180 |
// steal oldest voice on the oldest key from any other engine channel |
// steal oldest voice on the oldest key from any other engine channel |
1181 |
// (the smaller engine channel number, the higher priority) |
// (the smaller engine channel number, the higher priority) |
1182 |
EngineChannelBase<V, R, I>* pSelectedChannel; |
EngineChannelBase<V, R, I>* pSelectedChannel; |
1183 |
int iChannelIndex; |
int iChannelIndex; |
1184 |
VoiceIterator itSelectedVoice; |
VoiceIterator itSelectedVoice; |
|
|
|
|
// select engine channel |
|
|
if (pLastStolenChannel) { |
|
|
pSelectedChannel = pLastStolenChannel; |
|
|
iChannelIndex = pSelectedChannel->iEngineIndexSelf; |
|
|
} else { // pick the engine channel followed by this engine channel |
|
|
iChannelIndex = (pEngineChn->iEngineIndexSelf + 1) % engineChannels.size(); |
|
|
pSelectedChannel = static_cast<EngineChannelBase<V, R, I>*>(engineChannels[iChannelIndex]); |
|
|
} |
|
|
|
|
|
// if we already stole in this fragment, try to proceed on same key |
|
|
if (this->itLastStolenVoiceGlobally) { |
|
|
itSelectedVoice = this->itLastStolenVoiceGlobally; |
|
|
do { |
|
|
++itSelectedVoice; |
|
|
} while (itSelectedVoice && !itSelectedVoice->IsStealable()); // proceed iterating if voice was created in this fragment cycle |
|
|
} |
|
1185 |
|
|
1186 |
#if CONFIG_DEVMODE |
#if CONFIG_DEVMODE |
1187 |
EngineChannel* pBegin = pSelectedChannel; // to detect endless loop |
EngineChannel* pBegin = NULL; // to detect endless loop |
1188 |
#endif // CONFIG_DEVMODE |
#endif |
1189 |
|
|
1190 |
// did we find a 'stealable' voice? |
// select engine channel |
1191 |
if (itSelectedVoice && itSelectedVoice->IsStealable()) { |
if (pLastStolenChannel) { |
1192 |
// remember which voice we stole, so we can simply proceed on next voice stealing |
pSelectedChannel = pLastStolenChannel; |
1193 |
this->itLastStolenVoiceGlobally = itSelectedVoice; |
iChannelIndex = pSelectedChannel->iEngineIndexSelf; |
1194 |
} else while (true) { // iterate through engine channels |
} else { // pick the engine channel followed by this engine channel |
1195 |
// get (next) oldest key |
iChannelIndex = (pEngineChn->iEngineIndexSelf + 1) % engineChannels.size(); |
1196 |
RTList<uint>::Iterator iuiSelectedKey = (this->iuiLastStolenKeyGlobally) ? ++this->iuiLastStolenKeyGlobally : pSelectedChannel->pActiveKeys->first(); |
pSelectedChannel = static_cast<EngineChannelBase<V, R, I>*>(engineChannels[iChannelIndex]); |
1197 |
this->iuiLastStolenKeyGlobally = RTList<uint>::Iterator(); // to prevent endless loop (see line above) |
} |
1198 |
while (iuiSelectedKey) { |
|
1199 |
MidiKey* pSelectedKey = &pSelectedChannel->pMIDIKeyInfo[*iuiSelectedKey]; |
// if we already stole in this fragment, try to proceed on same note |
1200 |
itSelectedVoice = pSelectedKey->pActiveVoices->first(); |
if (this->itLastStolenVoiceGlobally) { |
1201 |
|
itSelectedVoice = this->itLastStolenVoiceGlobally; |
1202 |
|
do { |
1203 |
|
++itSelectedVoice; |
1204 |
|
} while (itSelectedVoice && !itSelectedVoice->IsStealable()); // proceed iterating if voice was created in this fragment cycle |
1205 |
|
} |
1206 |
|
// did we find a 'stealable' voice? |
1207 |
|
if (itSelectedVoice && itSelectedVoice->IsStealable()) { |
1208 |
|
// remember which voice we stole, so we can simply proceed on next voice stealing |
1209 |
|
this->itLastStolenVoiceGlobally = itSelectedVoice; |
1210 |
|
// done |
1211 |
|
goto stealable_voice_found; |
1212 |
|
} |
1213 |
|
|
1214 |
|
// get (next) oldest note |
1215 |
|
if (this->itLastStolenNoteGlobally) { |
1216 |
|
for (NoteIterator itNote = ++this->itLastStolenNoteGlobally; |
1217 |
|
itNote; ++itNote) |
1218 |
|
{ |
1219 |
|
for (itSelectedVoice = itNote->pActiveVoices->first(); itSelectedVoice; ++itSelectedVoice) { |
1220 |
|
// proceed iterating if voice was created in this audio fragment cycle |
1221 |
|
if (itSelectedVoice->IsStealable()) { |
1222 |
|
// remember which voice of which note we stole, so we can simply proceed on next voice stealing |
1223 |
|
this->itLastStolenNoteGlobally = itNote; |
1224 |
|
this->itLastStolenVoiceGlobally = itSelectedVoice; |
1225 |
|
goto stealable_voice_found; // selection succeeded |
1226 |
|
} |
1227 |
|
} |
1228 |
|
} |
1229 |
|
} |
1230 |
|
|
1231 |
|
#if CONFIG_DEVMODE |
1232 |
|
pBegin = pSelectedChannel; // to detect endless loop |
1233 |
|
#endif // CONFIG_DEVMODE |
1234 |
|
|
1235 |
|
while (true) { // iterate through engine channels |
1236 |
|
// get (next) oldest key |
1237 |
|
RTList<uint>::Iterator iuiSelectedKey = (this->iuiLastStolenKeyGlobally) ? ++this->iuiLastStolenKeyGlobally : pSelectedChannel->pActiveKeys->first(); |
1238 |
|
this->iuiLastStolenKeyGlobally = RTList<uint>::Iterator(); // to prevent endless loop (see line above) |
1239 |
|
while (iuiSelectedKey) { |
1240 |
|
MidiKey* pSelectedKey = &pSelectedChannel->pMIDIKeyInfo[*iuiSelectedKey]; |
1241 |
|
|
1242 |
|
for (NoteIterator itNote = pSelectedKey->pActiveNotes->first(), |
1243 |
|
itNotesEnd = pSelectedKey->pActiveNotes->end(); |
1244 |
|
itNote != itNotesEnd; ++itNote) |
1245 |
|
{ |
1246 |
|
itSelectedVoice = itNote->pActiveVoices->first(); |
1247 |
// proceed iterating if voice was created in this fragment cycle |
// proceed iterating if voice was created in this fragment cycle |
1248 |
while (itSelectedVoice && !itSelectedVoice->IsStealable()) ++itSelectedVoice; |
while (itSelectedVoice && !itSelectedVoice->IsStealable()) ++itSelectedVoice; |
1249 |
// found a "stealable" voice ? |
// found a "stealable" voice ? |
1250 |
if (itSelectedVoice && itSelectedVoice->IsStealable()) { |
if (itSelectedVoice && itSelectedVoice->IsStealable()) { |
1251 |
// remember which voice on which key on which engine channel we stole, so we can simply proceed on next voice stealing |
// remember which voice of which note on which key on which engine channel we stole, so we can simply proceed on next voice stealing |
1252 |
this->iuiLastStolenKeyGlobally = iuiSelectedKey; |
this->iuiLastStolenKeyGlobally = iuiSelectedKey; |
1253 |
|
this->itLastStolenNoteGlobally = itNote; |
1254 |
this->itLastStolenVoiceGlobally = itSelectedVoice; |
this->itLastStolenVoiceGlobally = itSelectedVoice; |
1255 |
this->pLastStolenChannel = pSelectedChannel; |
this->pLastStolenChannel = pSelectedChannel; |
1256 |
goto stealable_voice_found; // selection succeeded |
goto stealable_voice_found; // selection succeeded |
1257 |
} |
} |
|
++iuiSelectedKey; // get next key on current engine channel |
|
|
} |
|
|
// get next engine channel |
|
|
iChannelIndex = (iChannelIndex + 1) % engineChannels.size(); |
|
|
pSelectedChannel = static_cast<EngineChannelBase<V, R, I>*>(engineChannels[iChannelIndex]); |
|
|
|
|
|
#if CONFIG_DEVMODE |
|
|
if (pSelectedChannel == pBegin) { |
|
|
dmsg(1,("FATAL ERROR: voice stealing endless loop!\n")); |
|
|
dmsg(1,("VoiceSpawnsLeft=%d.\n", VoiceSpawnsLeft)); |
|
|
dmsg(1,("Exiting.\n")); |
|
|
exit(-1); |
|
1258 |
} |
} |
1259 |
#endif // CONFIG_DEVMODE |
++iuiSelectedKey; // get next key on current engine channel |
1260 |
} |
} |
1261 |
|
// get next engine channel |
1262 |
// jump point if a 'stealable' voice was found |
iChannelIndex = (iChannelIndex + 1) % engineChannels.size(); |
1263 |
stealable_voice_found: |
pSelectedChannel = static_cast<EngineChannelBase<V, R, I>*>(engineChannels[iChannelIndex]); |
1264 |
|
|
1265 |
#if CONFIG_DEVMODE |
#if CONFIG_DEVMODE |
1266 |
if (!itSelectedVoice->IsActive()) { |
if (pSelectedChannel == pBegin) { |
1267 |
dmsg(1,("EngineBase: ERROR, tried to steal a voice which was not active !!!\n")); |
dmsg(1,("FATAL ERROR: voice stealing endless loop!\n")); |
1268 |
return -1; |
dmsg(1,("VoiceSpawnsLeft=%d.\n", VoiceSpawnsLeft)); |
1269 |
|
dmsg(1,("Exiting.\n")); |
1270 |
|
exit(-1); |
1271 |
} |
} |
1272 |
#endif // CONFIG_DEVMODE |
#endif // CONFIG_DEVMODE |
1273 |
|
} |
1274 |
|
|
1275 |
// now kill the selected voice |
// jump point if a 'stealable' voice was found |
1276 |
itSelectedVoice->Kill(itNoteOnEvent); |
stealable_voice_found: |
|
|
|
|
--VoiceSpawnsLeft; |
|
1277 |
|
|
1278 |
return 0; // success |
#if CONFIG_DEVMODE |
1279 |
} |
if (!itSelectedVoice->IsActive()) { |
1280 |
else { |
dmsg(1,("EngineBase: ERROR, tried to steal a voice which was not active !!!\n")); |
|
dmsg(1,("Event pool emtpy!\n")); |
|
1281 |
return -1; |
return -1; |
1282 |
} |
} |
1283 |
|
#endif // CONFIG_DEVMODE |
1284 |
|
|
1285 |
|
// now kill the selected voice |
1286 |
|
itSelectedVoice->Kill(itNoteOnEvent); |
1287 |
|
|
1288 |
|
--VoiceSpawnsLeft; |
1289 |
|
|
1290 |
|
return 0; // success |
1291 |
} |
} |
1292 |
|
|
1293 |
void HandleInstrumentChanges() { |
void HandleInstrumentChanges() { |
1309 |
dmsg(5,("Engine: instrument change command received\n")); |
dmsg(5,("Engine: instrument change command received\n")); |
1310 |
cmd.bChangeInstrument = false; |
cmd.bChangeInstrument = false; |
1311 |
pEngineChannel->pInstrument = cmd.pInstrument; |
pEngineChannel->pInstrument = cmd.pInstrument; |
1312 |
|
pEngineChannel->pScript = |
1313 |
|
cmd.pScript->bHasValidScript ? cmd.pScript : NULL; |
1314 |
instrumentChanged = true; |
instrumentChanged = true; |
1315 |
|
|
1316 |
pEngineChannel->MarkAllActiveVoicesAsOrphans(); |
pEngineChannel->MarkAllActiveVoicesAsOrphans(); |
1317 |
|
|
1318 |
|
// the script's "init" event handler is only executed |
1319 |
|
// once (when the script is loaded or reloaded) |
1320 |
|
if (pEngineChannel->pScript && pEngineChannel->pScript->handlerInit) { |
1321 |
|
dmsg(5,("Engine: exec handlerInit %p\n", pEngineChannel->pScript->handlerInit)); |
1322 |
|
RTList<ScriptEvent>::Iterator itScriptEvent = |
1323 |
|
pEngineChannel->pScript->pEvents->allocAppend(); |
1324 |
|
|
1325 |
|
itScriptEvent->cause = pEventGenerator->CreateEvent(0); |
1326 |
|
itScriptEvent->cause.Type = (Event::type_t) -1; // some invalid type to avoid random event processing |
1327 |
|
itScriptEvent->cause.pEngineChannel = pEngineChannel; |
1328 |
|
itScriptEvent->cause.pMidiInputPort = pEngineChannel->GetMidiInputPort(); |
1329 |
|
itScriptEvent->id = 0; |
1330 |
|
itScriptEvent->handlers[0] = pEngineChannel->pScript->handlerInit; |
1331 |
|
itScriptEvent->handlers[1] = NULL; |
1332 |
|
itScriptEvent->currentHandler = 0; |
1333 |
|
itScriptEvent->executionSlices = 0; |
1334 |
|
itScriptEvent->ignoreAllWaitCalls = false; |
1335 |
|
itScriptEvent->handlerType = VM_EVENT_HANDLER_INIT; |
1336 |
|
itScriptEvent->parentHandlerID = 0; |
1337 |
|
itScriptEvent->childHandlerID[0] = 0; |
1338 |
|
itScriptEvent->autoAbortByParent = false; |
1339 |
|
itScriptEvent->forkIndex = 0; |
1340 |
|
|
1341 |
|
VMExecStatus_t res; |
1342 |
|
size_t instructionsCount = 0; |
1343 |
|
const size_t maxInstructions = 200000; // aiming approx. 1 second max. (based on very roughly 5us / instruction) |
1344 |
|
bool bWarningShown = false; |
1345 |
|
do { |
1346 |
|
res = pScriptVM->exec( |
1347 |
|
pEngineChannel->pScript->parserContext, &*itScriptEvent |
1348 |
|
); |
1349 |
|
instructionsCount += itScriptEvent->execCtx->instructionsPerformed(); |
1350 |
|
if (instructionsCount > maxInstructions && !bWarningShown) { |
1351 |
|
bWarningShown = true; |
1352 |
|
dmsg(0,("[ScriptVM] WARNING: \"init\" event handler of instrument script executing for long time!\n")); |
1353 |
|
} |
1354 |
|
} while (res & VM_EXEC_SUSPENDED && !(res & VM_EXEC_ERROR)); |
1355 |
|
|
1356 |
|
pEngineChannel->pScript->pEvents->free(itScriptEvent); |
1357 |
|
} |
1358 |
} |
} |
1359 |
} |
} |
1360 |
|
|
1362 |
//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 |
//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 |
1363 |
ResetSuspendedRegions(); |
ResetSuspendedRegions(); |
1364 |
} |
} |
|
|
|
|
for (int i = 0; i < engineChannels.size(); i++) { |
|
|
EngineChannelBase<V, R, I>* channel = |
|
|
static_cast<EngineChannelBase<V, R, I>*>(engineChannels[i]); |
|
|
channel->InstrumentChangeCommandReader.Unlock(); |
|
|
} |
|
1365 |
} |
} |
1366 |
|
|
1367 |
/** |
/** |
1405 |
EngineChannelBase<V, R, I>* pEngineChannel = |
EngineChannelBase<V, R, I>* pEngineChannel = |
1406 |
static_cast<EngineChannelBase<V, R, I>*>(itVoiceStealEvent->pEngineChannel);; |
static_cast<EngineChannelBase<V, R, I>*>(itVoiceStealEvent->pEngineChannel);; |
1407 |
if (!pEngineChannel->pInstrument) continue; // ignore if no instrument loaded |
if (!pEngineChannel->pInstrument) continue; // ignore if no instrument loaded |
1408 |
|
|
1409 |
PoolVoiceIterator itNewVoice = |
PoolVoiceIterator itNewVoice = |
1410 |
LaunchVoice(pEngineChannel, itVoiceStealEvent, itVoiceStealEvent->Param.Note.Layer, itVoiceStealEvent->Param.Note.ReleaseTrigger, false, false); |
LaunchVoice(pEngineChannel, itVoiceStealEvent, itVoiceStealEvent->Param.Note.Layer, itVoiceStealEvent->Param.Note.ReleaseTrigger, false, false); |
1411 |
if (itNewVoice) { |
if (itNewVoice) { |
1412 |
|
// usually there should already be a new Note object |
1413 |
|
NoteIterator itNote = GetNotePool()->fromID(itVoiceStealEvent->Param.Note.ID); |
1414 |
|
if (!itNote) { // should not happen, but just to be sure ... |
1415 |
|
dmsg(2,("Engine: No Note object for stolen voice!\n")); |
1416 |
|
const note_id_t noteID = LaunchNewNote(pEngineChannel, itVoiceStealEvent); |
1417 |
|
if (!noteID) { |
1418 |
|
dmsg(1,("Engine: Voice stealing failed; No Note object and Note pool empty!\n")); |
1419 |
|
continue; |
1420 |
|
} |
1421 |
|
itNote = GetNotePool()->fromID(noteID); |
1422 |
|
} |
1423 |
|
// move voice from whereever it was, to the new note's list of active voices |
1424 |
|
itNewVoice = itNewVoice.moveToEndOf(itNote->pActiveVoices); |
1425 |
|
// render audio of this new voice for the first time |
1426 |
itNewVoice->Render(Samples); |
itNewVoice->Render(Samples); |
1427 |
if (itNewVoice->IsActive()) { // still active |
if (itNewVoice->IsActive()) { // still active |
1428 |
*(pEngineChannel->pRegionsInUse->allocAppend()) = itNewVoice->GetRegion(); |
*(pEngineChannel->pRegionsInUse->allocAppend()) = itNewVoice->GetRegion(); |
1457 |
void PostProcess(EngineChannel* pEngineChannel) { |
void PostProcess(EngineChannel* pEngineChannel) { |
1458 |
EngineChannelBase<V, R, I>* pChannel = |
EngineChannelBase<V, R, I>* pChannel = |
1459 |
static_cast<EngineChannelBase<V, R, I>*>(pEngineChannel); |
static_cast<EngineChannelBase<V, R, I>*>(pEngineChannel); |
1460 |
pChannel->FreeAllInactiveKyes(); |
pChannel->FreeAllInactiveKeys(); |
1461 |
|
|
1462 |
// empty the engine channel's own event lists |
// empty the engine channel's own event lists |
1463 |
pChannel->ClearEventLists(); |
// (only events of the current audio fragment cycle) |
1464 |
|
pChannel->ClearEventListsOfCurrentFragment(); |
1465 |
} |
} |
1466 |
|
|
1467 |
|
/** |
1468 |
|
* Process MIDI control change events with hard coded behavior, |
1469 |
|
* that is controllers whose behavior is defined independently |
1470 |
|
* of the actual sampler engine type and instrument. |
1471 |
|
* |
1472 |
|
* @param pEngineChannel - engine channel on which the MIDI CC event was received |
1473 |
|
* @param itControlChangeEvent - the actual MIDI CC event |
1474 |
|
*/ |
1475 |
void ProcessHardcodedControllers ( |
void ProcessHardcodedControllers ( |
1476 |
EngineChannel* pEngineChannel, |
EngineChannel* pEngineChannel, |
1477 |
Pool<Event>::Iterator& itControlChangeEvent |
Pool<Event>::Iterator& itControlChangeEvent |
1484 |
pChannel->PortamentoTime = (float) itControlChangeEvent->Param.CC.Value / 127.0f * (float) CONFIG_PORTAMENTO_TIME_MAX + (float) CONFIG_PORTAMENTO_TIME_MIN; |
pChannel->PortamentoTime = (float) itControlChangeEvent->Param.CC.Value / 127.0f * (float) CONFIG_PORTAMENTO_TIME_MAX + (float) CONFIG_PORTAMENTO_TIME_MIN; |
1485 |
break; |
break; |
1486 |
} |
} |
1487 |
case 6: { // data entry (currently only used for RPN controllers) |
case 6: { // data entry (currently only used for RPN and NRPN controllers) |
1488 |
if (pChannel->GetMidiRpnController() == 2) { // coarse tuning in half tones |
//dmsg(1,("DATA ENTRY %d\n", itControlChangeEvent->Param.CC.Value)); |
1489 |
int transpose = (int) itControlChangeEvent->Param.CC.Value - 64; |
if (pChannel->GetMidiRpnController() >= 0) { // RPN controller number was sent previously ... |
1490 |
// limit to +- two octaves for now |
dmsg(4,("Guess it's an RPN ...\n")); |
1491 |
transpose = RTMath::Min(transpose, 24); |
if (pChannel->GetMidiRpnController() == 2) { // coarse tuning in half tones |
1492 |
transpose = RTMath::Max(transpose, -24); |
int transpose = (int) itControlChangeEvent->Param.CC.Value - 64; |
1493 |
pChannel->GlobalTranspose = transpose; |
// limit to +- two octaves for now |
1494 |
// workaround, so we won't have hanging notes |
transpose = RTMath::Min(transpose, 24); |
1495 |
pChannel->ReleaseAllVoices(itControlChangeEvent); |
transpose = RTMath::Max(transpose, -24); |
1496 |
|
pChannel->GlobalTranspose = transpose; |
1497 |
|
// workaround, so we won't have hanging notes |
1498 |
|
pChannel->ReleaseAllVoices(itControlChangeEvent); |
1499 |
|
} |
1500 |
|
// to prevent other MIDI CC #6 messages to be misenterpreted as RPN controller data |
1501 |
|
pChannel->ResetMidiRpnController(); |
1502 |
|
} else if (pChannel->GetMidiNrpnController() >= 0) { // NRPN controller number was sent previously ... |
1503 |
|
dmsg(4,("Guess it's an NRPN ...\n")); |
1504 |
|
const int NrpnCtrlMSB = pChannel->GetMidiNrpnController() >> 8; |
1505 |
|
const int NrpnCtrlLSB = pChannel->GetMidiNrpnController() & 0xff; |
1506 |
|
dmsg(4,("NRPN MSB=%d LSB=%d Data=%d\n", NrpnCtrlMSB, NrpnCtrlLSB, itControlChangeEvent->Param.CC.Value)); |
1507 |
|
switch (NrpnCtrlMSB) { |
1508 |
|
case 0x1a: { // volume level of note (Roland GS NRPN) |
1509 |
|
const uint note = NrpnCtrlLSB; |
1510 |
|
const uint vol = itControlChangeEvent->Param.CC.Value; |
1511 |
|
dmsg(4,("Note Volume NRPN received (note=%d,vol=%d).\n", note, vol)); |
1512 |
|
if (note < 128 && vol < 128) |
1513 |
|
pChannel->pMIDIKeyInfo[note].Volume = VolumeCurve[vol]; |
1514 |
|
break; |
1515 |
|
} |
1516 |
|
case 0x1c: { // panpot of note (Roland GS NRPN) |
1517 |
|
const uint note = NrpnCtrlLSB; |
1518 |
|
const uint pan = itControlChangeEvent->Param.CC.Value; |
1519 |
|
dmsg(4,("Note Pan NRPN received (note=%d,pan=%d).\n", note, pan)); |
1520 |
|
if (note < 128 && pan < 128) { |
1521 |
|
pChannel->pMIDIKeyInfo[note].PanLeft = PanCurve[128 - pan]; |
1522 |
|
pChannel->pMIDIKeyInfo[note].PanRight = PanCurve[pan]; |
1523 |
|
} |
1524 |
|
break; |
1525 |
|
} |
1526 |
|
case 0x1d: { // reverb send of note (Roland GS NRPN) |
1527 |
|
const uint note = NrpnCtrlLSB; |
1528 |
|
const float reverb = float(itControlChangeEvent->Param.CC.Value) / 127.0f; |
1529 |
|
dmsg(4,("Note Reverb Send NRPN received (note=%d,send=%f).\n", note, reverb)); |
1530 |
|
if (note < 128) |
1531 |
|
pChannel->pMIDIKeyInfo[note].ReverbSend = reverb; |
1532 |
|
break; |
1533 |
|
} |
1534 |
|
case 0x1e: { // chorus send of note (Roland GS NRPN) |
1535 |
|
const uint note = NrpnCtrlLSB; |
1536 |
|
const float chorus = float(itControlChangeEvent->Param.CC.Value) / 127.0f; |
1537 |
|
dmsg(4,("Note Chorus Send NRPN received (note=%d,send=%f).\n", note, chorus)); |
1538 |
|
if (note < 128) |
1539 |
|
pChannel->pMIDIKeyInfo[note].ChorusSend = chorus; |
1540 |
|
break; |
1541 |
|
} |
1542 |
|
} |
1543 |
|
// to prevent other MIDI CC #6 messages to be misenterpreted as NRPN controller data |
1544 |
|
pChannel->ResetMidiNrpnController(); |
1545 |
} |
} |
|
// to avoid other MIDI CC #6 messages to be misenterpreted as RPN controller data |
|
|
pChannel->ResetMidiRpnController(); |
|
1546 |
break; |
break; |
1547 |
} |
} |
1548 |
case 7: { // volume |
case 7: { // volume |
1553 |
} |
} |
1554 |
case 10: { // panpot |
case 10: { // panpot |
1555 |
//TODO: not sample accurate yet |
//TODO: not sample accurate yet |
|
pChannel->GlobalPanLeft = PanCurve[128 - itControlChangeEvent->Param.CC.Value]; |
|
|
pChannel->GlobalPanRight = PanCurve[itControlChangeEvent->Param.CC.Value]; |
|
1556 |
pChannel->iLastPanRequest = itControlChangeEvent->Param.CC.Value; |
pChannel->iLastPanRequest = itControlChangeEvent->Param.CC.Value; |
1557 |
break; |
break; |
1558 |
} |
} |
1629 |
} |
} |
1630 |
break; |
break; |
1631 |
} |
} |
1632 |
|
case 98: { // NRPN controller LSB |
1633 |
|
dmsg(4,("NRPN LSB %d\n", itControlChangeEvent->Param.CC.Value)); |
1634 |
|
pEngineChannel->SetMidiNrpnControllerLsb(itControlChangeEvent->Param.CC.Value); |
1635 |
|
break; |
1636 |
|
} |
1637 |
|
case 99: { // NRPN controller MSB |
1638 |
|
dmsg(4,("NRPN MSB %d\n", itControlChangeEvent->Param.CC.Value)); |
1639 |
|
pEngineChannel->SetMidiNrpnControllerMsb(itControlChangeEvent->Param.CC.Value); |
1640 |
|
break; |
1641 |
|
} |
1642 |
case 100: { // RPN controller LSB |
case 100: { // RPN controller LSB |
1643 |
|
dmsg(4,("RPN LSB %d\n", itControlChangeEvent->Param.CC.Value)); |
1644 |
pEngineChannel->SetMidiRpnControllerLsb(itControlChangeEvent->Param.CC.Value); |
pEngineChannel->SetMidiRpnControllerLsb(itControlChangeEvent->Param.CC.Value); |
1645 |
break; |
break; |
1646 |
} |
} |
1647 |
case 101: { // RPN controller MSB |
case 101: { // RPN controller MSB |
1648 |
|
dmsg(4,("RPN MSB %d\n", itControlChangeEvent->Param.CC.Value)); |
1649 |
pEngineChannel->SetMidiRpnControllerMsb(itControlChangeEvent->Param.CC.Value); |
pEngineChannel->SetMidiRpnControllerMsb(itControlChangeEvent->Param.CC.Value); |
1650 |
break; |
break; |
1651 |
} |
} |
1687 |
/** |
/** |
1688 |
* Assigns and triggers a new voice for the respective MIDI key. |
* Assigns and triggers a new voice for the respective MIDI key. |
1689 |
* |
* |
1690 |
* @param pEngineChannel - engine channel on which this event occured on |
* @param pEngineChannel - engine channel on which this event occurred on |
1691 |
* @param itNoteOnEvent - key, velocity and time stamp of the event |
* @param itNoteOnEvent - key, velocity and time stamp of the event |
1692 |
*/ |
*/ |
1693 |
virtual void ProcessNoteOn(EngineChannel* pEngineChannel, Pool<Event>::Iterator& itNoteOnEvent) { |
virtual void ProcessNoteOn(EngineChannel* pEngineChannel, Pool<Event>::Iterator& itNoteOnEvent) OVERRIDE { |
1694 |
EngineChannelBase<V, R, I>* pChannel = |
EngineChannelBase<V, R, I>* pChannel = |
1695 |
static_cast<EngineChannelBase<V, R, I>*>(pEngineChannel); |
static_cast<EngineChannelBase<V, R, I>*>(pEngineChannel); |
1696 |
|
|
|
//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 |
|
|
int k = itNoteOnEvent->Param.Note.Key + pChannel->GlobalTranspose; |
|
|
if (k < 0 || k > 127) return; //ignore keys outside the key range |
|
|
|
|
|
itNoteOnEvent->Param.Note.Key += pChannel->GlobalTranspose; |
|
|
int vel = itNoteOnEvent->Param.Note.Velocity; |
|
|
|
|
1697 |
const int key = itNoteOnEvent->Param.Note.Key; |
const int key = itNoteOnEvent->Param.Note.Key; |
1698 |
|
const int vel = itNoteOnEvent->Param.Note.Velocity; |
1699 |
|
if (key < 0 || key > 127) return; // ignore event, key outside allowed key range |
1700 |
|
|
1701 |
MidiKey* pKey = &pChannel->pMIDIKeyInfo[key]; |
MidiKey* pKey = &pChannel->pMIDIKeyInfo[key]; |
1702 |
|
|
1703 |
pChannel->listeners.PreProcessNoteOn(key, vel); |
// There are real MIDI note-on events (Event::type_note_on) and |
1704 |
|
// programmatically spawned notes (Event::type_play_note). We have |
1705 |
|
// to distinguish between them, since certain processing below |
1706 |
|
// must only be done on real MIDI note-on events (i.e. for |
1707 |
|
// correctly updating which MIDI keys are currently pressed down). |
1708 |
|
const bool isRealMIDINoteOnEvent = itNoteOnEvent->Type == Event::type_note_on; |
1709 |
|
|
1710 |
|
if (isRealMIDINoteOnEvent) |
1711 |
|
pChannel->listeners.PreProcessNoteOn(key, vel); |
1712 |
|
|
1713 |
#if !CONFIG_PROCESS_MUTED_CHANNELS |
#if !CONFIG_PROCESS_MUTED_CHANNELS |
1714 |
if (pEngineChannel->GetMute()) { // skip if sampler channel is muted |
if (pEngineChannel->GetMute()) { // skip if sampler channel is muted |
1715 |
pChannel->listeners.PostProcessNoteOn(key, vel); |
if (isRealMIDINoteOnEvent) |
1716 |
|
pChannel->listeners.PostProcessNoteOn(key, vel); |
1717 |
return; |
return; |
1718 |
} |
} |
1719 |
#endif |
#endif |
1720 |
|
|
1721 |
if (!pChannel->pInstrument) { |
if (!pChannel->pInstrument) { |
1722 |
pChannel->listeners.PostProcessNoteOn(key, vel); |
if (isRealMIDINoteOnEvent) |
1723 |
|
pChannel->listeners.PostProcessNoteOn(key, vel); |
1724 |
return; // ignore if no instrument loaded |
return; // ignore if no instrument loaded |
1725 |
} |
} |
1726 |
|
|
1728 |
RTList<Event>::Iterator itNoteOnEventOnKeyList = itNoteOnEvent.moveToEndOf(pKey->pEvents); |
RTList<Event>::Iterator itNoteOnEventOnKeyList = itNoteOnEvent.moveToEndOf(pKey->pEvents); |
1729 |
|
|
1730 |
// if Solo Mode then kill all already active voices |
// if Solo Mode then kill all already active voices |
1731 |
if (pChannel->SoloMode) { |
if (pChannel->SoloMode && isRealMIDINoteOnEvent) { |
1732 |
Pool<uint>::Iterator itYoungestKey = pChannel->pActiveKeys->last(); |
Pool<uint>::Iterator itYoungestKey = pChannel->pActiveKeys->last(); |
1733 |
if (itYoungestKey) { |
if (itYoungestKey) { |
1734 |
const int iYoungestKey = *itYoungestKey; |
const int iYoungestKey = *itYoungestKey; |
1736 |
if (pOtherKey->Active) { |
if (pOtherKey->Active) { |
1737 |
// get final portamento position of currently active voice |
// get final portamento position of currently active voice |
1738 |
if (pChannel->PortamentoMode) { |
if (pChannel->PortamentoMode) { |
1739 |
VoiceIterator itVoice = pOtherKey->pActiveVoices->last(); |
NoteIterator itNote = pOtherKey->pActiveNotes->last(); |
1740 |
if (itVoice) itVoice->UpdatePortamentoPos(itNoteOnEventOnKeyList); |
if (itNote) { |
1741 |
|
VoiceIterator itVoice = itNote->pActiveVoices->last(); |
1742 |
|
if (itVoice) itVoice->UpdatePortamentoPos(itNoteOnEventOnKeyList); |
1743 |
|
} |
1744 |
} |
} |
1745 |
// kill all voices on the (other) key |
// kill all voices on the (other) key |
1746 |
VoiceIterator itVoiceToBeKilled = pOtherKey->pActiveVoices->first(); |
for (NoteIterator itNote = pOtherKey->pActiveNotes->first(); itNote; ++itNote) { |
1747 |
VoiceIterator end = pOtherKey->pActiveVoices->end(); |
VoiceIterator itVoiceToBeKilled = itNote->pActiveVoices->first(); |
1748 |
for (; itVoiceToBeKilled != end; ++itVoiceToBeKilled) { |
VoiceIterator end = itNote->pActiveVoices->end(); |
1749 |
if (itVoiceToBeKilled->Type != Voice::type_release_trigger) |
for (; itVoiceToBeKilled != end; ++itVoiceToBeKilled) { |
1750 |
itVoiceToBeKilled->Kill(itNoteOnEventOnKeyList); |
if (!(itVoiceToBeKilled->Type & Voice::type_release_trigger)) |
1751 |
|
itVoiceToBeKilled->Kill(itNoteOnEventOnKeyList); |
1752 |
|
} |
1753 |
} |
} |
1754 |
} |
} |
1755 |
} |
} |
1757 |
pChannel->SoloKey = key; |
pChannel->SoloKey = key; |
1758 |
} |
} |
1759 |
|
|
1760 |
pChannel->ProcessKeySwitchChange(key); |
if (isRealMIDINoteOnEvent) { |
1761 |
|
pChannel->ProcessKeySwitchChange(key); |
1762 |
|
|
1763 |
pKey->KeyPressed = true; // the MIDI key was now pressed down |
pKey->KeyPressed = true; // the MIDI key was now pressed down |
1764 |
pKey->Velocity = itNoteOnEventOnKeyList->Param.Note.Velocity; |
pChannel->KeyDown[key] = true; // just used as built-in %KEY_DOWN script variable |
1765 |
pKey->NoteOnTime = FrameTime + itNoteOnEventOnKeyList->FragmentPos(); // will be used to calculate note length |
pKey->Velocity = itNoteOnEventOnKeyList->Param.Note.Velocity; |
1766 |
|
pKey->NoteOnTime = FrameTime + itNoteOnEventOnKeyList->FragmentPos(); // will be used to calculate note length |
1767 |
|
} |
1768 |
|
|
1769 |
// cancel release process of voices on this key if needed |
// cancel release process of voices on this key if needed |
1770 |
if (pKey->Active && !pChannel->SustainPedal) { |
if (pKey->Active && !pChannel->SustainPedal && isRealMIDINoteOnEvent) { |
1771 |
RTList<Event>::Iterator itCancelReleaseEvent = pKey->pEvents->allocAppend(); |
RTList<Event>::Iterator itCancelReleaseEvent = pKey->pEvents->allocAppend(); |
1772 |
if (itCancelReleaseEvent) { |
if (itCancelReleaseEvent) { |
1773 |
*itCancelReleaseEvent = *itNoteOnEventOnKeyList; // copy event |
*itCancelReleaseEvent = *itNoteOnEventOnKeyList; // copy event |
1774 |
itCancelReleaseEvent->Type = Event::type_cancel_release; // transform event type |
itCancelReleaseEvent->Type = Event::type_cancel_release_key; // transform event type |
1775 |
} |
} |
1776 |
else dmsg(1,("Event pool emtpy!\n")); |
else dmsg(1,("Event pool emtpy!\n")); |
1777 |
} |
} |
1782 |
if (!pKey->Active && !pKey->VoiceTheftsQueued) |
if (!pKey->Active && !pKey->VoiceTheftsQueued) |
1783 |
pKey->pEvents->free(itNoteOnEventOnKeyList); |
pKey->pEvents->free(itNoteOnEventOnKeyList); |
1784 |
|
|
1785 |
if (!pChannel->SoloMode || pChannel->PortamentoPos < 0.0f) pChannel->PortamentoPos = (float) key; |
if (isRealMIDINoteOnEvent && (!pChannel->SoloMode || pChannel->PortamentoPos < 0.0f)) |
1786 |
pKey->RoundRobinIndex++; |
pChannel->PortamentoPos = (float) key; |
1787 |
pChannel->listeners.PostProcessNoteOn(key, vel); |
|
1788 |
|
//NOTE: Hmm, I guess its a matter of taste whether round robin should be advanced only on real MIDI note-on events, isn't it? |
1789 |
|
if (pKey->pRoundRobinIndex) { |
1790 |
|
(*pKey->pRoundRobinIndex)++; // counter specific for the key or region |
1791 |
|
pChannel->RoundRobinIndex++; // common counter for the channel |
1792 |
|
} |
1793 |
|
|
1794 |
|
if (isRealMIDINoteOnEvent) |
1795 |
|
pChannel->listeners.PostProcessNoteOn(key, vel); |
1796 |
} |
} |
1797 |
|
|
1798 |
/** |
/** |
1818 |
* sustain pedal will be released or voice turned inactive by itself (e.g. |
* sustain pedal will be released or voice turned inactive by itself (e.g. |
1819 |
* due to completion of sample playback). |
* due to completion of sample playback). |
1820 |
* |
* |
1821 |
* @param pEngineChannel - engine channel on which this event occured on |
* @param pEngineChannel - engine channel on which this event occurred on |
1822 |
* @param itNoteOffEvent - key, velocity and time stamp of the event |
* @param itNoteOffEvent - key, velocity and time stamp of the event |
1823 |
*/ |
*/ |
1824 |
virtual void ProcessNoteOff(EngineChannel* pEngineChannel, Pool<Event>::Iterator& itNoteOffEvent) { |
virtual void ProcessNoteOff(EngineChannel* pEngineChannel, Pool<Event>::Iterator& itNoteOffEvent) OVERRIDE { |
1825 |
EngineChannelBase<V, R, I>* pChannel = static_cast<EngineChannelBase<V, R, I>*>(pEngineChannel); |
EngineChannelBase<V, R, I>* pChannel = static_cast<EngineChannelBase<V, R, I>*>(pEngineChannel); |
1826 |
|
|
|
int k = itNoteOffEvent->Param.Note.Key + pChannel->GlobalTranspose; |
|
|
if (k < 0 || k > 127) return; //ignore keys outside the key range |
|
|
|
|
|
//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 |
|
|
itNoteOffEvent->Param.Note.Key += pChannel->GlobalTranspose; |
|
|
int vel = itNoteOffEvent->Param.Note.Velocity; |
|
|
|
|
1827 |
const int iKey = itNoteOffEvent->Param.Note.Key; |
const int iKey = itNoteOffEvent->Param.Note.Key; |
1828 |
|
const int vel = itNoteOffEvent->Param.Note.Velocity; |
1829 |
|
if (iKey < 0 || iKey > 127) return; // ignore event, key outside allowed key range |
1830 |
|
|
1831 |
MidiKey* pKey = &pChannel->pMIDIKeyInfo[iKey]; |
MidiKey* pKey = &pChannel->pMIDIKeyInfo[iKey]; |
1832 |
|
|
1833 |
pChannel->listeners.PreProcessNoteOff(iKey, vel); |
// There are real MIDI note-off events (Event::type_note_off) and |
1834 |
|
// programmatically spawned notes (Event::type_stop_note). We have |
1835 |
|
// to distinguish between them, since certain processing below |
1836 |
|
// must only be done on real MIDI note-off events (i.e. for |
1837 |
|
// correctly updating which MIDI keys are currently pressed down), |
1838 |
|
// plus a stop-note event just releases voices of one particular |
1839 |
|
// note, whereas a note-off event releases all voices on a |
1840 |
|
// particular MIDI key instead. |
1841 |
|
const bool isRealMIDINoteOffEvent = itNoteOffEvent->Type == Event::type_note_off; |
1842 |
|
|
1843 |
|
if (isRealMIDINoteOffEvent) |
1844 |
|
pChannel->listeners.PreProcessNoteOff(iKey, vel); |
1845 |
|
|
1846 |
#if !CONFIG_PROCESS_MUTED_CHANNELS |
#if !CONFIG_PROCESS_MUTED_CHANNELS |
1847 |
if (pEngineChannel->GetMute()) { // skip if sampler channel is muted |
if (pEngineChannel->GetMute()) { // skip if sampler channel is muted |
1848 |
pChannel->listeners.PostProcessNoteOff(iKey, vel); |
if (isRealMIDINoteOffEvent) |
1849 |
|
pChannel->listeners.PostProcessNoteOff(iKey, vel); |
1850 |
return; |
return; |
1851 |
} |
} |
1852 |
#endif |
#endif |
1853 |
|
|
1854 |
pKey->KeyPressed = false; // the MIDI key was now released |
if (isRealMIDINoteOffEvent) { |
1855 |
|
pKey->KeyPressed = false; // the MIDI key was now released |
1856 |
|
pChannel->KeyDown[iKey] = false; // just used as built-in %KEY_DOWN script variable |
1857 |
|
} |
1858 |
|
|
1859 |
// move event to the key's own event list |
// move event to the key's own event list |
1860 |
RTList<Event>::Iterator itNoteOffEventOnKeyList = itNoteOffEvent.moveToEndOf(pKey->pEvents); |
RTList<Event>::Iterator itNoteOffEventOnKeyList = itNoteOffEvent.moveToEndOf(pKey->pEvents); |
1861 |
|
|
1862 |
bool bShouldRelease = pKey->Active && pChannel->ShouldReleaseVoice(itNoteOffEventOnKeyList->Param.Note.Key); |
if (isRealMIDINoteOffEvent) { |
1863 |
|
bool bShouldRelease = pKey->Active && pChannel->ShouldReleaseVoice(itNoteOffEventOnKeyList->Param.Note.Key); |
1864 |
|
|
1865 |
// in case Solo Mode is enabled, kill all voices on this key and respawn a voice on the highest pressed key (if any) |
// in case Solo Mode is enabled, kill all voices on this key and respawn a voice on the highest pressed key (if any) |
1866 |
if (pChannel->SoloMode && pChannel->pInstrument) { //TODO: this feels like too much code just for handling solo mode :P |
if (pChannel->SoloMode && pChannel->pInstrument) { //TODO: this feels like too much code just for handling solo mode :P |
1867 |
bool bOtherKeysPressed = false; |
bool bOtherKeysPressed = false; |
1868 |
if (iKey == pChannel->SoloKey) { |
if (iKey == pChannel->SoloKey) { |
1869 |
pChannel->SoloKey = -1; |
pChannel->SoloKey = -1; |
1870 |
// if there's still a key pressed down, respawn a voice (group) on the highest key |
// if there's still a key pressed down, respawn a voice (group) on the highest key |
1871 |
for (int i = 127; i > 0; i--) { |
for (int i = 127; i > 0; i--) { |
1872 |
MidiKey* pOtherKey = &pChannel->pMIDIKeyInfo[i]; |
MidiKey* pOtherKey = &pChannel->pMIDIKeyInfo[i]; |
1873 |
if (pOtherKey->KeyPressed) { |
if (pOtherKey->KeyPressed) { |
1874 |
bOtherKeysPressed = true; |
bOtherKeysPressed = true; |
1875 |
// make the other key the new 'currently active solo key' |
// make the other key the new 'currently active solo key' |
1876 |
pChannel->SoloKey = i; |
pChannel->SoloKey = i; |
1877 |
// get final portamento position of currently active voice |
// get final portamento position of currently active voice |
1878 |
if (pChannel->PortamentoMode) { |
if (pChannel->PortamentoMode) { |
1879 |
VoiceIterator itVoice = pKey->pActiveVoices->first(); |
NoteIterator itNote = pKey->pActiveNotes->first(); |
1880 |
if (itVoice) itVoice->UpdatePortamentoPos(itNoteOffEventOnKeyList); |
VoiceIterator itVoice = itNote->pActiveVoices->first(); |
1881 |
} |
if (itVoice) itVoice->UpdatePortamentoPos(itNoteOffEventOnKeyList); |
1882 |
// create a pseudo note on event |
} |
1883 |
RTList<Event>::Iterator itPseudoNoteOnEvent = pOtherKey->pEvents->allocAppend(); |
// create a pseudo note on event |
1884 |
if (itPseudoNoteOnEvent) { |
RTList<Event>::Iterator itPseudoNoteOnEvent = pOtherKey->pEvents->allocAppend(); |
1885 |
// copy event |
if (itPseudoNoteOnEvent) { |
1886 |
*itPseudoNoteOnEvent = *itNoteOffEventOnKeyList; |
// copy event |
1887 |
// transform event to a note on event |
*itPseudoNoteOnEvent = *itNoteOffEventOnKeyList; |
1888 |
itPseudoNoteOnEvent->Type = Event::type_note_on; |
// transform event to a note on event |
1889 |
itPseudoNoteOnEvent->Param.Note.Key = i; |
itPseudoNoteOnEvent->Type = Event::type_note_on; //FIXME: should probably use Event::type_play_note instead (to avoid i.e. hanging notes) |
1890 |
itPseudoNoteOnEvent->Param.Note.Velocity = pOtherKey->Velocity; |
itPseudoNoteOnEvent->Param.Note.Key = i; |
1891 |
// allocate and trigger new voice(s) for the other key |
itPseudoNoteOnEvent->Param.Note.Velocity = pOtherKey->Velocity; |
1892 |
TriggerNewVoices(pChannel, itPseudoNoteOnEvent, false); |
// assign a new note to this note-on event |
1893 |
// if neither a voice was spawned or postponed then remove note on event from key again |
if (LaunchNewNote(pChannel, itPseudoNoteOnEvent)) { |
1894 |
if (!pOtherKey->Active && !pOtherKey->VoiceTheftsQueued) |
// allocate and trigger new voice(s) for the other key |
1895 |
pOtherKey->pEvents->free(itPseudoNoteOnEvent); |
TriggerNewVoices(pChannel, itPseudoNoteOnEvent, false); |
1896 |
|
} |
1897 |
|
// if neither a voice was spawned or postponed then remove note on event from key again |
1898 |
|
if (!pOtherKey->Active && !pOtherKey->VoiceTheftsQueued) |
1899 |
|
pOtherKey->pEvents->free(itPseudoNoteOnEvent); |
1900 |
|
|
1901 |
} else dmsg(1,("Could not respawn voice, no free event left\n")); |
} else dmsg(1,("Could not respawn voice, no free event left\n")); |
1902 |
break; // done |
break; // done |
1903 |
|
} |
1904 |
} |
} |
1905 |
} |
} |
1906 |
} |
if (bOtherKeysPressed) { |
1907 |
if (bOtherKeysPressed) { |
if (pKey->Active) { // kill all voices on this key |
1908 |
if (pKey->Active) { // kill all voices on this key |
bShouldRelease = false; // no need to release, as we kill it here |
1909 |
bShouldRelease = false; // no need to release, as we kill it here |
for (NoteIterator itNote = pKey->pActiveNotes->first(); itNote; ++itNote) { |
1910 |
VoiceIterator itVoiceToBeKilled = pKey->pActiveVoices->first(); |
VoiceIterator itVoiceToBeKilled = itNote->pActiveVoices->first(); |
1911 |
VoiceIterator end = pKey->pActiveVoices->end(); |
VoiceIterator end = itNote->pActiveVoices->end(); |
1912 |
for (; itVoiceToBeKilled != end; ++itVoiceToBeKilled) { |
for (; itVoiceToBeKilled != end; ++itVoiceToBeKilled) { |
1913 |
if (itVoiceToBeKilled->Type != Voice::type_release_trigger) |
if (!(itVoiceToBeKilled->Type & Voice::type_release_trigger)) |
1914 |
itVoiceToBeKilled->Kill(itNoteOffEventOnKeyList); |
itVoiceToBeKilled->Kill(itNoteOffEventOnKeyList); |
1915 |
|
} |
1916 |
|
} |
1917 |
} |
} |
1918 |
} |
} else pChannel->PortamentoPos = -1.0f; |
1919 |
} else pChannel->PortamentoPos = -1.0f; |
} |
|
} |
|
|
|
|
|
// if no solo mode (the usual case) or if solo mode and no other key pressed, then release voices on this key if needed |
|
|
if (bShouldRelease) { |
|
|
itNoteOffEventOnKeyList->Type = Event::type_release; // transform event type |
|
1920 |
|
|
1921 |
// spawn release triggered voice(s) if needed |
// if no solo mode (the usual case) or if solo mode and no other key pressed, then release voices on this key if needed |
1922 |
if (pKey->ReleaseTrigger && pChannel->pInstrument) { |
if (bShouldRelease) { |
1923 |
TriggerReleaseVoices(pChannel, itNoteOffEventOnKeyList); |
itNoteOffEventOnKeyList->Type = Event::type_release_key; // transform event type |
1924 |
pKey->ReleaseTrigger = false; |
// spawn release triggered voice(s) if needed |
1925 |
|
if (pKey->ReleaseTrigger & release_trigger_noteoff) |
1926 |
|
ProcessReleaseTrigger(pChannel, itNoteOffEventOnKeyList, pKey); |
1927 |
|
} |
1928 |
|
} else if (itNoteOffEventOnKeyList->Type == Event::type_stop_note) { |
1929 |
|
// This programmatically caused event is caused by a call to |
1930 |
|
// the built-in instrument script function note_off(). In |
1931 |
|
// contrast to a real MIDI note-off event the stop-note |
1932 |
|
// event just intends to release voices of one particular note. |
1933 |
|
NoteBase* pNote = pChannel->pEngine->NoteByID( itNoteOffEventOnKeyList->Param.Note.ID ); |
1934 |
|
if (pNote) { // the requested note is still alive ... |
1935 |
|
itNoteOffEventOnKeyList->Type = Event::type_release_note; // transform event type |
1936 |
|
} else { // note is dead and gone .. |
1937 |
|
pKey->pEvents->free(itNoteOffEventOnKeyList); // remove stop-note event from key again |
1938 |
|
return; // prevent event to be removed a 2nd time below |
1939 |
} |
} |
1940 |
} |
} |
1941 |
|
|
1943 |
if (!pKey->Active && !pKey->VoiceTheftsQueued) |
if (!pKey->Active && !pKey->VoiceTheftsQueued) |
1944 |
pKey->pEvents->free(itNoteOffEventOnKeyList); |
pKey->pEvents->free(itNoteOffEventOnKeyList); |
1945 |
|
|
1946 |
pChannel->listeners.PostProcessNoteOff(iKey, vel); |
if (isRealMIDINoteOffEvent) |
1947 |
|
pChannel->listeners.PostProcessNoteOff(iKey, vel); |
1948 |
|
} |
1949 |
|
|
1950 |
|
/** |
1951 |
|
* Called on sustain pedal up events to check and if required, |
1952 |
|
* launch release trigger voices on the respective active key. |
1953 |
|
* |
1954 |
|
* @param pEngineChannel - engine channel on which this event occurred on |
1955 |
|
* @param itEvent - release trigger event (contains note number) |
1956 |
|
*/ |
1957 |
|
virtual void ProcessReleaseTriggerBySustain(EngineChannel* pEngineChannel, RTList<Event>::Iterator& itEvent) OVERRIDE { |
1958 |
|
EngineChannelBase<V, R, I>* pChannel = static_cast<EngineChannelBase<V, R, I>*>(pEngineChannel); |
1959 |
|
|
1960 |
|
const int iKey = itEvent->Param.Note.Key; |
1961 |
|
if (iKey < 0 || iKey > 127) return; // ignore event, key outside allowed key range |
1962 |
|
|
1963 |
|
MidiKey* pKey = &pChannel->pMIDIKeyInfo[iKey]; |
1964 |
|
|
1965 |
|
ProcessReleaseTrigger(pChannel, itEvent, pKey); |
1966 |
|
} |
1967 |
|
|
1968 |
|
/** |
1969 |
|
* Called on note-off and sustain pedal up events to check and if |
1970 |
|
* required, launch release trigger voices on the respective active |
1971 |
|
* key. |
1972 |
|
* |
1973 |
|
* @param pEngineChannel - engine channel on which this event occurred on |
1974 |
|
* @param itEvent - note off event / release trigger event |
1975 |
|
* @param pKey - key on which the release trigger voices shall be spawned |
1976 |
|
*/ |
1977 |
|
inline void ProcessReleaseTrigger(EngineChannelBase<V, R, I>* pChannel, RTList<Event>::Iterator& itEvent, MidiKey* pKey) { |
1978 |
|
// spawn release triggered voice(s) if needed |
1979 |
|
if (pKey->ReleaseTrigger && pChannel->pInstrument) { |
1980 |
|
// assign a new note to this release event |
1981 |
|
if (LaunchNewNote(pChannel, itEvent)) { |
1982 |
|
// allocate and trigger new release voice(s) |
1983 |
|
TriggerReleaseVoices(pChannel, itEvent); |
1984 |
|
} |
1985 |
|
pKey->ReleaseTrigger = release_trigger_none; |
1986 |
|
} |
1987 |
|
} |
1988 |
|
|
1989 |
|
/** |
1990 |
|
* Called on "kill note" events, which currently only happens on |
1991 |
|
* built-in real-time instrument script function fade_out(). This |
1992 |
|
* method only fulfills one task: moving the even to the Note's own |
1993 |
|
* event list so that its voices can process the kill event sample |
1994 |
|
* accurately. |
1995 |
|
*/ |
1996 |
|
void ProcessKillNote(EngineChannel* pEngineChannel, RTList<Event>::Iterator& itEvent) { |
1997 |
|
EngineChannelBase<V, R, I>* pChannel = static_cast<EngineChannelBase<V, R, I>*>(pEngineChannel); |
1998 |
|
|
1999 |
|
NoteBase* pNote = pChannel->pEngine->NoteByID( itEvent->Param.Note.ID ); |
2000 |
|
if (!pNote || pNote->hostKey < 0 || pNote->hostKey >= 128) return; |
2001 |
|
|
2002 |
|
// move note kill event to its MIDI key |
2003 |
|
MidiKey* pKey = &pChannel->pMIDIKeyInfo[pNote->hostKey]; |
2004 |
|
itEvent.moveToEndOf(pKey->pEvents); |
2005 |
|
} |
2006 |
|
|
2007 |
|
/** |
2008 |
|
* Called on note synthesis parameter change events. These are |
2009 |
|
* internal events caused by calling built-in real-time instrument |
2010 |
|
* script functions like change_vol(), change_tune(), etc. |
2011 |
|
* |
2012 |
|
* This method performs two tasks: |
2013 |
|
* |
2014 |
|
* - It converts the event's relative values changes (Deltas) to |
2015 |
|
* the respective final new synthesis parameter value (AbsValue), |
2016 |
|
* for that particular moment of the event that is. |
2017 |
|
* |
2018 |
|
* - It moves the individual events to the Note's own event list |
2019 |
|
* (or actually to the event list of the MIDI key), so that |
2020 |
|
* voices can process those events sample accurately. |
2021 |
|
* |
2022 |
|
* @param pEngineChannel - engine channel on which this event occurred on |
2023 |
|
* @param itEvent - note synthesis parameter change event |
2024 |
|
*/ |
2025 |
|
virtual void ProcessNoteSynthParam(EngineChannel* pEngineChannel, RTList<Event>::Iterator& itEvent) { |
2026 |
|
EngineChannelBase<V, R, I>* pChannel = static_cast<EngineChannelBase<V, R, I>*>(pEngineChannel); |
2027 |
|
|
2028 |
|
NoteBase* pNote = pChannel->pEngine->NoteByID( itEvent->Param.NoteSynthParam.NoteID ); |
2029 |
|
if (!pNote || pNote->hostKey < 0 || pNote->hostKey >= 128) return; |
2030 |
|
|
2031 |
|
const bool& relative = itEvent->Param.NoteSynthParam.Relative; |
2032 |
|
|
2033 |
|
switch (itEvent->Param.NoteSynthParam.Type) { |
2034 |
|
case Event::synth_param_volume: |
2035 |
|
if (relative) |
2036 |
|
pNote->Override.Volume *= itEvent->Param.NoteSynthParam.Delta; |
2037 |
|
else |
2038 |
|
pNote->Override.Volume = itEvent->Param.NoteSynthParam.Delta; |
2039 |
|
itEvent->Param.NoteSynthParam.AbsValue = pNote->Override.Volume; |
2040 |
|
break; |
2041 |
|
case Event::synth_param_volume_time: |
2042 |
|
pNote->Override.VolumeTime = itEvent->Param.NoteSynthParam.AbsValue = itEvent->Param.NoteSynthParam.Delta; |
2043 |
|
break; |
2044 |
|
case Event::synth_param_volume_curve: |
2045 |
|
itEvent->Param.NoteSynthParam.AbsValue = itEvent->Param.NoteSynthParam.Delta; |
2046 |
|
pNote->Override.VolumeCurve = (fade_curve_t) itEvent->Param.NoteSynthParam.AbsValue; |
2047 |
|
break; |
2048 |
|
case Event::synth_param_pitch: |
2049 |
|
if (relative) |
2050 |
|
pNote->Override.Pitch *= itEvent->Param.NoteSynthParam.Delta; |
2051 |
|
else |
2052 |
|
pNote->Override.Pitch = itEvent->Param.NoteSynthParam.Delta; |
2053 |
|
itEvent->Param.NoteSynthParam.AbsValue = pNote->Override.Pitch; |
2054 |
|
break; |
2055 |
|
case Event::synth_param_pitch_time: |
2056 |
|
pNote->Override.PitchTime = itEvent->Param.NoteSynthParam.AbsValue = itEvent->Param.NoteSynthParam.Delta; |
2057 |
|
break; |
2058 |
|
case Event::synth_param_pitch_curve: |
2059 |
|
itEvent->Param.NoteSynthParam.AbsValue = itEvent->Param.NoteSynthParam.Delta; |
2060 |
|
pNote->Override.PitchCurve = (fade_curve_t) itEvent->Param.NoteSynthParam.AbsValue; |
2061 |
|
break; |
2062 |
|
case Event::synth_param_pan: |
2063 |
|
if (relative) { |
2064 |
|
pNote->Override.Pan = RTMath::RelativeSummedAvg(pNote->Override.Pan, itEvent->Param.NoteSynthParam.Delta, ++pNote->Override.PanSources); |
2065 |
|
} else { |
2066 |
|
pNote->Override.Pan = itEvent->Param.NoteSynthParam.Delta; |
2067 |
|
pNote->Override.PanSources = 1; // only relevant on subsequent change_pan() instrument script calls on same note with 'relative' argument being set |
2068 |
|
} |
2069 |
|
itEvent->Param.NoteSynthParam.AbsValue = pNote->Override.Pan; |
2070 |
|
break; |
2071 |
|
case Event::synth_param_pan_time: |
2072 |
|
pNote->Override.PanTime = itEvent->Param.NoteSynthParam.AbsValue = itEvent->Param.NoteSynthParam.Delta; |
2073 |
|
break; |
2074 |
|
case Event::synth_param_pan_curve: |
2075 |
|
itEvent->Param.NoteSynthParam.AbsValue = itEvent->Param.NoteSynthParam.Delta; |
2076 |
|
pNote->Override.PanCurve = (fade_curve_t) itEvent->Param.NoteSynthParam.AbsValue; |
2077 |
|
break; |
2078 |
|
case Event::synth_param_cutoff: |
2079 |
|
pNote->Override.Cutoff = itEvent->Param.NoteSynthParam.AbsValue = itEvent->Param.NoteSynthParam.Delta; |
2080 |
|
break; |
2081 |
|
case Event::synth_param_resonance: |
2082 |
|
pNote->Override.Resonance = itEvent->Param.NoteSynthParam.AbsValue = itEvent->Param.NoteSynthParam.Delta; |
2083 |
|
break; |
2084 |
|
case Event::synth_param_attack: |
2085 |
|
pNote->Override.Attack = itEvent->Param.NoteSynthParam.AbsValue = itEvent->Param.NoteSynthParam.Delta; |
2086 |
|
break; |
2087 |
|
case Event::synth_param_decay: |
2088 |
|
pNote->Override.Decay = itEvent->Param.NoteSynthParam.AbsValue = itEvent->Param.NoteSynthParam.Delta; |
2089 |
|
break; |
2090 |
|
case Event::synth_param_sustain: |
2091 |
|
pNote->Override.Sustain = itEvent->Param.NoteSynthParam.AbsValue = itEvent->Param.NoteSynthParam.Delta; |
2092 |
|
break; |
2093 |
|
case Event::synth_param_release: |
2094 |
|
pNote->Override.Release = itEvent->Param.NoteSynthParam.AbsValue = itEvent->Param.NoteSynthParam.Delta; |
2095 |
|
break; |
2096 |
|
|
2097 |
|
case Event::synth_param_cutoff_attack: |
2098 |
|
pNote->Override.CutoffAttack = itEvent->Param.NoteSynthParam.AbsValue = itEvent->Param.NoteSynthParam.Delta; |
2099 |
|
break; |
2100 |
|
case Event::synth_param_cutoff_decay: |
2101 |
|
pNote->Override.CutoffDecay = itEvent->Param.NoteSynthParam.AbsValue = itEvent->Param.NoteSynthParam.Delta; |
2102 |
|
break; |
2103 |
|
case Event::synth_param_cutoff_sustain: |
2104 |
|
pNote->Override.CutoffSustain = itEvent->Param.NoteSynthParam.AbsValue = itEvent->Param.NoteSynthParam.Delta; |
2105 |
|
break; |
2106 |
|
case Event::synth_param_cutoff_release: |
2107 |
|
pNote->Override.CutoffRelease = itEvent->Param.NoteSynthParam.AbsValue = itEvent->Param.NoteSynthParam.Delta; |
2108 |
|
break; |
2109 |
|
|
2110 |
|
case Event::synth_param_amp_lfo_depth: |
2111 |
|
pNote->Override.AmpLFODepth = itEvent->Param.NoteSynthParam.AbsValue = itEvent->Param.NoteSynthParam.Delta; |
2112 |
|
break; |
2113 |
|
case Event::synth_param_amp_lfo_freq: |
2114 |
|
pNote->Override.AmpLFOFreq = itEvent->Param.NoteSynthParam.AbsValue = itEvent->Param.NoteSynthParam.Delta; |
2115 |
|
break; |
2116 |
|
case Event::synth_param_cutoff_lfo_depth: |
2117 |
|
pNote->Override.CutoffLFODepth = itEvent->Param.NoteSynthParam.AbsValue = itEvent->Param.NoteSynthParam.Delta; |
2118 |
|
break; |
2119 |
|
case Event::synth_param_cutoff_lfo_freq: |
2120 |
|
pNote->Override.CutoffLFOFreq = itEvent->Param.NoteSynthParam.AbsValue = itEvent->Param.NoteSynthParam.Delta; |
2121 |
|
break; |
2122 |
|
case Event::synth_param_pitch_lfo_depth: |
2123 |
|
pNote->Override.PitchLFODepth = itEvent->Param.NoteSynthParam.AbsValue = itEvent->Param.NoteSynthParam.Delta; |
2124 |
|
break; |
2125 |
|
case Event::synth_param_pitch_lfo_freq: |
2126 |
|
pNote->Override.PitchLFOFreq = itEvent->Param.NoteSynthParam.AbsValue = itEvent->Param.NoteSynthParam.Delta; |
2127 |
|
break; |
2128 |
|
} |
2129 |
|
|
2130 |
|
// move note parameter event to its MIDI key |
2131 |
|
MidiKey* pKey = &pChannel->pMIDIKeyInfo[pNote->hostKey]; |
2132 |
|
itEvent.moveToEndOf(pKey->pEvents); |
2133 |
} |
} |
2134 |
|
|
2135 |
/** |
/** |
2136 |
* Reset all voices and disk thread and clear input event queue and all |
* Reset all voices and disk thread and clear input event queue and all |
2137 |
* control and status variables. This method is protected by a mutex. |
* control and status variables. This method is protected by a mutex. |
2138 |
*/ |
*/ |
2139 |
virtual void ResetInternal() { |
virtual void ResetInternal() OVERRIDE { |
2140 |
ResetInternalMutex.Lock(); |
LockGuard lock(ResetInternalMutex); |
2141 |
|
|
2142 |
// make sure that the engine does not get any sysex messages |
// make sure that the engine does not get any sysex messages |
2143 |
// while it's reseting |
// while it's reseting |
2149 |
pVoiceStealingQueue->clear(); |
pVoiceStealingQueue->clear(); |
2150 |
itLastStolenVoice = VoiceIterator(); |
itLastStolenVoice = VoiceIterator(); |
2151 |
itLastStolenVoiceGlobally = VoiceIterator(); |
itLastStolenVoiceGlobally = VoiceIterator(); |
2152 |
|
itLastStolenNote = NoteIterator(); |
2153 |
|
itLastStolenNoteGlobally = NoteIterator(); |
2154 |
iuiLastStolenKey = RTList<uint>::Iterator(); |
iuiLastStolenKey = RTList<uint>::Iterator(); |
2155 |
iuiLastStolenKeyGlobally = RTList<uint>::Iterator(); |
iuiLastStolenKeyGlobally = RTList<uint>::Iterator(); |
2156 |
pLastStolenChannel = NULL; |
pLastStolenChannel = NULL; |
2157 |
|
|
2158 |
|
// reset all notes |
2159 |
|
pNotePool->clear(); |
2160 |
|
for (NoteIterator itNote = pNotePool->allocAppend(); itNote; |
2161 |
|
itNote = pNotePool->allocAppend()) |
2162 |
|
{ |
2163 |
|
itNote->reset(); |
2164 |
|
} |
2165 |
|
pNotePool->clear(); |
2166 |
|
|
2167 |
// reset all voices |
// reset all voices |
2168 |
|
pVoicePool->clear(); |
2169 |
for (VoiceIterator iterVoice = pVoicePool->allocAppend(); iterVoice == pVoicePool->last(); iterVoice = pVoicePool->allocAppend()) { |
for (VoiceIterator iterVoice = pVoicePool->allocAppend(); iterVoice == pVoicePool->last(); iterVoice = pVoicePool->allocAppend()) { |
2170 |
iterVoice->Reset(); |
iterVoice->Reset(); |
2171 |
} |
} |
2172 |
pVoicePool->clear(); |
pVoicePool->clear(); |
2173 |
|
|
2174 |
|
// reset all engine channels |
2175 |
|
for (int i = 0; i < engineChannels.size(); i++) { |
2176 |
|
AbstractEngineChannel* pEngineChannel = |
2177 |
|
static_cast<AbstractEngineChannel*>(engineChannels[i]); |
2178 |
|
pEngineChannel->ResetInternal(false/*don't reset engine*/); |
2179 |
|
} |
2180 |
|
|
2181 |
// reset disk thread |
// reset disk thread |
2182 |
if (pDiskThread) pDiskThread->Reset(); |
if (pDiskThread) pDiskThread->Reset(); |
2183 |
|
|
2185 |
pEventQueue->init(); |
pEventQueue->init(); |
2186 |
pSysexBuffer->init(); |
pSysexBuffer->init(); |
2187 |
if (sysexDisabled) MidiInputPort::AddSysexListener(this); |
if (sysexDisabled) MidiInputPort::AddSysexListener(this); |
|
ResetInternalMutex.Unlock(); |
|
2188 |
} |
} |
2189 |
|
|
2190 |
/** |
/** |
2195 |
* @param pEngineChannel - engine channel on which all voices should be killed |
* @param pEngineChannel - engine channel on which all voices should be killed |
2196 |
* @param itKillEvent - event which caused this killing of all voices |
* @param itKillEvent - event which caused this killing of all voices |
2197 |
*/ |
*/ |
2198 |
virtual void KillAllVoices(EngineChannel* pEngineChannel, Pool<Event>::Iterator& itKillEvent) { |
virtual void KillAllVoices(EngineChannel* pEngineChannel, Pool<Event>::Iterator& itKillEvent) OVERRIDE { |
2199 |
EngineChannelBase<V, R, I>* pChannel = static_cast<EngineChannelBase<V, R, I>*>(pEngineChannel); |
EngineChannelBase<V, R, I>* pChannel = static_cast<EngineChannelBase<V, R, I>*>(pEngineChannel); |
2200 |
int count = pChannel->KillAllVoices(itKillEvent); |
int count = pChannel->KillAllVoices(itKillEvent); |
2201 |
VoiceSpawnsLeft -= count; //FIXME: just a temporary workaround, we should check the cause in StealVoice() instead |
VoiceSpawnsLeft -= count; //FIXME: just a temporary workaround, we should check the cause in StealVoice() instead |
2206 |
* called by the ProcessNoteOn() method and by the voices itself |
* called by the ProcessNoteOn() method and by the voices itself |
2207 |
* (e.g. to spawn further voices on the same key for layered sounds). |
* (e.g. to spawn further voices on the same key for layered sounds). |
2208 |
* |
* |
2209 |
* @param pEngineChannel - engine channel on which this event occured on |
* @param pEngineChannel - engine channel on which this event occurred on |
2210 |
* @param itNoteOnEvent - key, velocity and time stamp of the event |
* @param itNoteOnEvent - key, velocity and time stamp of the event |
2211 |
* @param iLayer - layer index for the new voice (optional - only |
* @param iLayer - layer index for the new voice (optional - only |
2212 |
* in case of layered sounds of course) |
* in case of layered sounds of course) |
2230 |
bool HandleKeyGroupConflicts |
bool HandleKeyGroupConflicts |
2231 |
) = 0; |
) = 0; |
2232 |
|
|
2233 |
|
virtual int GetMinFadeOutSamples() OVERRIDE { return MinFadeOutSamples; } |
2234 |
|
|
2235 |
|
int InitNewVoice ( |
2236 |
|
EngineChannelBase<V, R, I>* pChannel, |
2237 |
|
R* pRegion, |
2238 |
|
Pool<Event>::Iterator& itNoteOnEvent, |
2239 |
|
Voice::type_t VoiceType, |
2240 |
|
int iLayer, |
2241 |
|
int iKeyGroup, |
2242 |
|
bool ReleaseTriggerVoice, |
2243 |
|
bool VoiceStealing, |
2244 |
|
typename Pool<V>::Iterator& itNewVoice |
2245 |
|
) { |
2246 |
|
int key = itNoteOnEvent->Param.Note.Key; |
2247 |
|
typename MidiKeyboardManager<V>::MidiKey* pKey = &pChannel->pMIDIKeyInfo[key]; |
2248 |
|
if (itNewVoice) { |
2249 |
|
// launch the new voice |
2250 |
|
if (itNewVoice->Trigger(pChannel, itNoteOnEvent, pChannel->Pitch, pRegion, VoiceType, iKeyGroup) < 0) { |
2251 |
|
dmsg(4,("Voice not triggered\n")); |
2252 |
|
GetVoicePool()->free(itNewVoice); |
2253 |
|
} |
2254 |
|
else { // on success |
2255 |
|
--VoiceSpawnsLeft; |
2256 |
|
|
2257 |
|
// should actually be superfluous now, since this is |
2258 |
|
// already done in LaunchNewNote() |
2259 |
|
pChannel->markKeyAsActive(pKey); |
2260 |
|
|
2261 |
|
if (itNewVoice->Type & Voice::type_release_trigger_required) |
2262 |
|
pKey->ReleaseTrigger |= itNewVoice->GetReleaseTriggerFlags(); // mark key for the need of release triggered voice(s) |
2263 |
|
return 0; // success |
2264 |
|
} |
2265 |
|
} |
2266 |
|
else if (VoiceStealing) { |
2267 |
|
// try to steal one voice |
2268 |
|
int result = StealVoice(pChannel, itNoteOnEvent); |
2269 |
|
if (!result) { // voice stolen successfully |
2270 |
|
// put note-on event into voice-stealing queue, so it will be reprocessed after killed voice died |
2271 |
|
RTList<Event>::Iterator itStealEvent = pVoiceStealingQueue->allocAppend(); |
2272 |
|
if (itStealEvent) { |
2273 |
|
*itStealEvent = *itNoteOnEvent; // copy event |
2274 |
|
itStealEvent->Param.Note.Layer = iLayer; |
2275 |
|
itStealEvent->Param.Note.ReleaseTrigger = ReleaseTriggerVoice; |
2276 |
|
pKey->VoiceTheftsQueued++; |
2277 |
|
} |
2278 |
|
else dmsg(1,("Voice stealing queue full!\n")); |
2279 |
|
} |
2280 |
|
} |
2281 |
|
|
2282 |
|
return -1; |
2283 |
|
} |
2284 |
|
|
2285 |
|
/** |
2286 |
|
* Checks whether scale tuning setting has been changed since last |
2287 |
|
* time this method was called, if yes, it recalculates the pitch |
2288 |
|
* for all active voices. |
2289 |
|
*/ |
2290 |
|
void ProcessScaleTuningChange() { |
2291 |
|
const bool changed = ScaleTuningChanged.readAndReset(); |
2292 |
|
if (!changed) return; |
2293 |
|
|
2294 |
|
for (int i = 0; i < engineChannels.size(); i++) { |
2295 |
|
EngineChannelBase<V, R, I>* channel = |
2296 |
|
static_cast<EngineChannelBase<V, R, I>*>(engineChannels[i]); |
2297 |
|
channel->OnScaleTuningChanged(); |
2298 |
|
} |
2299 |
|
} |
2300 |
|
|
2301 |
private: |
private: |
2302 |
|
Pool< Note<V> >* pNotePool; |
2303 |
|
Pool<note_id_t> noteIDPool; |
2304 |
Pool<V>* pVoicePool; ///< Contains all voices that can be activated. |
Pool<V>* pVoicePool; ///< Contains all voices that can be activated. |
2305 |
Pool<RR*> SuspendedRegions; |
Pool<RR*> SuspendedRegions; |
2306 |
Mutex SuspendedRegionsMutex; |
Mutex SuspendedRegionsMutex; |