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-2008 Christian Schoenebeck * |
* Copyright (C) 2005-2008 Christian Schoenebeck * |
7 |
* Copyright (C) 2009-2012 Christian Schoenebeck and Grigor Iliev * |
* Copyright (C) 2009-2012 Christian Schoenebeck and Grigor Iliev * |
8 |
* Copyright (C) 2012-2016 Christian Schoenebeck and Andreas Persson * |
* 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 * |
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), noteIDPool(GLOBAL_MAX_NOTES) { |
EngineBase() : noteIDPool(GLOBAL_MAX_NOTES), SuspendedRegions(128) { |
62 |
pDiskThread = NULL; |
pDiskThread = NULL; |
63 |
pNotePool = new Pool< Note<V> >(GLOBAL_MAX_NOTES); |
pNotePool = new Pool< Note<V> >(GLOBAL_MAX_NOTES); |
64 |
pNotePool->setPoolElementIDsReservedBits(INSTR_SCRIPT_EVENT_ID_RESERVED_BITS); |
pNotePool->setPoolElementIDsReservedBits(INSTR_SCRIPT_EVENT_ID_RESERVED_BITS); |
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 |
} |
} |
379 |
} |
} |
380 |
pVoicePool->clear(); |
pVoicePool->clear(); |
381 |
|
|
382 |
// (re)create event generator |
// update event generator |
383 |
if (pEventGenerator) delete pEventGenerator; |
pEventGenerator->SetSampleRate(pAudioOut->SampleRate()); |
|
pEventGenerator = new EventGenerator(pAudioOut->SampleRate()); |
|
384 |
|
|
385 |
dmsg(1,("Starting disk thread...")); |
dmsg(1,("Starting disk thread...")); |
386 |
pDiskThread->StartThread(); |
pDiskThread->StartThread(); |
596 |
} |
} |
597 |
|
|
598 |
// implementation of abstract method derived from class 'LinuxSampler::RegionPools' |
// implementation of abstract method derived from class 'LinuxSampler::RegionPools' |
599 |
virtual Pool<R*>* GetRegionPool(int index) { |
virtual Pool<R*>* GetRegionPool(int index) OVERRIDE { |
600 |
if (index < 0 || index > 1) throw Exception("Index out of bounds"); |
if (index < 0 || index > 1) throw Exception("Index out of bounds"); |
601 |
return pRegionPool[index]; |
return pRegionPool[index]; |
602 |
} |
} |
604 |
// implementation of abstract methods derived from class 'LinuxSampler::NotePool' |
// implementation of abstract methods derived from class 'LinuxSampler::NotePool' |
605 |
virtual Pool<V>* GetVoicePool() OVERRIDE { return pVoicePool; } |
virtual Pool<V>* GetVoicePool() OVERRIDE { return pVoicePool; } |
606 |
virtual Pool< Note<V> >* GetNotePool() OVERRIDE { return pNotePool; } |
virtual Pool< Note<V> >* GetNotePool() OVERRIDE { return pNotePool; } |
607 |
virtual Pool<note_id_t>* GetNodeIDPool() OVERRIDE { return ¬eIDPool; } |
virtual Pool<note_id_t>* GetNoteIDPool() OVERRIDE { return ¬eIDPool; } |
608 |
|
|
609 |
D* GetDiskThread() { return pDiskThread; } |
D* GetDiskThread() { return pDiskThread; } |
610 |
|
|
675 |
* @param pNoteOnEvent - event which caused this |
* @param pNoteOnEvent - event which caused this |
676 |
* @returns new note's unique ID (or zero on error) |
* @returns new note's unique ID (or zero on error) |
677 |
*/ |
*/ |
678 |
note_id_t LaunchNewNote(LinuxSampler::EngineChannel* pEngineChannel, Event* pNoteOnEvent) OVERRIDE { |
note_id_t LaunchNewNote(LinuxSampler::EngineChannel* pEngineChannel, Pool<Event>::Iterator& itNoteOnEvent) OVERRIDE { |
679 |
EngineChannelBase<V, R, I>* pChannel = static_cast<EngineChannelBase<V, R, I>*>(pEngineChannel); |
EngineChannelBase<V, R, I>* pChannel = static_cast<EngineChannelBase<V, R, I>*>(pEngineChannel); |
680 |
Pool< Note<V> >* pNotePool = GetNotePool(); |
Pool< Note<V> >* pNotePool = GetNotePool(); |
681 |
|
|
689 |
NoteIterator itNewNote = pNotePool->allocAppend(); |
NoteIterator itNewNote = pNotePool->allocAppend(); |
690 |
const note_id_t newNoteID = pNotePool->getID(itNewNote); |
const note_id_t newNoteID = pNotePool->getID(itNewNote); |
691 |
|
|
692 |
|
// remember the engine's time when this note was triggered exactly |
693 |
|
itNewNote->triggerSchedTime = itNoteOnEvent->SchedTime(); |
694 |
|
|
695 |
// usually the new note (and its subsequent voices) will be |
// usually the new note (and its subsequent voices) will be |
696 |
// allocated on the key provided by the event's note number, |
// allocated on the key provided by the event's note number, |
697 |
// however if this new note is requested not to be a regular |
// however if this new note is requested not to be a regular |
698 |
// note, but rather a child note, then this new note will be |
// note, but rather a child note, then this new note will be |
699 |
// allocated on the parent note's key instead in order to |
// allocated on the parent note's key instead in order to |
700 |
// release the child note simultaniously with its parent note |
// release the child note simultaniously with its parent note |
701 |
itNewNote->hostKey = pNoteOnEvent->Param.Note.Key; |
itNewNote->hostKey = itNoteOnEvent->Param.Note.Key; |
702 |
|
|
703 |
// in case this new note was requested to be a child note, |
// in case this new note was requested to be a child note, |
704 |
// then retrieve its parent note and link them with each other |
// then retrieve its parent note and link them with each other |
705 |
const note_id_t parentNoteID = pNoteOnEvent->Param.Note.ParentNoteID; |
const note_id_t parentNoteID = itNoteOnEvent->Param.Note.ParentNoteID; |
706 |
if (parentNoteID) { |
if (parentNoteID) { |
707 |
NoteIterator itParentNote = pNotePool->fromID(parentNoteID); |
NoteIterator itParentNote = pNotePool->fromID(parentNoteID); |
708 |
if (itParentNote) { |
if (itParentNote) { |
730 |
dmsg(2,("Launched new note on host key %d\n", itNewNote->hostKey)); |
dmsg(2,("Launched new note on host key %d\n", itNewNote->hostKey)); |
731 |
|
|
732 |
// copy event which caused this note |
// copy event which caused this note |
733 |
itNewNote->cause = *pNoteOnEvent; |
itNewNote->cause = *itNoteOnEvent; |
734 |
itNewNote->eventID = pEventPool->getID(pNoteOnEvent); |
itNewNote->eventID = pEventPool->getID(itNoteOnEvent); |
735 |
|
if (!itNewNote->eventID) { |
736 |
|
dmsg(0,("Engine: No valid event ID resolved for note. This is a bug!!!\n")); |
737 |
|
} |
738 |
|
|
739 |
// move new note to its host key |
// move new note to its host key |
740 |
MidiKey* pKey = &pChannel->pMIDIKeyInfo[itNewNote->hostKey]; |
MidiKey* pKey = &pChannel->pMIDIKeyInfo[itNewNote->hostKey]; |
741 |
itNewNote.moveToEndOf(pKey->pActiveNotes); |
itNewNote.moveToEndOf(pKey->pActiveNotes); |
742 |
|
|
743 |
// assign unique note ID of this new note to the original note on event |
// assign unique note ID of this new note to the original note on event |
744 |
pNoteOnEvent->Param.Note.ID = newNoteID; |
itNoteOnEvent->Param.Note.ID = newNoteID; |
745 |
|
|
746 |
return newNoteID; // success |
return newNoteID; // success |
747 |
} |
} |
777 |
// |
// |
778 |
// 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 |
// 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 |
779 |
for (RTList<Event>::Iterator itEvent = pChannel->pEvents->first(), |
for (RTList<Event>::Iterator itEvent = pChannel->pEvents->first(), |
780 |
end = pChannel->pEvents->end(); itEvent != end; ++itEvent) |
end = pChannel->pEvents->end(); itEvent != end; ) |
781 |
{ |
{ |
782 |
|
//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). |
783 |
|
RTList<Event>::Iterator itNext = itEvent; |
784 |
|
++itNext; |
785 |
|
|
786 |
switch (itEvent->Type) { |
switch (itEvent->Type) { |
787 |
case Event::type_note_on: |
case Event::type_note_on: |
788 |
if (pChannel->pScript->handlerNote) |
if (pChannel->pScript->handlerNote) |
801 |
case Event::type_note_pressure: |
case Event::type_note_pressure: |
802 |
//TODO: ... |
//TODO: ... |
803 |
break; |
break; |
804 |
|
|
805 |
|
case Event::type_sysex: |
806 |
|
//TODO: ... |
807 |
|
break; |
808 |
|
|
809 |
|
case Event::type_cancel_release_key: |
810 |
|
case Event::type_release_key: |
811 |
|
case Event::type_release_note: |
812 |
|
case Event::type_play_note: |
813 |
|
case Event::type_stop_note: |
814 |
|
case Event::type_kill_note: |
815 |
|
case Event::type_note_synth_param: |
816 |
|
break; // noop |
817 |
} |
} |
818 |
|
|
819 |
|
// see HACK comment above |
820 |
|
itEvent = itNext; |
821 |
} |
} |
822 |
|
|
823 |
// this has to be run again, since the newly spawned scripts |
// this has to be run again, since the newly spawned scripts |
872 |
dmsg(5,("Engine: Note on received\n")); |
dmsg(5,("Engine: Note on received\n")); |
873 |
ProcessNoteOn((EngineChannel*)itEvent->pEngineChannel, itEvent); |
ProcessNoteOn((EngineChannel*)itEvent->pEngineChannel, itEvent); |
874 |
break; |
break; |
875 |
|
case Event::type_play_note: |
876 |
|
dmsg(5,("Engine: Play Note received\n")); |
877 |
|
ProcessNoteOn((EngineChannel*)itEvent->pEngineChannel, itEvent); |
878 |
|
break; |
879 |
case Event::type_note_off: |
case Event::type_note_off: |
880 |
dmsg(5,("Engine: Note off received\n")); |
dmsg(5,("Engine: Note off received\n")); |
881 |
ProcessNoteOff((EngineChannel*)itEvent->pEngineChannel, itEvent); |
ProcessNoteOff((EngineChannel*)itEvent->pEngineChannel, itEvent); |
882 |
break; |
break; |
883 |
|
case Event::type_stop_note: |
884 |
|
dmsg(5,("Engine: Stop Note received\n")); |
885 |
|
ProcessNoteOff((EngineChannel*)itEvent->pEngineChannel, itEvent); |
886 |
|
break; |
887 |
|
case Event::type_kill_note: |
888 |
|
dmsg(5,("Engine: Kill Note received\n")); |
889 |
|
ProcessKillNote((EngineChannel*)itEvent->pEngineChannel, itEvent); |
890 |
|
break; |
891 |
case Event::type_control_change: |
case Event::type_control_change: |
892 |
dmsg(5,("Engine: MIDI CC received\n")); |
dmsg(5,("Engine: MIDI CC received\n")); |
893 |
ProcessControlChange((EngineChannel*)itEvent->pEngineChannel, itEvent); |
ProcessControlChange((EngineChannel*)itEvent->pEngineChannel, itEvent); |
904 |
dmsg(5,("Engine: Pitchbend received\n")); |
dmsg(5,("Engine: Pitchbend received\n")); |
905 |
ProcessPitchbend(static_cast<AbstractEngineChannel*>(itEvent->pEngineChannel), itEvent); |
ProcessPitchbend(static_cast<AbstractEngineChannel*>(itEvent->pEngineChannel), itEvent); |
906 |
break; |
break; |
907 |
|
case Event::type_note_synth_param: |
908 |
|
dmsg(5,("Engine: Note Synth Param received\n")); |
909 |
|
ProcessNoteSynthParam(itEvent->pEngineChannel, itEvent); |
910 |
|
break; |
911 |
|
case Event::type_sysex: |
912 |
|
break; // TODO ... |
913 |
|
|
914 |
|
case Event::type_cancel_release_key: |
915 |
|
case Event::type_release_key: |
916 |
|
case Event::type_release_note: |
917 |
|
break; // noop |
918 |
} |
} |
919 |
} |
} |
920 |
} |
} |
983 |
// script event object |
// script event object |
984 |
RTList<ScriptEvent>::Iterator itScriptEvent = |
RTList<ScriptEvent>::Iterator itScriptEvent = |
985 |
pChannel->pScript->pEvents->allocAppend(); |
pChannel->pScript->pEvents->allocAppend(); |
986 |
|
// if event handler uses polyphonic variables, reset them |
987 |
|
// to zero values before starting to execute the handler |
988 |
|
if (pEventHandler->isPolyphonic()) |
989 |
|
itScriptEvent->execCtx->resetPolyphonicData(); |
990 |
ProcessScriptEvent( |
ProcessScriptEvent( |
991 |
pChannel, itEvent, pEventHandler, itScriptEvent |
pChannel, itEvent, pEventHandler, itScriptEvent |
992 |
); |
); |
1019 |
|
|
1020 |
// initialize/reset other members |
// initialize/reset other members |
1021 |
itScriptEvent->cause = *itEvent; |
itScriptEvent->cause = *itEvent; |
1022 |
|
itScriptEvent->scheduleTime = itEvent->SchedTime(); |
1023 |
itScriptEvent->currentHandler = 0; |
itScriptEvent->currentHandler = 0; |
1024 |
itScriptEvent->executionSlices = 0; |
itScriptEvent->executionSlices = 0; |
1025 |
|
itScriptEvent->ignoreAllWaitCalls = false; |
1026 |
|
itScriptEvent->handlerType = pEventHandler->eventHandlerType(); |
1027 |
|
itScriptEvent->parentHandlerID = 0; |
1028 |
|
itScriptEvent->childHandlerID[0] = 0; |
1029 |
|
itScriptEvent->autoAbortByParent = false; |
1030 |
|
itScriptEvent->forkIndex = 0; |
1031 |
// this is the native representation of the $EVENT_ID script variable |
// this is the native representation of the $EVENT_ID script variable |
1032 |
itScriptEvent->id = |
itScriptEvent->id = |
1033 |
(itEvent->Type == Event::type_note_on) |
(itEvent->Type == Event::type_note_on) |
1295 |
// the script's "init" event handler is only executed |
// the script's "init" event handler is only executed |
1296 |
// once (when the script is loaded or reloaded) |
// once (when the script is loaded or reloaded) |
1297 |
if (pEngineChannel->pScript && pEngineChannel->pScript->handlerInit) { |
if (pEngineChannel->pScript && pEngineChannel->pScript->handlerInit) { |
1298 |
|
dmsg(5,("Engine: exec handlerInit %p\n", pEngineChannel->pScript->handlerInit)); |
1299 |
RTList<ScriptEvent>::Iterator itScriptEvent = |
RTList<ScriptEvent>::Iterator itScriptEvent = |
1300 |
pEngineChannel->pScript->pEvents->allocAppend(); |
pEngineChannel->pScript->pEvents->allocAppend(); |
1301 |
|
|
1302 |
|
itScriptEvent->cause = pEventGenerator->CreateEvent(0); |
1303 |
|
itScriptEvent->cause.Type = (Event::type_t) -1; // some invalid type to avoid random event processing |
1304 |
itScriptEvent->cause.pEngineChannel = pEngineChannel; |
itScriptEvent->cause.pEngineChannel = pEngineChannel; |
1305 |
|
itScriptEvent->cause.pMidiInputPort = pEngineChannel->GetMidiInputPort(); |
1306 |
|
itScriptEvent->id = 0; |
1307 |
itScriptEvent->handlers[0] = pEngineChannel->pScript->handlerInit; |
itScriptEvent->handlers[0] = pEngineChannel->pScript->handlerInit; |
1308 |
itScriptEvent->handlers[1] = NULL; |
itScriptEvent->handlers[1] = NULL; |
1309 |
|
itScriptEvent->currentHandler = 0; |
1310 |
VMExecStatus_t res = pScriptVM->exec( |
itScriptEvent->executionSlices = 0; |
1311 |
pEngineChannel->pScript->parserContext, &*itScriptEvent |
itScriptEvent->ignoreAllWaitCalls = false; |
1312 |
); |
itScriptEvent->handlerType = VM_EVENT_HANDLER_INIT; |
1313 |
|
itScriptEvent->parentHandlerID = 0; |
1314 |
|
itScriptEvent->childHandlerID[0] = 0; |
1315 |
|
itScriptEvent->autoAbortByParent = false; |
1316 |
|
itScriptEvent->forkIndex = 0; |
1317 |
|
|
1318 |
|
VMExecStatus_t res; |
1319 |
|
size_t instructionsCount = 0; |
1320 |
|
const size_t maxInstructions = 200000; // aiming approx. 1 second max. (based on very roughly 5us / instruction) |
1321 |
|
bool bWarningShown = false; |
1322 |
|
do { |
1323 |
|
res = pScriptVM->exec( |
1324 |
|
pEngineChannel->pScript->parserContext, &*itScriptEvent |
1325 |
|
); |
1326 |
|
instructionsCount += itScriptEvent->execCtx->instructionsPerformed(); |
1327 |
|
if (instructionsCount > maxInstructions && !bWarningShown) { |
1328 |
|
bWarningShown = true; |
1329 |
|
dmsg(0,("[ScriptVM] WARNING: \"init\" event handler of instrument script executing for long time!\n")); |
1330 |
|
} |
1331 |
|
} while (res & VM_EXEC_SUSPENDED && !(res & VM_EXEC_ERROR)); |
1332 |
|
|
1333 |
pEngineChannel->pScript->pEvents->free(itScriptEvent); |
pEngineChannel->pScript->pEvents->free(itScriptEvent); |
1334 |
} |
} |
1389 |
// usually there should already be a new Note object |
// usually there should already be a new Note object |
1390 |
NoteIterator itNote = GetNotePool()->fromID(itVoiceStealEvent->Param.Note.ID); |
NoteIterator itNote = GetNotePool()->fromID(itVoiceStealEvent->Param.Note.ID); |
1391 |
if (!itNote) { // should not happen, but just to be sure ... |
if (!itNote) { // should not happen, but just to be sure ... |
1392 |
const note_id_t noteID = LaunchNewNote(pEngineChannel, &*itVoiceStealEvent); |
const note_id_t noteID = LaunchNewNote(pEngineChannel, itVoiceStealEvent); |
1393 |
if (!noteID) { |
if (!noteID) { |
1394 |
dmsg(1,("Engine: Voice stealing failed; No Note object and Note pool empty!\n")); |
dmsg(1,("Engine: Voice stealing failed; No Note object and Note pool empty!\n")); |
1395 |
continue; |
continue; |
1666 |
* @param pEngineChannel - engine channel on which this event occurred on |
* @param pEngineChannel - engine channel on which this event occurred on |
1667 |
* @param itNoteOnEvent - key, velocity and time stamp of the event |
* @param itNoteOnEvent - key, velocity and time stamp of the event |
1668 |
*/ |
*/ |
1669 |
virtual void ProcessNoteOn(EngineChannel* pEngineChannel, Pool<Event>::Iterator& itNoteOnEvent) { |
virtual void ProcessNoteOn(EngineChannel* pEngineChannel, Pool<Event>::Iterator& itNoteOnEvent) OVERRIDE { |
1670 |
EngineChannelBase<V, R, I>* pChannel = |
EngineChannelBase<V, R, I>* pChannel = |
1671 |
static_cast<EngineChannelBase<V, R, I>*>(pEngineChannel); |
static_cast<EngineChannelBase<V, R, I>*>(pEngineChannel); |
1672 |
|
|
1676 |
|
|
1677 |
MidiKey* pKey = &pChannel->pMIDIKeyInfo[key]; |
MidiKey* pKey = &pChannel->pMIDIKeyInfo[key]; |
1678 |
|
|
1679 |
pChannel->listeners.PreProcessNoteOn(key, vel); |
// There are real MIDI note-on events (Event::type_note_on) and |
1680 |
|
// programmatically spawned notes (Event::type_play_note). We have |
1681 |
|
// to distinguish between them, since certain processing below |
1682 |
|
// must only be done on real MIDI note-on events (i.e. for |
1683 |
|
// correctly updating which MIDI keys are currently pressed down). |
1684 |
|
const bool isRealMIDINoteOnEvent = itNoteOnEvent->Type == Event::type_note_on; |
1685 |
|
|
1686 |
|
if (isRealMIDINoteOnEvent) |
1687 |
|
pChannel->listeners.PreProcessNoteOn(key, vel); |
1688 |
|
|
1689 |
#if !CONFIG_PROCESS_MUTED_CHANNELS |
#if !CONFIG_PROCESS_MUTED_CHANNELS |
1690 |
if (pEngineChannel->GetMute()) { // skip if sampler channel is muted |
if (pEngineChannel->GetMute()) { // skip if sampler channel is muted |
1691 |
pChannel->listeners.PostProcessNoteOn(key, vel); |
if (isRealMIDINoteOnEvent) |
1692 |
|
pChannel->listeners.PostProcessNoteOn(key, vel); |
1693 |
return; |
return; |
1694 |
} |
} |
1695 |
#endif |
#endif |
1696 |
|
|
1697 |
if (!pChannel->pInstrument) { |
if (!pChannel->pInstrument) { |
1698 |
pChannel->listeners.PostProcessNoteOn(key, vel); |
if (isRealMIDINoteOnEvent) |
1699 |
|
pChannel->listeners.PostProcessNoteOn(key, vel); |
1700 |
return; // ignore if no instrument loaded |
return; // ignore if no instrument loaded |
1701 |
} |
} |
1702 |
|
|
1704 |
RTList<Event>::Iterator itNoteOnEventOnKeyList = itNoteOnEvent.moveToEndOf(pKey->pEvents); |
RTList<Event>::Iterator itNoteOnEventOnKeyList = itNoteOnEvent.moveToEndOf(pKey->pEvents); |
1705 |
|
|
1706 |
// if Solo Mode then kill all already active voices |
// if Solo Mode then kill all already active voices |
1707 |
if (pChannel->SoloMode) { |
if (pChannel->SoloMode && isRealMIDINoteOnEvent) { |
1708 |
Pool<uint>::Iterator itYoungestKey = pChannel->pActiveKeys->last(); |
Pool<uint>::Iterator itYoungestKey = pChannel->pActiveKeys->last(); |
1709 |
if (itYoungestKey) { |
if (itYoungestKey) { |
1710 |
const int iYoungestKey = *itYoungestKey; |
const int iYoungestKey = *itYoungestKey; |
1733 |
pChannel->SoloKey = key; |
pChannel->SoloKey = key; |
1734 |
} |
} |
1735 |
|
|
1736 |
pChannel->ProcessKeySwitchChange(key); |
if (isRealMIDINoteOnEvent) { |
1737 |
|
pChannel->ProcessKeySwitchChange(key); |
1738 |
|
|
1739 |
pKey->KeyPressed = true; // the MIDI key was now pressed down |
pKey->KeyPressed = true; // the MIDI key was now pressed down |
1740 |
pChannel->KeyDown[key] = true; // just used as built-in %KEY_DOWN script variable |
pChannel->KeyDown[key] = true; // just used as built-in %KEY_DOWN script variable |
1741 |
pKey->Velocity = itNoteOnEventOnKeyList->Param.Note.Velocity; |
pKey->Velocity = itNoteOnEventOnKeyList->Param.Note.Velocity; |
1742 |
pKey->NoteOnTime = FrameTime + itNoteOnEventOnKeyList->FragmentPos(); // will be used to calculate note length |
pKey->NoteOnTime = FrameTime + itNoteOnEventOnKeyList->FragmentPos(); // will be used to calculate note length |
1743 |
|
} |
1744 |
|
|
1745 |
// cancel release process of voices on this key if needed |
// cancel release process of voices on this key if needed |
1746 |
if (pKey->Active && !pChannel->SustainPedal) { |
if (pKey->Active && !pChannel->SustainPedal && isRealMIDINoteOnEvent) { |
1747 |
RTList<Event>::Iterator itCancelReleaseEvent = pKey->pEvents->allocAppend(); |
RTList<Event>::Iterator itCancelReleaseEvent = pKey->pEvents->allocAppend(); |
1748 |
if (itCancelReleaseEvent) { |
if (itCancelReleaseEvent) { |
1749 |
*itCancelReleaseEvent = *itNoteOnEventOnKeyList; // copy event |
*itCancelReleaseEvent = *itNoteOnEventOnKeyList; // copy event |
1750 |
itCancelReleaseEvent->Type = Event::type_cancel_release; // transform event type |
itCancelReleaseEvent->Type = Event::type_cancel_release_key; // transform event type |
1751 |
} |
} |
1752 |
else dmsg(1,("Event pool emtpy!\n")); |
else dmsg(1,("Event pool emtpy!\n")); |
1753 |
} |
} |
1758 |
if (!pKey->Active && !pKey->VoiceTheftsQueued) |
if (!pKey->Active && !pKey->VoiceTheftsQueued) |
1759 |
pKey->pEvents->free(itNoteOnEventOnKeyList); |
pKey->pEvents->free(itNoteOnEventOnKeyList); |
1760 |
|
|
1761 |
if (!pChannel->SoloMode || pChannel->PortamentoPos < 0.0f) pChannel->PortamentoPos = (float) key; |
if (isRealMIDINoteOnEvent && (!pChannel->SoloMode || pChannel->PortamentoPos < 0.0f)) |
1762 |
|
pChannel->PortamentoPos = (float) key; |
1763 |
|
|
1764 |
|
//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? |
1765 |
if (pKey->pRoundRobinIndex) { |
if (pKey->pRoundRobinIndex) { |
1766 |
(*pKey->pRoundRobinIndex)++; // counter specific for the key or region |
(*pKey->pRoundRobinIndex)++; // counter specific for the key or region |
1767 |
pChannel->RoundRobinIndex++; // common counter for the channel |
pChannel->RoundRobinIndex++; // common counter for the channel |
1768 |
} |
} |
1769 |
pChannel->listeners.PostProcessNoteOn(key, vel); |
|
1770 |
|
if (isRealMIDINoteOnEvent) |
1771 |
|
pChannel->listeners.PostProcessNoteOn(key, vel); |
1772 |
} |
} |
1773 |
|
|
1774 |
/** |
/** |
1797 |
* @param pEngineChannel - engine channel on which this event occurred on |
* @param pEngineChannel - engine channel on which this event occurred on |
1798 |
* @param itNoteOffEvent - key, velocity and time stamp of the event |
* @param itNoteOffEvent - key, velocity and time stamp of the event |
1799 |
*/ |
*/ |
1800 |
virtual void ProcessNoteOff(EngineChannel* pEngineChannel, Pool<Event>::Iterator& itNoteOffEvent) { |
virtual void ProcessNoteOff(EngineChannel* pEngineChannel, Pool<Event>::Iterator& itNoteOffEvent) OVERRIDE { |
1801 |
EngineChannelBase<V, R, I>* pChannel = static_cast<EngineChannelBase<V, R, I>*>(pEngineChannel); |
EngineChannelBase<V, R, I>* pChannel = static_cast<EngineChannelBase<V, R, I>*>(pEngineChannel); |
1802 |
|
|
1803 |
const int iKey = itNoteOffEvent->Param.Note.Key; |
const int iKey = itNoteOffEvent->Param.Note.Key; |
1806 |
|
|
1807 |
MidiKey* pKey = &pChannel->pMIDIKeyInfo[iKey]; |
MidiKey* pKey = &pChannel->pMIDIKeyInfo[iKey]; |
1808 |
|
|
1809 |
pChannel->listeners.PreProcessNoteOff(iKey, vel); |
// There are real MIDI note-off events (Event::type_note_off) and |
1810 |
|
// programmatically spawned notes (Event::type_stop_note). We have |
1811 |
|
// to distinguish between them, since certain processing below |
1812 |
|
// must only be done on real MIDI note-off events (i.e. for |
1813 |
|
// correctly updating which MIDI keys are currently pressed down), |
1814 |
|
// plus a stop-note event just releases voices of one particular |
1815 |
|
// note, whereas a note-off event releases all voices on a |
1816 |
|
// particular MIDI key instead. |
1817 |
|
const bool isRealMIDINoteOffEvent = itNoteOffEvent->Type == Event::type_note_off; |
1818 |
|
|
1819 |
|
if (isRealMIDINoteOffEvent) |
1820 |
|
pChannel->listeners.PreProcessNoteOff(iKey, vel); |
1821 |
|
|
1822 |
#if !CONFIG_PROCESS_MUTED_CHANNELS |
#if !CONFIG_PROCESS_MUTED_CHANNELS |
1823 |
if (pEngineChannel->GetMute()) { // skip if sampler channel is muted |
if (pEngineChannel->GetMute()) { // skip if sampler channel is muted |
1824 |
pChannel->listeners.PostProcessNoteOff(iKey, vel); |
if (isRealMIDINoteOffEvent) |
1825 |
|
pChannel->listeners.PostProcessNoteOff(iKey, vel); |
1826 |
return; |
return; |
1827 |
} |
} |
1828 |
#endif |
#endif |
1829 |
|
|
1830 |
pKey->KeyPressed = false; // the MIDI key was now released |
if (isRealMIDINoteOffEvent) { |
1831 |
pChannel->KeyDown[iKey] = false; // just used as built-in %KEY_DOWN script variable |
pKey->KeyPressed = false; // the MIDI key was now released |
1832 |
|
pChannel->KeyDown[iKey] = false; // just used as built-in %KEY_DOWN script variable |
1833 |
|
} |
1834 |
|
|
1835 |
// move event to the key's own event list |
// move event to the key's own event list |
1836 |
RTList<Event>::Iterator itNoteOffEventOnKeyList = itNoteOffEvent.moveToEndOf(pKey->pEvents); |
RTList<Event>::Iterator itNoteOffEventOnKeyList = itNoteOffEvent.moveToEndOf(pKey->pEvents); |
1837 |
|
|
1838 |
bool bShouldRelease = pKey->Active && pChannel->ShouldReleaseVoice(itNoteOffEventOnKeyList->Param.Note.Key); |
if (isRealMIDINoteOffEvent) { |
1839 |
|
bool bShouldRelease = pKey->Active && pChannel->ShouldReleaseVoice(itNoteOffEventOnKeyList->Param.Note.Key); |
1840 |
|
|
1841 |
// 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) |
1842 |
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 |
1843 |
bool bOtherKeysPressed = false; |
bool bOtherKeysPressed = false; |
1844 |
if (iKey == pChannel->SoloKey) { |
if (iKey == pChannel->SoloKey) { |
1845 |
pChannel->SoloKey = -1; |
pChannel->SoloKey = -1; |
1846 |
// 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 |
1847 |
for (int i = 127; i > 0; i--) { |
for (int i = 127; i > 0; i--) { |
1848 |
MidiKey* pOtherKey = &pChannel->pMIDIKeyInfo[i]; |
MidiKey* pOtherKey = &pChannel->pMIDIKeyInfo[i]; |
1849 |
if (pOtherKey->KeyPressed) { |
if (pOtherKey->KeyPressed) { |
1850 |
bOtherKeysPressed = true; |
bOtherKeysPressed = true; |
1851 |
// make the other key the new 'currently active solo key' |
// make the other key the new 'currently active solo key' |
1852 |
pChannel->SoloKey = i; |
pChannel->SoloKey = i; |
1853 |
// get final portamento position of currently active voice |
// get final portamento position of currently active voice |
1854 |
if (pChannel->PortamentoMode) { |
if (pChannel->PortamentoMode) { |
1855 |
NoteIterator itNote = pKey->pActiveNotes->first(); |
NoteIterator itNote = pKey->pActiveNotes->first(); |
1856 |
VoiceIterator itVoice = itNote->pActiveVoices->first(); |
VoiceIterator itVoice = itNote->pActiveVoices->first(); |
1857 |
if (itVoice) itVoice->UpdatePortamentoPos(itNoteOffEventOnKeyList); |
if (itVoice) itVoice->UpdatePortamentoPos(itNoteOffEventOnKeyList); |
|
} |
|
|
// create a pseudo note on event |
|
|
RTList<Event>::Iterator itPseudoNoteOnEvent = pOtherKey->pEvents->allocAppend(); |
|
|
if (itPseudoNoteOnEvent) { |
|
|
// copy event |
|
|
*itPseudoNoteOnEvent = *itNoteOffEventOnKeyList; |
|
|
// transform event to a note on event |
|
|
itPseudoNoteOnEvent->Type = Event::type_note_on; |
|
|
itPseudoNoteOnEvent->Param.Note.Key = i; |
|
|
itPseudoNoteOnEvent->Param.Note.Velocity = pOtherKey->Velocity; |
|
|
// assign a new note to this note-on event |
|
|
if (LaunchNewNote(pChannel, &*itPseudoNoteOnEvent)) { |
|
|
// allocate and trigger new voice(s) for the other key |
|
|
TriggerNewVoices(pChannel, itPseudoNoteOnEvent, false); |
|
1858 |
} |
} |
1859 |
// if neither a voice was spawned or postponed then remove note on event from key again |
// create a pseudo note on event |
1860 |
if (!pOtherKey->Active && !pOtherKey->VoiceTheftsQueued) |
RTList<Event>::Iterator itPseudoNoteOnEvent = pOtherKey->pEvents->allocAppend(); |
1861 |
pOtherKey->pEvents->free(itPseudoNoteOnEvent); |
if (itPseudoNoteOnEvent) { |
1862 |
|
// copy event |
1863 |
|
*itPseudoNoteOnEvent = *itNoteOffEventOnKeyList; |
1864 |
|
// transform event to a note on event |
1865 |
|
itPseudoNoteOnEvent->Type = Event::type_note_on; //FIXME: should probably use Event::type_play_note instead (to avoid i.e. hanging notes) |
1866 |
|
itPseudoNoteOnEvent->Param.Note.Key = i; |
1867 |
|
itPseudoNoteOnEvent->Param.Note.Velocity = pOtherKey->Velocity; |
1868 |
|
// assign a new note to this note-on event |
1869 |
|
if (LaunchNewNote(pChannel, itPseudoNoteOnEvent)) { |
1870 |
|
// allocate and trigger new voice(s) for the other key |
1871 |
|
TriggerNewVoices(pChannel, itPseudoNoteOnEvent, false); |
1872 |
|
} |
1873 |
|
// if neither a voice was spawned or postponed then remove note on event from key again |
1874 |
|
if (!pOtherKey->Active && !pOtherKey->VoiceTheftsQueued) |
1875 |
|
pOtherKey->pEvents->free(itPseudoNoteOnEvent); |
1876 |
|
|
1877 |
} else dmsg(1,("Could not respawn voice, no free event left\n")); |
} else dmsg(1,("Could not respawn voice, no free event left\n")); |
1878 |
break; // done |
break; // done |
1879 |
|
} |
1880 |
} |
} |
1881 |
} |
} |
1882 |
} |
if (bOtherKeysPressed) { |
1883 |
if (bOtherKeysPressed) { |
if (pKey->Active) { // kill all voices on this key |
1884 |
if (pKey->Active) { // kill all voices on this key |
bShouldRelease = false; // no need to release, as we kill it here |
1885 |
bShouldRelease = false; // no need to release, as we kill it here |
for (NoteIterator itNote = pKey->pActiveNotes->first(); itNote; ++itNote) { |
1886 |
for (NoteIterator itNote = pKey->pActiveNotes->first(); itNote; ++itNote) { |
VoiceIterator itVoiceToBeKilled = itNote->pActiveVoices->first(); |
1887 |
VoiceIterator itVoiceToBeKilled = itNote->pActiveVoices->first(); |
VoiceIterator end = itNote->pActiveVoices->end(); |
1888 |
VoiceIterator end = itNote->pActiveVoices->end(); |
for (; itVoiceToBeKilled != end; ++itVoiceToBeKilled) { |
1889 |
for (; itVoiceToBeKilled != end; ++itVoiceToBeKilled) { |
if (!(itVoiceToBeKilled->Type & Voice::type_release_trigger)) |
1890 |
if (!(itVoiceToBeKilled->Type & Voice::type_release_trigger)) |
itVoiceToBeKilled->Kill(itNoteOffEventOnKeyList); |
1891 |
itVoiceToBeKilled->Kill(itNoteOffEventOnKeyList); |
} |
1892 |
} |
} |
1893 |
} |
} |
1894 |
} |
} else pChannel->PortamentoPos = -1.0f; |
1895 |
} else pChannel->PortamentoPos = -1.0f; |
} |
|
} |
|
1896 |
|
|
1897 |
// 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 no solo mode (the usual case) or if solo mode and no other key pressed, then release voices on this key if needed |
1898 |
if (bShouldRelease) { |
if (bShouldRelease) { |
1899 |
itNoteOffEventOnKeyList->Type = Event::type_release; // transform event type |
itNoteOffEventOnKeyList->Type = Event::type_release_key; // transform event type |
1900 |
|
// spawn release triggered voice(s) if needed |
1901 |
// spawn release triggered voice(s) if needed |
ProcessReleaseTrigger(pChannel, itNoteOffEventOnKeyList, pKey); |
1902 |
if (pKey->ReleaseTrigger && pChannel->pInstrument) { |
} |
1903 |
// assign a new note to this release event |
} else if (itNoteOffEventOnKeyList->Type == Event::type_stop_note) { |
1904 |
if (LaunchNewNote(pChannel, &*itNoteOffEventOnKeyList)) { |
// This programmatically caused event is caused by a call to |
1905 |
// allocate and trigger new release voice(s) |
// the built-in instrument script function note_off(). In |
1906 |
TriggerReleaseVoices(pChannel, itNoteOffEventOnKeyList); |
// contrast to a real MIDI note-off event the stop-note |
1907 |
} |
// event just intends to release voices of one particular note. |
1908 |
pKey->ReleaseTrigger = false; |
NoteBase* pNote = pChannel->pEngine->NoteByID( itNoteOffEventOnKeyList->Param.Note.ID ); |
1909 |
|
if (pNote) { // the requested note is still alive ... |
1910 |
|
itNoteOffEventOnKeyList->Type = Event::type_release_note; // transform event type |
1911 |
|
} else { // note is dead and gone .. |
1912 |
|
pKey->pEvents->free(itNoteOffEventOnKeyList); // remove stop-note event from key again |
1913 |
|
return; // prevent event to be removed a 2nd time below |
1914 |
} |
} |
1915 |
} |
} |
1916 |
|
|
1918 |
if (!pKey->Active && !pKey->VoiceTheftsQueued) |
if (!pKey->Active && !pKey->VoiceTheftsQueued) |
1919 |
pKey->pEvents->free(itNoteOffEventOnKeyList); |
pKey->pEvents->free(itNoteOffEventOnKeyList); |
1920 |
|
|
1921 |
pChannel->listeners.PostProcessNoteOff(iKey, vel); |
if (isRealMIDINoteOffEvent) |
1922 |
|
pChannel->listeners.PostProcessNoteOff(iKey, vel); |
1923 |
|
} |
1924 |
|
|
1925 |
|
/** |
1926 |
|
* Called on sustain pedal up events to check and if required, |
1927 |
|
* launch release trigger voices on the respective active key. |
1928 |
|
* |
1929 |
|
* @param pEngineChannel - engine channel on which this event occurred on |
1930 |
|
* @param itEvent - release trigger event (contains note number) |
1931 |
|
*/ |
1932 |
|
virtual void ProcessReleaseTrigger(EngineChannel* pEngineChannel, RTList<Event>::Iterator& itEvent) OVERRIDE { |
1933 |
|
EngineChannelBase<V, R, I>* pChannel = static_cast<EngineChannelBase<V, R, I>*>(pEngineChannel); |
1934 |
|
|
1935 |
|
const int iKey = itEvent->Param.Note.Key; |
1936 |
|
if (iKey < 0 || iKey > 127) return; // ignore event, key outside allowed key range |
1937 |
|
|
1938 |
|
MidiKey* pKey = &pChannel->pMIDIKeyInfo[iKey]; |
1939 |
|
|
1940 |
|
ProcessReleaseTrigger(pChannel, itEvent, pKey); |
1941 |
|
} |
1942 |
|
|
1943 |
|
/** |
1944 |
|
* Called on note-off and sustain pedal up events to check and if |
1945 |
|
* required, launch release trigger voices on the respective active |
1946 |
|
* key. |
1947 |
|
* |
1948 |
|
* @param pEngineChannel - engine channel on which this event occurred on |
1949 |
|
* @param itEvent - note off event / release trigger event |
1950 |
|
* @param pKey - key on which the release trigger voices shall be spawned |
1951 |
|
*/ |
1952 |
|
inline void ProcessReleaseTrigger(EngineChannelBase<V, R, I>* pChannel, RTList<Event>::Iterator& itEvent, MidiKey* pKey) { |
1953 |
|
// spawn release triggered voice(s) if needed |
1954 |
|
if (pKey->ReleaseTrigger && pChannel->pInstrument) { |
1955 |
|
// assign a new note to this release event |
1956 |
|
if (LaunchNewNote(pChannel, itEvent)) { |
1957 |
|
// allocate and trigger new release voice(s) |
1958 |
|
TriggerReleaseVoices(pChannel, itEvent); |
1959 |
|
} |
1960 |
|
pKey->ReleaseTrigger = false; |
1961 |
|
} |
1962 |
|
} |
1963 |
|
|
1964 |
|
/** |
1965 |
|
* Called on "kill note" events, which currently only happens on |
1966 |
|
* built-in real-time instrument script function fade_out(). This |
1967 |
|
* method only fulfills one task: moving the even to the Note's own |
1968 |
|
* event list so that its voices can process the kill event sample |
1969 |
|
* accurately. |
1970 |
|
*/ |
1971 |
|
void ProcessKillNote(EngineChannel* pEngineChannel, RTList<Event>::Iterator& itEvent) { |
1972 |
|
EngineChannelBase<V, R, I>* pChannel = static_cast<EngineChannelBase<V, R, I>*>(pEngineChannel); |
1973 |
|
|
1974 |
|
NoteBase* pNote = pChannel->pEngine->NoteByID( itEvent->Param.Note.ID ); |
1975 |
|
if (!pNote || pNote->hostKey < 0 || pNote->hostKey >= 128) return; |
1976 |
|
|
1977 |
|
// move note kill event to its MIDI key |
1978 |
|
MidiKey* pKey = &pChannel->pMIDIKeyInfo[pNote->hostKey]; |
1979 |
|
itEvent.moveToEndOf(pKey->pEvents); |
1980 |
|
} |
1981 |
|
|
1982 |
|
/** |
1983 |
|
* Called on note synthesis parameter change events. These are |
1984 |
|
* internal events caused by calling built-in real-time instrument |
1985 |
|
* script functions like change_vol(), change_tune(), etc. |
1986 |
|
* |
1987 |
|
* This method performs two tasks: |
1988 |
|
* |
1989 |
|
* - It converts the event's relative values changes (Deltas) to |
1990 |
|
* the respective final new synthesis parameter value (AbsValue), |
1991 |
|
* for that particular moment of the event that is. |
1992 |
|
* |
1993 |
|
* - It moves the individual events to the Note's own event list |
1994 |
|
* (or actually to the event list of the MIDI key), so that |
1995 |
|
* voices can process those events sample accurately. |
1996 |
|
* |
1997 |
|
* @param pEngineChannel - engine channel on which this event occurred on |
1998 |
|
* @param itEvent - note synthesis parameter change event |
1999 |
|
*/ |
2000 |
|
virtual void ProcessNoteSynthParam(EngineChannel* pEngineChannel, RTList<Event>::Iterator& itEvent) { |
2001 |
|
EngineChannelBase<V, R, I>* pChannel = static_cast<EngineChannelBase<V, R, I>*>(pEngineChannel); |
2002 |
|
|
2003 |
|
NoteBase* pNote = pChannel->pEngine->NoteByID( itEvent->Param.NoteSynthParam.NoteID ); |
2004 |
|
if (!pNote || pNote->hostKey < 0 || pNote->hostKey >= 128) return; |
2005 |
|
|
2006 |
|
const bool& relative = itEvent->Param.NoteSynthParam.Relative; |
2007 |
|
|
2008 |
|
switch (itEvent->Param.NoteSynthParam.Type) { |
2009 |
|
case Event::synth_param_volume: |
2010 |
|
if (relative) |
2011 |
|
pNote->Override.Volume *= itEvent->Param.NoteSynthParam.Delta; |
2012 |
|
else |
2013 |
|
pNote->Override.Volume = itEvent->Param.NoteSynthParam.Delta; |
2014 |
|
itEvent->Param.NoteSynthParam.AbsValue = pNote->Override.Volume; |
2015 |
|
break; |
2016 |
|
case Event::synth_param_volume_time: |
2017 |
|
pNote->Override.VolumeTime = itEvent->Param.NoteSynthParam.AbsValue = itEvent->Param.NoteSynthParam.Delta; |
2018 |
|
break; |
2019 |
|
case Event::synth_param_volume_curve: |
2020 |
|
itEvent->Param.NoteSynthParam.AbsValue = itEvent->Param.NoteSynthParam.Delta; |
2021 |
|
pNote->Override.VolumeCurve = (fade_curve_t) itEvent->Param.NoteSynthParam.AbsValue; |
2022 |
|
break; |
2023 |
|
case Event::synth_param_pitch: |
2024 |
|
if (relative) |
2025 |
|
pNote->Override.Pitch *= itEvent->Param.NoteSynthParam.Delta; |
2026 |
|
else |
2027 |
|
pNote->Override.Pitch = itEvent->Param.NoteSynthParam.Delta; |
2028 |
|
itEvent->Param.NoteSynthParam.AbsValue = pNote->Override.Pitch; |
2029 |
|
break; |
2030 |
|
case Event::synth_param_pitch_time: |
2031 |
|
pNote->Override.PitchTime = itEvent->Param.NoteSynthParam.AbsValue = itEvent->Param.NoteSynthParam.Delta; |
2032 |
|
break; |
2033 |
|
case Event::synth_param_pitch_curve: |
2034 |
|
itEvent->Param.NoteSynthParam.AbsValue = itEvent->Param.NoteSynthParam.Delta; |
2035 |
|
pNote->Override.PitchCurve = (fade_curve_t) itEvent->Param.NoteSynthParam.AbsValue; |
2036 |
|
break; |
2037 |
|
case Event::synth_param_pan: |
2038 |
|
if (relative) { |
2039 |
|
pNote->Override.Pan = RTMath::RelativeSummedAvg(pNote->Override.Pan, itEvent->Param.NoteSynthParam.Delta, ++pNote->Override.PanSources); |
2040 |
|
} else { |
2041 |
|
pNote->Override.Pan = itEvent->Param.NoteSynthParam.Delta; |
2042 |
|
pNote->Override.PanSources = 1; // only relevant on subsequent change_pan() instrument script calls on same note with 'relative' argument being set |
2043 |
|
} |
2044 |
|
itEvent->Param.NoteSynthParam.AbsValue = pNote->Override.Pan; |
2045 |
|
break; |
2046 |
|
case Event::synth_param_cutoff: |
2047 |
|
pNote->Override.Cutoff = itEvent->Param.NoteSynthParam.AbsValue = itEvent->Param.NoteSynthParam.Delta; |
2048 |
|
break; |
2049 |
|
case Event::synth_param_resonance: |
2050 |
|
pNote->Override.Resonance = itEvent->Param.NoteSynthParam.AbsValue = itEvent->Param.NoteSynthParam.Delta; |
2051 |
|
break; |
2052 |
|
case Event::synth_param_attack: |
2053 |
|
pNote->Override.Attack = itEvent->Param.NoteSynthParam.AbsValue = itEvent->Param.NoteSynthParam.Delta; |
2054 |
|
break; |
2055 |
|
case Event::synth_param_decay: |
2056 |
|
pNote->Override.Decay = itEvent->Param.NoteSynthParam.AbsValue = itEvent->Param.NoteSynthParam.Delta; |
2057 |
|
break; |
2058 |
|
case Event::synth_param_release: |
2059 |
|
pNote->Override.Release = itEvent->Param.NoteSynthParam.AbsValue = itEvent->Param.NoteSynthParam.Delta; |
2060 |
|
break; |
2061 |
|
case Event::synth_param_amp_lfo_depth: |
2062 |
|
pNote->Override.AmpLFODepth = itEvent->Param.NoteSynthParam.AbsValue = itEvent->Param.NoteSynthParam.Delta; |
2063 |
|
break; |
2064 |
|
case Event::synth_param_amp_lfo_freq: |
2065 |
|
pNote->Override.AmpLFOFreq = itEvent->Param.NoteSynthParam.AbsValue = itEvent->Param.NoteSynthParam.Delta; |
2066 |
|
break; |
2067 |
|
case Event::synth_param_pitch_lfo_depth: |
2068 |
|
pNote->Override.PitchLFODepth = itEvent->Param.NoteSynthParam.AbsValue = itEvent->Param.NoteSynthParam.Delta; |
2069 |
|
break; |
2070 |
|
case Event::synth_param_pitch_lfo_freq: |
2071 |
|
pNote->Override.PitchLFOFreq = itEvent->Param.NoteSynthParam.AbsValue = itEvent->Param.NoteSynthParam.Delta; |
2072 |
|
break; |
2073 |
|
} |
2074 |
|
|
2075 |
|
// move note parameter event to its MIDI key |
2076 |
|
MidiKey* pKey = &pChannel->pMIDIKeyInfo[pNote->hostKey]; |
2077 |
|
itEvent.moveToEndOf(pKey->pEvents); |
2078 |
} |
} |
2079 |
|
|
2080 |
/** |
/** |
2081 |
* 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 |
2082 |
* control and status variables. This method is protected by a mutex. |
* control and status variables. This method is protected by a mutex. |
2083 |
*/ |
*/ |
2084 |
virtual void ResetInternal() { |
virtual void ResetInternal() OVERRIDE { |
2085 |
LockGuard lock(ResetInternalMutex); |
LockGuard lock(ResetInternalMutex); |
2086 |
|
|
2087 |
// make sure that the engine does not get any sysex messages |
// make sure that the engine does not get any sysex messages |
2140 |
* @param pEngineChannel - engine channel on which all voices should be killed |
* @param pEngineChannel - engine channel on which all voices should be killed |
2141 |
* @param itKillEvent - event which caused this killing of all voices |
* @param itKillEvent - event which caused this killing of all voices |
2142 |
*/ |
*/ |
2143 |
virtual void KillAllVoices(EngineChannel* pEngineChannel, Pool<Event>::Iterator& itKillEvent) { |
virtual void KillAllVoices(EngineChannel* pEngineChannel, Pool<Event>::Iterator& itKillEvent) OVERRIDE { |
2144 |
EngineChannelBase<V, R, I>* pChannel = static_cast<EngineChannelBase<V, R, I>*>(pEngineChannel); |
EngineChannelBase<V, R, I>* pChannel = static_cast<EngineChannelBase<V, R, I>*>(pEngineChannel); |
2145 |
int count = pChannel->KillAllVoices(itKillEvent); |
int count = pChannel->KillAllVoices(itKillEvent); |
2146 |
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 |
2175 |
bool HandleKeyGroupConflicts |
bool HandleKeyGroupConflicts |
2176 |
) = 0; |
) = 0; |
2177 |
|
|
2178 |
virtual int GetMinFadeOutSamples() { return MinFadeOutSamples; } |
virtual int GetMinFadeOutSamples() OVERRIDE { return MinFadeOutSamples; } |
2179 |
|
|
2180 |
int InitNewVoice ( |
int InitNewVoice ( |
2181 |
EngineChannelBase<V, R, I>* pChannel, |
EngineChannelBase<V, R, I>* pChannel, |