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 |
} |
} |
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(); |
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(); |
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 |
} |
} |
621 |
// implementation of abstract methods derived from class 'LinuxSampler::NotePool' |
// implementation of abstract methods derived from class 'LinuxSampler::NotePool' |
622 |
virtual Pool<V>* GetVoicePool() OVERRIDE { return pVoicePool; } |
virtual Pool<V>* GetVoicePool() OVERRIDE { return pVoicePool; } |
623 |
virtual Pool< Note<V> >* GetNotePool() OVERRIDE { return pNotePool; } |
virtual Pool< Note<V> >* GetNotePool() OVERRIDE { return pNotePool; } |
624 |
virtual Pool<note_id_t>* GetNodeIDPool() OVERRIDE { return ¬eIDPool; } |
virtual Pool<note_id_t>* GetNoteIDPool() OVERRIDE { return ¬eIDPool; } |
625 |
|
|
626 |
D* GetDiskThread() { return pDiskThread; } |
D* GetDiskThread() { return pDiskThread; } |
627 |
|
|
692 |
* @param pNoteOnEvent - event which caused this |
* @param pNoteOnEvent - event which caused this |
693 |
* @returns new note's unique ID (or zero on error) |
* @returns new note's unique ID (or zero on error) |
694 |
*/ |
*/ |
695 |
note_id_t LaunchNewNote(LinuxSampler::EngineChannel* pEngineChannel, Event* pNoteOnEvent) OVERRIDE { |
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); |
EngineChannelBase<V, R, I>* pChannel = static_cast<EngineChannelBase<V, R, I>*>(pEngineChannel); |
697 |
Pool< Note<V> >* pNotePool = GetNotePool(); |
Pool< Note<V> >* pNotePool = GetNotePool(); |
698 |
|
|
706 |
NoteIterator itNewNote = pNotePool->allocAppend(); |
NoteIterator itNewNote = pNotePool->allocAppend(); |
707 |
const note_id_t newNoteID = pNotePool->getID(itNewNote); |
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 |
// usually the new note (and its subsequent voices) will be |
713 |
// allocated on the key provided by the event's note number, |
// allocated on the key provided by the event's note number, |
714 |
// however if this new note is requested not to be a regular |
// 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 |
// note, but rather a child note, then this new note will be |
716 |
// allocated on the parent note's key instead in order to |
// allocated on the parent note's key instead in order to |
717 |
// release the child note simultaniously with its parent note |
// release the child note simultaniously with its parent note |
718 |
itNewNote->hostKey = pNoteOnEvent->Param.Note.Key; |
itNewNote->hostKey = itNoteOnEvent->Param.Note.Key; |
719 |
|
|
720 |
// in case this new note was requested to be a child note, |
// in case this new note was requested to be a child note, |
721 |
// then retrieve its parent note and link them with each other |
// then retrieve its parent note and link them with each other |
722 |
const note_id_t parentNoteID = pNoteOnEvent->Param.Note.ParentNoteID; |
const note_id_t parentNoteID = itNoteOnEvent->Param.Note.ParentNoteID; |
723 |
if (parentNoteID) { |
if (parentNoteID) { |
724 |
NoteIterator itParentNote = pNotePool->fromID(parentNoteID); |
NoteIterator itParentNote = pNotePool->fromID(parentNoteID); |
725 |
if (itParentNote) { |
if (itParentNote) { |
747 |
dmsg(2,("Launched new note on host key %d\n", itNewNote->hostKey)); |
dmsg(2,("Launched new note on host key %d\n", itNewNote->hostKey)); |
748 |
|
|
749 |
// copy event which caused this note |
// copy event which caused this note |
750 |
itNewNote->cause = *pNoteOnEvent; |
itNewNote->cause = *itNoteOnEvent; |
751 |
itNewNote->eventID = pEventPool->getID(pNoteOnEvent); |
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 |
// move new note to its host key |
757 |
MidiKey* pKey = &pChannel->pMIDIKeyInfo[itNewNote->hostKey]; |
MidiKey* pKey = &pChannel->pMIDIKeyInfo[itNewNote->hostKey]; |
758 |
itNewNote.moveToEndOf(pKey->pActiveNotes); |
itNewNote.moveToEndOf(pKey->pActiveNotes); |
759 |
|
pChannel->markKeyAsActive(pKey); |
760 |
|
|
761 |
// 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 |
762 |
pNoteOnEvent->Param.Note.ID = newNoteID; |
itNoteOnEvent->Param.Note.ID = newNoteID; |
763 |
|
|
764 |
return newNoteID; // success |
return newNoteID; // success |
765 |
} |
} |
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 |
// 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(), |
for (RTList<Event>::Iterator itEvent = pChannel->pEvents->first(), |
798 |
end = pChannel->pEvents->end(); itEvent != end; ++itEvent) |
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) { |
switch (itEvent->Type) { |
805 |
case Event::type_note_on: |
case Event::type_note_on: |
806 |
if (pChannel->pScript->handlerNote) |
if (pChannel->pScript->handlerNote) |
819 |
case Event::type_note_pressure: |
case Event::type_note_pressure: |
820 |
//TODO: ... |
//TODO: ... |
821 |
break; |
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 |
// this has to be run again, since the newly spawned scripts |
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(); |
887 |
for (; itEvent != end; ++itEvent) { |
for (; itEvent != end; ++itEvent) { |
888 |
|
bool bIsCC = false; // just for resetting RPN/NRPN below |
889 |
switch (itEvent->Type) { |
switch (itEvent->Type) { |
890 |
case Event::type_note_on: |
case Event::type_note_on: |
891 |
dmsg(5,("Engine: Note on received\n")); |
dmsg(5,("Engine: Note on received\n")); |
892 |
ProcessNoteOn((EngineChannel*)itEvent->pEngineChannel, itEvent); |
ProcessNoteOn((EngineChannel*)itEvent->pEngineChannel, itEvent); |
893 |
break; |
break; |
894 |
|
case Event::type_play_note: |
895 |
|
dmsg(5,("Engine: Play Note received\n")); |
896 |
|
ProcessNoteOn((EngineChannel*)itEvent->pEngineChannel, itEvent); |
897 |
|
break; |
898 |
case Event::type_note_off: |
case Event::type_note_off: |
899 |
dmsg(5,("Engine: Note off received\n")); |
dmsg(5,("Engine: Note off received\n")); |
900 |
ProcessNoteOff((EngineChannel*)itEvent->pEngineChannel, itEvent); |
ProcessNoteOff((EngineChannel*)itEvent->pEngineChannel, itEvent); |
901 |
break; |
break; |
902 |
|
case Event::type_stop_note: |
903 |
|
dmsg(5,("Engine: Stop Note received\n")); |
904 |
|
ProcessNoteOff((EngineChannel*)itEvent->pEngineChannel, itEvent); |
905 |
|
break; |
906 |
|
case Event::type_kill_note: |
907 |
|
dmsg(5,("Engine: Kill Note received\n")); |
908 |
|
ProcessKillNote((EngineChannel*)itEvent->pEngineChannel, itEvent); |
909 |
|
break; |
910 |
case Event::type_control_change: |
case Event::type_control_change: |
911 |
dmsg(5,("Engine: MIDI CC received\n")); |
dmsg(5,("Engine: MIDI CC received\n")); |
912 |
ProcessControlChange((EngineChannel*)itEvent->pEngineChannel, itEvent); |
ProcessControlChange((EngineChannel*)itEvent->pEngineChannel, itEvent); |
913 |
|
bIsCC = true; |
914 |
|
break; |
915 |
|
case Event::type_rpn: // this can only be reached here by an instrument script having called set_rpn() |
916 |
|
dmsg(5,("Engine: MIDI RPN received\n")); |
917 |
|
ProcessHardcodedRpn((EngineChannel*)itEvent->pEngineChannel, itEvent); |
918 |
|
bIsCC = true; |
919 |
|
break; |
920 |
|
case Event::type_nrpn: // this can only be reached here by an instrument script having called set_nrpn() |
921 |
|
dmsg(5,("Engine: MIDI NRPN received\n")); |
922 |
|
ProcessHardcodedNrpn((EngineChannel*)itEvent->pEngineChannel, itEvent); |
923 |
|
bIsCC = true; |
924 |
break; |
break; |
925 |
case Event::type_channel_pressure: |
case Event::type_channel_pressure: |
926 |
dmsg(5,("Engine: MIDI Chan. Pressure received\n")); |
dmsg(5,("Engine: MIDI Chan. Pressure received\n")); |
934 |
dmsg(5,("Engine: Pitchbend received\n")); |
dmsg(5,("Engine: Pitchbend received\n")); |
935 |
ProcessPitchbend(static_cast<AbstractEngineChannel*>(itEvent->pEngineChannel), itEvent); |
ProcessPitchbend(static_cast<AbstractEngineChannel*>(itEvent->pEngineChannel), itEvent); |
936 |
break; |
break; |
937 |
|
case Event::type_note_synth_param: |
938 |
|
dmsg(5,("Engine: Note Synth Param received\n")); |
939 |
|
ProcessNoteSynthParam(itEvent->pEngineChannel, itEvent); |
940 |
|
break; |
941 |
|
case Event::type_sysex: |
942 |
|
break; // TODO ... |
943 |
|
|
944 |
|
case Event::type_cancel_release_key: |
945 |
|
case Event::type_release_key: |
946 |
|
case Event::type_release_note: |
947 |
|
break; // noop |
948 |
|
} |
949 |
|
// reset cached RPN/NRPN parameter number and data in |
950 |
|
// case this event was not a control change event |
951 |
|
if (!bIsCC) { |
952 |
|
if (pChannel->GetMidiRpnParameter() >= 0) |
953 |
|
pChannel->ResetMidiRpnParameter(); |
954 |
|
if (pChannel->GetMidiNrpnParameter() >= 0) |
955 |
|
pChannel->ResetMidiNrpnParameter(); |
956 |
} |
} |
957 |
} |
} |
958 |
} |
} |
1021 |
// script event object |
// script event object |
1022 |
RTList<ScriptEvent>::Iterator itScriptEvent = |
RTList<ScriptEvent>::Iterator itScriptEvent = |
1023 |
pChannel->pScript->pEvents->allocAppend(); |
pChannel->pScript->pEvents->allocAppend(); |
1024 |
|
// if event handler uses polyphonic variables, reset them |
1025 |
|
// to zero values before starting to execute the handler |
1026 |
|
if (pEventHandler->isPolyphonic()) |
1027 |
|
itScriptEvent->execCtx->resetPolyphonicData(); |
1028 |
ProcessScriptEvent( |
ProcessScriptEvent( |
1029 |
pChannel, itEvent, pEventHandler, itScriptEvent |
pChannel, itEvent, pEventHandler, itScriptEvent |
1030 |
); |
); |
1057 |
|
|
1058 |
// initialize/reset other members |
// initialize/reset other members |
1059 |
itScriptEvent->cause = *itEvent; |
itScriptEvent->cause = *itEvent; |
1060 |
|
itScriptEvent->scheduleTime = itEvent->SchedTime(); |
1061 |
itScriptEvent->currentHandler = 0; |
itScriptEvent->currentHandler = 0; |
1062 |
itScriptEvent->executionSlices = 0; |
itScriptEvent->executionSlices = 0; |
1063 |
|
itScriptEvent->ignoreAllWaitCalls = false; |
1064 |
|
itScriptEvent->handlerType = pEventHandler->eventHandlerType(); |
1065 |
|
itScriptEvent->parentHandlerID = 0; |
1066 |
|
itScriptEvent->childHandlerID[0] = 0; |
1067 |
|
itScriptEvent->autoAbortByParent = false; |
1068 |
|
itScriptEvent->forkIndex = 0; |
1069 |
// this is the native representation of the $EVENT_ID script variable |
// this is the native representation of the $EVENT_ID script variable |
1070 |
itScriptEvent->id = |
itScriptEvent->id = |
1071 |
(itEvent->Type == Event::type_note_on) |
(itEvent->Type == Event::type_note_on) |
1178 |
* @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 |
1179 |
*/ |
*/ |
1180 |
int StealVoice(EngineChannel* pEngineChannel, Pool<Event>::Iterator& itNoteOnEvent) { |
int StealVoice(EngineChannel* pEngineChannel, Pool<Event>::Iterator& itNoteOnEvent) { |
1181 |
|
dmsg(3,("StealVoice()\n")); |
1182 |
if (VoiceSpawnsLeft <= 0) { |
if (VoiceSpawnsLeft <= 0) { |
1183 |
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")); |
1184 |
return -1; |
return -1; |
1203 |
int iChannelIndex; |
int iChannelIndex; |
1204 |
VoiceIterator itSelectedVoice; |
VoiceIterator itSelectedVoice; |
1205 |
|
|
1206 |
|
#if CONFIG_DEVMODE |
1207 |
|
EngineChannel* pBegin = NULL; // to detect endless loop |
1208 |
|
#endif |
1209 |
|
|
1210 |
// select engine channel |
// select engine channel |
1211 |
if (pLastStolenChannel) { |
if (pLastStolenChannel) { |
1212 |
pSelectedChannel = pLastStolenChannel; |
pSelectedChannel = pLastStolenChannel; |
1249 |
} |
} |
1250 |
|
|
1251 |
#if CONFIG_DEVMODE |
#if CONFIG_DEVMODE |
1252 |
EngineChannel* pBegin = pSelectedChannel; // to detect endless loop |
pBegin = pSelectedChannel; // to detect endless loop |
1253 |
#endif // CONFIG_DEVMODE |
#endif // CONFIG_DEVMODE |
1254 |
|
|
1255 |
while (true) { // iterate through engine channels |
while (true) { // iterate through engine channels |
1338 |
// the script's "init" event handler is only executed |
// the script's "init" event handler is only executed |
1339 |
// once (when the script is loaded or reloaded) |
// once (when the script is loaded or reloaded) |
1340 |
if (pEngineChannel->pScript && pEngineChannel->pScript->handlerInit) { |
if (pEngineChannel->pScript && pEngineChannel->pScript->handlerInit) { |
1341 |
|
dmsg(5,("Engine: exec handlerInit %p\n", pEngineChannel->pScript->handlerInit)); |
1342 |
RTList<ScriptEvent>::Iterator itScriptEvent = |
RTList<ScriptEvent>::Iterator itScriptEvent = |
1343 |
pEngineChannel->pScript->pEvents->allocAppend(); |
pEngineChannel->pScript->pEvents->allocAppend(); |
1344 |
|
|
1345 |
|
itScriptEvent->cause = pEventGenerator->CreateEvent(0); |
1346 |
|
itScriptEvent->cause.Type = (Event::type_t) -1; // some invalid type to avoid random event processing |
1347 |
itScriptEvent->cause.pEngineChannel = pEngineChannel; |
itScriptEvent->cause.pEngineChannel = pEngineChannel; |
1348 |
|
itScriptEvent->cause.pMidiInputPort = pEngineChannel->GetMidiInputPort(); |
1349 |
|
itScriptEvent->id = 0; |
1350 |
itScriptEvent->handlers[0] = pEngineChannel->pScript->handlerInit; |
itScriptEvent->handlers[0] = pEngineChannel->pScript->handlerInit; |
1351 |
itScriptEvent->handlers[1] = NULL; |
itScriptEvent->handlers[1] = NULL; |
1352 |
|
itScriptEvent->currentHandler = 0; |
1353 |
VMExecStatus_t res = pScriptVM->exec( |
itScriptEvent->executionSlices = 0; |
1354 |
pEngineChannel->pScript->parserContext, &*itScriptEvent |
itScriptEvent->ignoreAllWaitCalls = false; |
1355 |
); |
itScriptEvent->handlerType = VM_EVENT_HANDLER_INIT; |
1356 |
|
itScriptEvent->parentHandlerID = 0; |
1357 |
|
itScriptEvent->childHandlerID[0] = 0; |
1358 |
|
itScriptEvent->autoAbortByParent = false; |
1359 |
|
itScriptEvent->forkIndex = 0; |
1360 |
|
|
1361 |
|
VMExecStatus_t res; |
1362 |
|
size_t instructionsCount = 0; |
1363 |
|
const size_t maxInstructions = 200000; // aiming approx. 1 second max. (based on very roughly 5us / instruction) |
1364 |
|
bool bWarningShown = false; |
1365 |
|
do { |
1366 |
|
res = pScriptVM->exec( |
1367 |
|
pEngineChannel->pScript->parserContext, &*itScriptEvent |
1368 |
|
); |
1369 |
|
instructionsCount += itScriptEvent->execCtx->instructionsPerformed(); |
1370 |
|
if (instructionsCount > maxInstructions && !bWarningShown) { |
1371 |
|
bWarningShown = true; |
1372 |
|
dmsg(0,("[ScriptVM] WARNING: \"init\" event handler of instrument script executing for long time!\n")); |
1373 |
|
} |
1374 |
|
} while (res & VM_EXEC_SUSPENDED && !(res & VM_EXEC_ERROR)); |
1375 |
|
|
1376 |
pEngineChannel->pScript->pEvents->free(itScriptEvent); |
pEngineChannel->pScript->pEvents->free(itScriptEvent); |
1377 |
} |
} |
1432 |
// usually there should already be a new Note object |
// usually there should already be a new Note object |
1433 |
NoteIterator itNote = GetNotePool()->fromID(itVoiceStealEvent->Param.Note.ID); |
NoteIterator itNote = GetNotePool()->fromID(itVoiceStealEvent->Param.Note.ID); |
1434 |
if (!itNote) { // should not happen, but just to be sure ... |
if (!itNote) { // should not happen, but just to be sure ... |
1435 |
const note_id_t noteID = LaunchNewNote(pEngineChannel, &*itVoiceStealEvent); |
dmsg(2,("Engine: No Note object for stolen voice!\n")); |
1436 |
|
const note_id_t noteID = LaunchNewNote(pEngineChannel, itVoiceStealEvent); |
1437 |
if (!noteID) { |
if (!noteID) { |
1438 |
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")); |
1439 |
continue; |
continue; |
1477 |
void PostProcess(EngineChannel* pEngineChannel) { |
void PostProcess(EngineChannel* pEngineChannel) { |
1478 |
EngineChannelBase<V, R, I>* pChannel = |
EngineChannelBase<V, R, I>* pChannel = |
1479 |
static_cast<EngineChannelBase<V, R, I>*>(pEngineChannel); |
static_cast<EngineChannelBase<V, R, I>*>(pEngineChannel); |
1480 |
pChannel->FreeAllInactiveKyes(); |
pChannel->FreeAllInactiveKeys(); |
1481 |
|
|
1482 |
// empty the engine channel's own event lists |
// empty the engine channel's own event lists |
1483 |
// (only events of the current audio fragment cycle) |
// (only events of the current audio fragment cycle) |
1499 |
EngineChannelBase<V, R, I>* pChannel = |
EngineChannelBase<V, R, I>* pChannel = |
1500 |
static_cast<EngineChannelBase<V, R, I>*>(pEngineChannel); |
static_cast<EngineChannelBase<V, R, I>*>(pEngineChannel); |
1501 |
|
|
1502 |
|
// will be set to true if this CC event has anything to do with RPN/NRPN |
1503 |
|
bool bIsRpn = false, bIsNrpn = false; |
1504 |
|
|
1505 |
switch (itControlChangeEvent->Param.CC.Controller) { |
switch (itControlChangeEvent->Param.CC.Controller) { |
1506 |
case 5: { // portamento time |
case 5: { // portamento time |
1507 |
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; |
1508 |
break; |
break; |
1509 |
} |
} |
1510 |
case 6: { // data entry (currently only used for RPN and NRPN controllers) |
case 6: { // data entry (MSB) |
1511 |
//dmsg(1,("DATA ENTRY %d\n", itControlChangeEvent->Param.CC.Value)); |
//dmsg(1,("DATA ENTRY MSB %d\n", itControlChangeEvent->Param.CC.Value)); |
1512 |
if (pChannel->GetMidiRpnController() >= 0) { // RPN controller number was sent previously ... |
if (pChannel->GetMidiRpnParameter() >= 0) { // RPN parameter number was sent previously ... |
1513 |
dmsg(4,("Guess it's an RPN ...\n")); |
pChannel->SetMidiRpnDataMsb( |
1514 |
if (pChannel->GetMidiRpnController() == 2) { // coarse tuning in half tones |
itControlChangeEvent->Param.CC.Value |
1515 |
int transpose = (int) itControlChangeEvent->Param.CC.Value - 64; |
); |
1516 |
// limit to +- two octaves for now |
bIsRpn = true; |
1517 |
transpose = RTMath::Min(transpose, 24); |
|
1518 |
transpose = RTMath::Max(transpose, -24); |
// look-ahead: if next MIDI event is data entry LSB, |
1519 |
pChannel->GlobalTranspose = transpose; |
// then skip this event here for now (to avoid double |
1520 |
// workaround, so we won't have hanging notes |
// handling of what's supposed to be one RPN event) |
1521 |
pChannel->ReleaseAllVoices(itControlChangeEvent); |
if (isNextEventCCNr(itControlChangeEvent, 38)) |
1522 |
} |
break; |
1523 |
// to prevent other MIDI CC #6 messages to be misenterpreted as RPN controller data |
|
1524 |
pChannel->ResetMidiRpnController(); |
int ch = itControlChangeEvent->Param.CC.Channel; |
1525 |
} else if (pChannel->GetMidiNrpnController() >= 0) { // NRPN controller number was sent previously ... |
int param = pChannel->GetMidiRpnParameter(); |
1526 |
dmsg(4,("Guess it's an NRPN ...\n")); |
int value = pChannel->GetMidiRpnData(); |
1527 |
const int NrpnCtrlMSB = pChannel->GetMidiNrpnController() >> 8; |
|
1528 |
const int NrpnCtrlLSB = pChannel->GetMidiNrpnController() & 0xff; |
// transform event type: CC event -> RPN event |
1529 |
dmsg(4,("NRPN MSB=%d LSB=%d Data=%d\n", NrpnCtrlMSB, NrpnCtrlLSB, itControlChangeEvent->Param.CC.Value)); |
itControlChangeEvent->Type = Event::type_rpn; |
1530 |
switch (NrpnCtrlMSB) { |
itControlChangeEvent->Param.RPN.Channel = ch; |
1531 |
case 0x1a: { // volume level of note (Roland GS NRPN) |
itControlChangeEvent->Param.RPN.Parameter = param; |
1532 |
const uint note = NrpnCtrlLSB; |
itControlChangeEvent->Param.RPN.Value = value; |
1533 |
const uint vol = itControlChangeEvent->Param.CC.Value; |
|
1534 |
dmsg(4,("Note Volume NRPN received (note=%d,vol=%d).\n", note, vol)); |
// if there's a RPN script handler, run it ... |
1535 |
if (note < 128 && vol < 128) |
if (pChannel->pScript->handlerRpn) { |
1536 |
pChannel->pMIDIKeyInfo[note].Volume = VolumeCurve[vol]; |
const event_id_t eventID = |
1537 |
break; |
pEventPool->getID(itControlChangeEvent); |
1538 |
} |
// run the RPN script handler |
1539 |
case 0x1c: { // panpot of note (Roland GS NRPN) |
ProcessEventByScript( |
1540 |
const uint note = NrpnCtrlLSB; |
pChannel, itControlChangeEvent, |
1541 |
const uint pan = itControlChangeEvent->Param.CC.Value; |
pChannel->pScript->handlerRpn |
1542 |
dmsg(4,("Note Pan NRPN received (note=%d,pan=%d).\n", note, pan)); |
); |
1543 |
if (note < 128 && pan < 128) { |
// if RPN event was dropped by script, abort |
1544 |
pChannel->pMIDIKeyInfo[note].PanLeft = PanCurve[128 - pan]; |
// here to avoid hard coded RPN processing below |
1545 |
pChannel->pMIDIKeyInfo[note].PanRight = PanCurve[pan]; |
if (!pEventPool->fromID(eventID)) |
|
} |
|
|
break; |
|
|
} |
|
|
case 0x1d: { // reverb send of note (Roland GS NRPN) |
|
|
const uint note = NrpnCtrlLSB; |
|
|
const float reverb = float(itControlChangeEvent->Param.CC.Value) / 127.0f; |
|
|
dmsg(4,("Note Reverb Send NRPN received (note=%d,send=%f).\n", note, reverb)); |
|
|
if (note < 128) |
|
|
pChannel->pMIDIKeyInfo[note].ReverbSend = reverb; |
|
1546 |
break; |
break; |
1547 |
} |
} |
1548 |
case 0x1e: { // chorus send of note (Roland GS NRPN) |
|
1549 |
const uint note = NrpnCtrlLSB; |
// do the actual (hard-coded) RPN value change processing |
1550 |
const float chorus = float(itControlChangeEvent->Param.CC.Value) / 127.0f; |
ProcessHardcodedRpn(pEngineChannel, itControlChangeEvent); |
1551 |
dmsg(4,("Note Chorus Send NRPN received (note=%d,send=%f).\n", note, chorus)); |
|
1552 |
if (note < 128) |
} else if (pChannel->GetMidiNrpnParameter() >= 0) { // NRPN parameter number was sent previously ... |
1553 |
pChannel->pMIDIKeyInfo[note].ChorusSend = chorus; |
pChannel->SetMidiNrpnDataMsb( |
1554 |
|
itControlChangeEvent->Param.CC.Value |
1555 |
|
); |
1556 |
|
bIsNrpn = true; |
1557 |
|
|
1558 |
|
// look-ahead: if next MIDI event is data entry LSB, |
1559 |
|
// then skip this event here for now (to avoid double |
1560 |
|
// handling of what's supposed to be one NRPN event) |
1561 |
|
if (isNextEventCCNr(itControlChangeEvent, 38)) |
1562 |
|
break; |
1563 |
|
|
1564 |
|
int ch = itControlChangeEvent->Param.CC.Channel; |
1565 |
|
int param = pChannel->GetMidiNrpnParameter(); |
1566 |
|
int value = pChannel->GetMidiNrpnData(); |
1567 |
|
|
1568 |
|
// transform event type: CC event -> NRPN event |
1569 |
|
itControlChangeEvent->Type = Event::type_nrpn; |
1570 |
|
itControlChangeEvent->Param.NRPN.Channel = ch; |
1571 |
|
itControlChangeEvent->Param.NRPN.Parameter = param; |
1572 |
|
itControlChangeEvent->Param.NRPN.Value = value; |
1573 |
|
|
1574 |
|
// if there's a NRPN script handler, run it ... |
1575 |
|
if (pChannel->pScript->handlerNrpn) { |
1576 |
|
const event_id_t eventID = |
1577 |
|
pEventPool->getID(itControlChangeEvent); |
1578 |
|
// run the NRPN script handler |
1579 |
|
ProcessEventByScript( |
1580 |
|
pChannel, itControlChangeEvent, |
1581 |
|
pChannel->pScript->handlerNrpn |
1582 |
|
); |
1583 |
|
// if NRPN event was dropped by script, abort |
1584 |
|
// here to avoid hard coded NRPN processing below |
1585 |
|
if (!pEventPool->fromID(eventID)) |
1586 |
break; |
break; |
|
} |
|
1587 |
} |
} |
1588 |
// to prevent other MIDI CC #6 messages to be misenterpreted as NRPN controller data |
|
1589 |
pChannel->ResetMidiNrpnController(); |
// do the actual (hard-coded) NRPN value change processing |
1590 |
|
ProcessHardcodedNrpn(pEngineChannel, itControlChangeEvent); |
1591 |
} |
} |
1592 |
break; |
break; |
1593 |
} |
} |
1602 |
pChannel->iLastPanRequest = itControlChangeEvent->Param.CC.Value; |
pChannel->iLastPanRequest = itControlChangeEvent->Param.CC.Value; |
1603 |
break; |
break; |
1604 |
} |
} |
1605 |
|
case 38: { // data entry (LSB) |
1606 |
|
//dmsg(1,("DATA ENTRY LSB %d\n", itControlChangeEvent->Param.CC.Value)); |
1607 |
|
if (pChannel->GetMidiRpnParameter() >= 0) { // RPN parameter number was sent previously ... |
1608 |
|
pChannel->SetMidiRpnDataLsb( |
1609 |
|
itControlChangeEvent->Param.CC.Value |
1610 |
|
); |
1611 |
|
bIsRpn = true; |
1612 |
|
|
1613 |
|
int ch = itControlChangeEvent->Param.CC.Channel; |
1614 |
|
int param = pChannel->GetMidiRpnParameter(); |
1615 |
|
int value = pChannel->GetMidiRpnData(); |
1616 |
|
|
1617 |
|
// transform event type: CC event -> RPN event |
1618 |
|
itControlChangeEvent->Type = Event::type_rpn; |
1619 |
|
itControlChangeEvent->Param.RPN.Channel = ch; |
1620 |
|
itControlChangeEvent->Param.RPN.Parameter = param; |
1621 |
|
itControlChangeEvent->Param.RPN.Value = value; |
1622 |
|
|
1623 |
|
// if there's a RPN script handler, run it ... |
1624 |
|
if (pChannel->pScript->handlerRpn) { |
1625 |
|
const event_id_t eventID = |
1626 |
|
pEventPool->getID(itControlChangeEvent); |
1627 |
|
// run the RPN script handler |
1628 |
|
ProcessEventByScript( |
1629 |
|
pChannel, itControlChangeEvent, |
1630 |
|
pChannel->pScript->handlerRpn |
1631 |
|
); |
1632 |
|
// if RPN event was dropped by script, abort |
1633 |
|
// here to avoid hard coded RPN processing below |
1634 |
|
if (!pEventPool->fromID(eventID)) |
1635 |
|
break; |
1636 |
|
} |
1637 |
|
|
1638 |
|
// do the actual (hard-coded) RPN value change processing |
1639 |
|
ProcessHardcodedRpn(pEngineChannel, itControlChangeEvent); |
1640 |
|
|
1641 |
|
} else if (pChannel->GetMidiNrpnParameter() >= 0) { // NRPN parameter number was sent previously ... |
1642 |
|
pChannel->SetMidiNrpnDataLsb( |
1643 |
|
itControlChangeEvent->Param.CC.Value |
1644 |
|
); |
1645 |
|
bIsNrpn = true; |
1646 |
|
|
1647 |
|
int ch = itControlChangeEvent->Param.CC.Channel; |
1648 |
|
int param = pChannel->GetMidiNrpnParameter(); |
1649 |
|
int value = pChannel->GetMidiNrpnData(); |
1650 |
|
|
1651 |
|
// transform event type: CC event -> NRPN event |
1652 |
|
itControlChangeEvent->Type = Event::type_nrpn; |
1653 |
|
itControlChangeEvent->Param.NRPN.Channel = ch; |
1654 |
|
itControlChangeEvent->Param.NRPN.Parameter = param; |
1655 |
|
itControlChangeEvent->Param.NRPN.Value = value; |
1656 |
|
|
1657 |
|
// if there's a NRPN script handler, run it ... |
1658 |
|
if (pChannel->pScript->handlerNrpn) { |
1659 |
|
const event_id_t eventID = |
1660 |
|
pEventPool->getID(itControlChangeEvent); |
1661 |
|
// run the NRPN script handler |
1662 |
|
ProcessEventByScript( |
1663 |
|
pChannel, itControlChangeEvent, |
1664 |
|
pChannel->pScript->handlerNrpn |
1665 |
|
); |
1666 |
|
// if NRPN event was dropped by script, abort |
1667 |
|
// here to avoid hard coded NRPN processing below |
1668 |
|
if (!pEventPool->fromID(eventID)) |
1669 |
|
break; |
1670 |
|
} |
1671 |
|
|
1672 |
|
// do the actual (hard-coded) NRPN value change processing |
1673 |
|
ProcessHardcodedNrpn(pEngineChannel, itControlChangeEvent); |
1674 |
|
} |
1675 |
|
break; |
1676 |
|
} |
1677 |
case 64: { // sustain |
case 64: { // sustain |
1678 |
if (itControlChangeEvent->Param.CC.Value >= 64 && !pChannel->SustainPedal) { |
if (itControlChangeEvent->Param.CC.Value >= 64 && !pChannel->SustainPedal) { |
1679 |
dmsg(4,("DAMPER (RIGHT) PEDAL DOWN\n")); |
dmsg(4,("DAMPER (RIGHT) PEDAL DOWN\n")); |
1747 |
} |
} |
1748 |
break; |
break; |
1749 |
} |
} |
1750 |
case 98: { // NRPN controller LSB |
case 98: { // NRPN parameter LSB |
1751 |
dmsg(4,("NRPN LSB %d\n", itControlChangeEvent->Param.CC.Value)); |
dmsg(4,("NRPN LSB %d\n", itControlChangeEvent->Param.CC.Value)); |
1752 |
pEngineChannel->SetMidiNrpnControllerLsb(itControlChangeEvent->Param.CC.Value); |
bIsNrpn = true; |
1753 |
|
pEngineChannel->SetMidiNrpnParameterLsb(itControlChangeEvent->Param.CC.Value); |
1754 |
break; |
break; |
1755 |
} |
} |
1756 |
case 99: { // NRPN controller MSB |
case 99: { // NRPN parameter MSB |
1757 |
dmsg(4,("NRPN MSB %d\n", itControlChangeEvent->Param.CC.Value)); |
dmsg(4,("NRPN MSB %d\n", itControlChangeEvent->Param.CC.Value)); |
1758 |
pEngineChannel->SetMidiNrpnControllerMsb(itControlChangeEvent->Param.CC.Value); |
bIsNrpn = true; |
1759 |
|
pEngineChannel->SetMidiNrpnParameterMsb(itControlChangeEvent->Param.CC.Value); |
1760 |
break; |
break; |
1761 |
} |
} |
1762 |
case 100: { // RPN controller LSB |
case 100: { // RPN parameter LSB |
1763 |
dmsg(4,("RPN LSB %d\n", itControlChangeEvent->Param.CC.Value)); |
dmsg(4,("RPN LSB %d\n", itControlChangeEvent->Param.CC.Value)); |
1764 |
pEngineChannel->SetMidiRpnControllerLsb(itControlChangeEvent->Param.CC.Value); |
bIsRpn = true; |
1765 |
|
pEngineChannel->SetMidiRpnParameterLsb(itControlChangeEvent->Param.CC.Value); |
1766 |
break; |
break; |
1767 |
} |
} |
1768 |
case 101: { // RPN controller MSB |
case 101: { // RPN parameter MSB |
1769 |
dmsg(4,("RPN MSB %d\n", itControlChangeEvent->Param.CC.Value)); |
dmsg(4,("RPN MSB %d\n", itControlChangeEvent->Param.CC.Value)); |
1770 |
pEngineChannel->SetMidiRpnControllerMsb(itControlChangeEvent->Param.CC.Value); |
bIsRpn = true; |
1771 |
|
pEngineChannel->SetMidiRpnParameterMsb(itControlChangeEvent->Param.CC.Value); |
1772 |
break; |
break; |
1773 |
} |
} |
1774 |
|
|
1802 |
break; |
break; |
1803 |
} |
} |
1804 |
} |
} |
1805 |
|
|
1806 |
|
// reset cached RPN/NRPN parameter number and data in case this |
1807 |
|
// CC event had nothing to do with RPN/NRPN |
1808 |
|
if (!bIsRpn && pChannel->GetMidiRpnParameter() >= 0) |
1809 |
|
pChannel->ResetMidiRpnParameter(); |
1810 |
|
if (!bIsNrpn && pChannel->GetMidiNrpnParameter() >= 0) |
1811 |
|
pChannel->ResetMidiNrpnParameter(); |
1812 |
|
} |
1813 |
|
|
1814 |
|
/** |
1815 |
|
* Process MIDI RPN events with hard coded behavior. |
1816 |
|
* |
1817 |
|
* @param pEngineChannel - engine channel on which the MIDI RPN |
1818 |
|
* event was received |
1819 |
|
* @param itRpnEvent - the actual MIDI RPN event |
1820 |
|
*/ |
1821 |
|
void ProcessHardcodedRpn(EngineChannel* pEngineChannel, |
1822 |
|
Pool<Event>::Iterator& itRpnEvent) |
1823 |
|
{ |
1824 |
|
EngineChannelBase<V, R, I>* pChannel = |
1825 |
|
static_cast<EngineChannelBase<V, R, I>*>(pEngineChannel); |
1826 |
|
|
1827 |
|
if (itRpnEvent->Param.RPN.Parameter == 2) { // coarse tuning in half tones |
1828 |
|
int transpose = (int) itRpnEvent->Param.RPN.ValueMSB() - 64; |
1829 |
|
// limit to +- two octaves for now |
1830 |
|
transpose = RTMath::Min(transpose, 24); |
1831 |
|
transpose = RTMath::Max(transpose, -24); |
1832 |
|
pChannel->GlobalTranspose = transpose; |
1833 |
|
// workaround, so we won't have hanging notes |
1834 |
|
pChannel->ReleaseAllVoices(itRpnEvent); |
1835 |
|
} |
1836 |
|
} |
1837 |
|
|
1838 |
|
/** |
1839 |
|
* Process MIDI NRPN events with hard coded behavior. |
1840 |
|
* |
1841 |
|
* @param pEngineChannel - engine channel on which the MIDI NRPN |
1842 |
|
* event was received |
1843 |
|
* @param itRpnEvent - the actual MIDI NRPN event |
1844 |
|
*/ |
1845 |
|
void ProcessHardcodedNrpn(EngineChannel* pEngineChannel, |
1846 |
|
Pool<Event>::Iterator& itNrpnEvent) |
1847 |
|
{ |
1848 |
|
EngineChannelBase<V, R, I>* pChannel = |
1849 |
|
static_cast<EngineChannelBase<V, R, I>*>(pEngineChannel); |
1850 |
|
|
1851 |
|
switch (itNrpnEvent->Param.NRPN.ParameterMSB()) { |
1852 |
|
case 0x1a: { // volume level of note (Roland GS NRPN) |
1853 |
|
const uint note = itNrpnEvent->Param.NRPN.ParameterLSB(); |
1854 |
|
const uint vol = itNrpnEvent->Param.NRPN.ValueMSB(); |
1855 |
|
dmsg(4,("Note Volume NRPN received (note=%d,vol=%d).\n", note, vol)); |
1856 |
|
if (note < 128 && vol < 128) |
1857 |
|
pChannel->pMIDIKeyInfo[note].Volume = VolumeCurve[vol]; |
1858 |
|
break; |
1859 |
|
} |
1860 |
|
case 0x1c: { // panpot of note (Roland GS NRPN) |
1861 |
|
const uint note = itNrpnEvent->Param.NRPN.ParameterLSB(); |
1862 |
|
const uint pan = itNrpnEvent->Param.NRPN.ValueMSB(); |
1863 |
|
dmsg(4,("Note Pan NRPN received (note=%d,pan=%d).\n", note, pan)); |
1864 |
|
if (note < 128 && pan < 128) { |
1865 |
|
pChannel->pMIDIKeyInfo[note].PanLeft = PanCurve[128 - pan]; |
1866 |
|
pChannel->pMIDIKeyInfo[note].PanRight = PanCurve[pan]; |
1867 |
|
} |
1868 |
|
break; |
1869 |
|
} |
1870 |
|
case 0x1d: { // reverb send of note (Roland GS NRPN) |
1871 |
|
const uint note = itNrpnEvent->Param.NRPN.ParameterLSB(); |
1872 |
|
const float reverb = float(itNrpnEvent->Param.NRPN.Value) / 16383.f; |
1873 |
|
dmsg(4,("Note Reverb Send NRPN received (note=%d,send=%f).\n", note, reverb)); |
1874 |
|
if (note < 128) |
1875 |
|
pChannel->pMIDIKeyInfo[note].ReverbSend = reverb; |
1876 |
|
break; |
1877 |
|
} |
1878 |
|
case 0x1e: { // chorus send of note (Roland GS NRPN) |
1879 |
|
const uint note = itNrpnEvent->Param.NRPN.ParameterLSB(); |
1880 |
|
const float chorus = float(itNrpnEvent->Param.NRPN.Value) / 16383.f; |
1881 |
|
dmsg(4,("Note Chorus Send NRPN received (note=%d,send=%f).\n", note, chorus)); |
1882 |
|
if (note < 128) |
1883 |
|
pChannel->pMIDIKeyInfo[note].ChorusSend = chorus; |
1884 |
|
break; |
1885 |
|
} |
1886 |
|
} |
1887 |
} |
} |
1888 |
|
|
1889 |
virtual D* CreateDiskThread() = 0; |
virtual D* CreateDiskThread() = 0; |
1894 |
* @param pEngineChannel - engine channel on which this event occurred on |
* @param pEngineChannel - engine channel on which this event occurred on |
1895 |
* @param itNoteOnEvent - key, velocity and time stamp of the event |
* @param itNoteOnEvent - key, velocity and time stamp of the event |
1896 |
*/ |
*/ |
1897 |
virtual void ProcessNoteOn(EngineChannel* pEngineChannel, Pool<Event>::Iterator& itNoteOnEvent) { |
virtual void ProcessNoteOn(EngineChannel* pEngineChannel, Pool<Event>::Iterator& itNoteOnEvent) OVERRIDE { |
1898 |
EngineChannelBase<V, R, I>* pChannel = |
EngineChannelBase<V, R, I>* pChannel = |
1899 |
static_cast<EngineChannelBase<V, R, I>*>(pEngineChannel); |
static_cast<EngineChannelBase<V, R, I>*>(pEngineChannel); |
1900 |
|
|
1904 |
|
|
1905 |
MidiKey* pKey = &pChannel->pMIDIKeyInfo[key]; |
MidiKey* pKey = &pChannel->pMIDIKeyInfo[key]; |
1906 |
|
|
1907 |
pChannel->listeners.PreProcessNoteOn(key, vel); |
// There are real MIDI note-on events (Event::type_note_on) and |
1908 |
|
// programmatically spawned notes (Event::type_play_note). We have |
1909 |
|
// to distinguish between them, since certain processing below |
1910 |
|
// must only be done on real MIDI note-on events (i.e. for |
1911 |
|
// correctly updating which MIDI keys are currently pressed down). |
1912 |
|
const bool isRealMIDINoteOnEvent = itNoteOnEvent->Type == Event::type_note_on; |
1913 |
|
|
1914 |
|
if (isRealMIDINoteOnEvent) |
1915 |
|
pChannel->listeners.PreProcessNoteOn(key, vel); |
1916 |
|
|
1917 |
#if !CONFIG_PROCESS_MUTED_CHANNELS |
#if !CONFIG_PROCESS_MUTED_CHANNELS |
1918 |
if (pEngineChannel->GetMute()) { // skip if sampler channel is muted |
if (pEngineChannel->GetMute()) { // skip if sampler channel is muted |
1919 |
pChannel->listeners.PostProcessNoteOn(key, vel); |
if (isRealMIDINoteOnEvent) |
1920 |
|
pChannel->listeners.PostProcessNoteOn(key, vel); |
1921 |
return; |
return; |
1922 |
} |
} |
1923 |
#endif |
#endif |
1924 |
|
|
1925 |
if (!pChannel->pInstrument) { |
if (!pChannel->pInstrument) { |
1926 |
pChannel->listeners.PostProcessNoteOn(key, vel); |
if (isRealMIDINoteOnEvent) |
1927 |
|
pChannel->listeners.PostProcessNoteOn(key, vel); |
1928 |
return; // ignore if no instrument loaded |
return; // ignore if no instrument loaded |
1929 |
} |
} |
1930 |
|
|
1932 |
RTList<Event>::Iterator itNoteOnEventOnKeyList = itNoteOnEvent.moveToEndOf(pKey->pEvents); |
RTList<Event>::Iterator itNoteOnEventOnKeyList = itNoteOnEvent.moveToEndOf(pKey->pEvents); |
1933 |
|
|
1934 |
// if Solo Mode then kill all already active voices |
// if Solo Mode then kill all already active voices |
1935 |
if (pChannel->SoloMode) { |
if (pChannel->SoloMode && isRealMIDINoteOnEvent) { |
1936 |
Pool<uint>::Iterator itYoungestKey = pChannel->pActiveKeys->last(); |
Pool<uint>::Iterator itYoungestKey = pChannel->pActiveKeys->last(); |
1937 |
if (itYoungestKey) { |
if (itYoungestKey) { |
1938 |
const int iYoungestKey = *itYoungestKey; |
const int iYoungestKey = *itYoungestKey; |
1961 |
pChannel->SoloKey = key; |
pChannel->SoloKey = key; |
1962 |
} |
} |
1963 |
|
|
1964 |
pChannel->ProcessKeySwitchChange(key); |
if (isRealMIDINoteOnEvent) { |
1965 |
|
pChannel->ProcessKeySwitchChange(key); |
1966 |
|
|
1967 |
pKey->KeyPressed = true; // the MIDI key was now pressed down |
pKey->KeyPressed = true; // the MIDI key was now pressed down |
1968 |
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 |
1969 |
pKey->Velocity = itNoteOnEventOnKeyList->Param.Note.Velocity; |
pKey->Velocity = itNoteOnEventOnKeyList->Param.Note.Velocity; |
1970 |
pKey->NoteOnTime = FrameTime + itNoteOnEventOnKeyList->FragmentPos(); // will be used to calculate note length |
pKey->NoteOnTime = FrameTime + itNoteOnEventOnKeyList->FragmentPos(); // will be used to calculate note length |
1971 |
|
} |
1972 |
|
|
1973 |
// cancel release process of voices on this key if needed |
// cancel release process of voices on this key if needed |
1974 |
if (pKey->Active && !pChannel->SustainPedal) { |
if (pKey->Active && !pChannel->SustainPedal && isRealMIDINoteOnEvent) { |
1975 |
RTList<Event>::Iterator itCancelReleaseEvent = pKey->pEvents->allocAppend(); |
RTList<Event>::Iterator itCancelReleaseEvent = pKey->pEvents->allocAppend(); |
1976 |
if (itCancelReleaseEvent) { |
if (itCancelReleaseEvent) { |
1977 |
*itCancelReleaseEvent = *itNoteOnEventOnKeyList; // copy event |
*itCancelReleaseEvent = *itNoteOnEventOnKeyList; // copy event |
1978 |
itCancelReleaseEvent->Type = Event::type_cancel_release; // transform event type |
itCancelReleaseEvent->Type = Event::type_cancel_release_key; // transform event type |
1979 |
} |
} |
1980 |
else dmsg(1,("Event pool emtpy!\n")); |
else dmsg(1,("Event pool emtpy!\n")); |
1981 |
} |
} |
1986 |
if (!pKey->Active && !pKey->VoiceTheftsQueued) |
if (!pKey->Active && !pKey->VoiceTheftsQueued) |
1987 |
pKey->pEvents->free(itNoteOnEventOnKeyList); |
pKey->pEvents->free(itNoteOnEventOnKeyList); |
1988 |
|
|
1989 |
if (!pChannel->SoloMode || pChannel->PortamentoPos < 0.0f) pChannel->PortamentoPos = (float) key; |
if (isRealMIDINoteOnEvent && (!pChannel->SoloMode || pChannel->PortamentoPos < 0.0f)) |
1990 |
|
pChannel->PortamentoPos = (float) key; |
1991 |
|
|
1992 |
|
//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? |
1993 |
if (pKey->pRoundRobinIndex) { |
if (pKey->pRoundRobinIndex) { |
1994 |
(*pKey->pRoundRobinIndex)++; // counter specific for the key or region |
(*pKey->pRoundRobinIndex)++; // counter specific for the key or region |
1995 |
pChannel->RoundRobinIndex++; // common counter for the channel |
pChannel->RoundRobinIndex++; // common counter for the channel |
1996 |
} |
} |
1997 |
pChannel->listeners.PostProcessNoteOn(key, vel); |
|
1998 |
|
if (isRealMIDINoteOnEvent) |
1999 |
|
pChannel->listeners.PostProcessNoteOn(key, vel); |
2000 |
} |
} |
2001 |
|
|
2002 |
/** |
/** |
2025 |
* @param pEngineChannel - engine channel on which this event occurred on |
* @param pEngineChannel - engine channel on which this event occurred on |
2026 |
* @param itNoteOffEvent - key, velocity and time stamp of the event |
* @param itNoteOffEvent - key, velocity and time stamp of the event |
2027 |
*/ |
*/ |
2028 |
virtual void ProcessNoteOff(EngineChannel* pEngineChannel, Pool<Event>::Iterator& itNoteOffEvent) { |
virtual void ProcessNoteOff(EngineChannel* pEngineChannel, Pool<Event>::Iterator& itNoteOffEvent) OVERRIDE { |
2029 |
EngineChannelBase<V, R, I>* pChannel = static_cast<EngineChannelBase<V, R, I>*>(pEngineChannel); |
EngineChannelBase<V, R, I>* pChannel = static_cast<EngineChannelBase<V, R, I>*>(pEngineChannel); |
2030 |
|
|
2031 |
const int iKey = itNoteOffEvent->Param.Note.Key; |
const int iKey = itNoteOffEvent->Param.Note.Key; |
2034 |
|
|
2035 |
MidiKey* pKey = &pChannel->pMIDIKeyInfo[iKey]; |
MidiKey* pKey = &pChannel->pMIDIKeyInfo[iKey]; |
2036 |
|
|
2037 |
pChannel->listeners.PreProcessNoteOff(iKey, vel); |
// There are real MIDI note-off events (Event::type_note_off) and |
2038 |
|
// programmatically spawned notes (Event::type_stop_note). We have |
2039 |
|
// to distinguish between them, since certain processing below |
2040 |
|
// must only be done on real MIDI note-off events (i.e. for |
2041 |
|
// correctly updating which MIDI keys are currently pressed down), |
2042 |
|
// plus a stop-note event just releases voices of one particular |
2043 |
|
// note, whereas a note-off event releases all voices on a |
2044 |
|
// particular MIDI key instead. |
2045 |
|
const bool isRealMIDINoteOffEvent = itNoteOffEvent->Type == Event::type_note_off; |
2046 |
|
|
2047 |
|
if (isRealMIDINoteOffEvent) |
2048 |
|
pChannel->listeners.PreProcessNoteOff(iKey, vel); |
2049 |
|
|
2050 |
#if !CONFIG_PROCESS_MUTED_CHANNELS |
#if !CONFIG_PROCESS_MUTED_CHANNELS |
2051 |
if (pEngineChannel->GetMute()) { // skip if sampler channel is muted |
if (pEngineChannel->GetMute()) { // skip if sampler channel is muted |
2052 |
pChannel->listeners.PostProcessNoteOff(iKey, vel); |
if (isRealMIDINoteOffEvent) |
2053 |
|
pChannel->listeners.PostProcessNoteOff(iKey, vel); |
2054 |
return; |
return; |
2055 |
} |
} |
2056 |
#endif |
#endif |
2057 |
|
|
2058 |
pKey->KeyPressed = false; // the MIDI key was now released |
if (isRealMIDINoteOffEvent) { |
2059 |
pChannel->KeyDown[iKey] = false; // just used as built-in %KEY_DOWN script variable |
pKey->KeyPressed = false; // the MIDI key was now released |
2060 |
|
pChannel->KeyDown[iKey] = false; // just used as built-in %KEY_DOWN script variable |
2061 |
|
} |
2062 |
|
|
2063 |
// move event to the key's own event list |
// move event to the key's own event list |
2064 |
RTList<Event>::Iterator itNoteOffEventOnKeyList = itNoteOffEvent.moveToEndOf(pKey->pEvents); |
RTList<Event>::Iterator itNoteOffEventOnKeyList = itNoteOffEvent.moveToEndOf(pKey->pEvents); |
2065 |
|
|
2066 |
bool bShouldRelease = pKey->Active && pChannel->ShouldReleaseVoice(itNoteOffEventOnKeyList->Param.Note.Key); |
if (isRealMIDINoteOffEvent) { |
2067 |
|
bool bShouldRelease = pKey->Active && pChannel->ShouldReleaseVoice(itNoteOffEventOnKeyList->Param.Note.Key); |
2068 |
|
|
2069 |
// 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) |
2070 |
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 |
2071 |
bool bOtherKeysPressed = false; |
bool bOtherKeysPressed = false; |
2072 |
if (iKey == pChannel->SoloKey) { |
if (iKey == pChannel->SoloKey) { |
2073 |
pChannel->SoloKey = -1; |
pChannel->SoloKey = -1; |
2074 |
// 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 |
2075 |
for (int i = 127; i > 0; i--) { |
for (int i = 127; i > 0; i--) { |
2076 |
MidiKey* pOtherKey = &pChannel->pMIDIKeyInfo[i]; |
MidiKey* pOtherKey = &pChannel->pMIDIKeyInfo[i]; |
2077 |
if (pOtherKey->KeyPressed) { |
if (pOtherKey->KeyPressed) { |
2078 |
bOtherKeysPressed = true; |
bOtherKeysPressed = true; |
2079 |
// make the other key the new 'currently active solo key' |
// make the other key the new 'currently active solo key' |
2080 |
pChannel->SoloKey = i; |
pChannel->SoloKey = i; |
2081 |
// get final portamento position of currently active voice |
// get final portamento position of currently active voice |
2082 |
if (pChannel->PortamentoMode) { |
if (pChannel->PortamentoMode) { |
2083 |
NoteIterator itNote = pKey->pActiveNotes->first(); |
NoteIterator itNote = pKey->pActiveNotes->first(); |
2084 |
VoiceIterator itVoice = itNote->pActiveVoices->first(); |
VoiceIterator itVoice = itNote->pActiveVoices->first(); |
2085 |
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); |
|
2086 |
} |
} |
2087 |
// if neither a voice was spawned or postponed then remove note on event from key again |
// create a pseudo note on event |
2088 |
if (!pOtherKey->Active && !pOtherKey->VoiceTheftsQueued) |
RTList<Event>::Iterator itPseudoNoteOnEvent = pOtherKey->pEvents->allocAppend(); |
2089 |
pOtherKey->pEvents->free(itPseudoNoteOnEvent); |
if (itPseudoNoteOnEvent) { |
2090 |
|
// copy event |
2091 |
|
*itPseudoNoteOnEvent = *itNoteOffEventOnKeyList; |
2092 |
|
// transform event to a note on event |
2093 |
|
itPseudoNoteOnEvent->Type = Event::type_note_on; //FIXME: should probably use Event::type_play_note instead (to avoid i.e. hanging notes) |
2094 |
|
itPseudoNoteOnEvent->Param.Note.Key = i; |
2095 |
|
itPseudoNoteOnEvent->Param.Note.Velocity = pOtherKey->Velocity; |
2096 |
|
// assign a new note to this note-on event |
2097 |
|
if (LaunchNewNote(pChannel, itPseudoNoteOnEvent)) { |
2098 |
|
// allocate and trigger new voice(s) for the other key |
2099 |
|
TriggerNewVoices(pChannel, itPseudoNoteOnEvent, false); |
2100 |
|
} |
2101 |
|
// if neither a voice was spawned or postponed then remove note on event from key again |
2102 |
|
if (!pOtherKey->Active && !pOtherKey->VoiceTheftsQueued) |
2103 |
|
pOtherKey->pEvents->free(itPseudoNoteOnEvent); |
2104 |
|
|
2105 |
} else dmsg(1,("Could not respawn voice, no free event left\n")); |
} else dmsg(1,("Could not respawn voice, no free event left\n")); |
2106 |
break; // done |
break; // done |
2107 |
|
} |
2108 |
} |
} |
2109 |
} |
} |
2110 |
} |
if (bOtherKeysPressed) { |
2111 |
if (bOtherKeysPressed) { |
if (pKey->Active) { // kill all voices on this key |
2112 |
if (pKey->Active) { // kill all voices on this key |
bShouldRelease = false; // no need to release, as we kill it here |
2113 |
bShouldRelease = false; // no need to release, as we kill it here |
for (NoteIterator itNote = pKey->pActiveNotes->first(); itNote; ++itNote) { |
2114 |
for (NoteIterator itNote = pKey->pActiveNotes->first(); itNote; ++itNote) { |
VoiceIterator itVoiceToBeKilled = itNote->pActiveVoices->first(); |
2115 |
VoiceIterator itVoiceToBeKilled = itNote->pActiveVoices->first(); |
VoiceIterator end = itNote->pActiveVoices->end(); |
2116 |
VoiceIterator end = itNote->pActiveVoices->end(); |
for (; itVoiceToBeKilled != end; ++itVoiceToBeKilled) { |
2117 |
for (; itVoiceToBeKilled != end; ++itVoiceToBeKilled) { |
if (!(itVoiceToBeKilled->Type & Voice::type_release_trigger)) |
2118 |
if (!(itVoiceToBeKilled->Type & Voice::type_release_trigger)) |
itVoiceToBeKilled->Kill(itNoteOffEventOnKeyList); |
2119 |
itVoiceToBeKilled->Kill(itNoteOffEventOnKeyList); |
} |
2120 |
} |
} |
2121 |
} |
} |
2122 |
} |
} else pChannel->PortamentoPos = -1.0f; |
2123 |
} 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 |
|
2124 |
|
|
2125 |
// 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 |
2126 |
if (pKey->ReleaseTrigger && pChannel->pInstrument) { |
if (bShouldRelease) { |
2127 |
// assign a new note to this release event |
itNoteOffEventOnKeyList->Type = Event::type_release_key; // transform event type |
2128 |
if (LaunchNewNote(pChannel, &*itNoteOffEventOnKeyList)) { |
// spawn release triggered voice(s) if needed |
2129 |
// allocate and trigger new release voice(s) |
if (pKey->ReleaseTrigger & release_trigger_noteoff) |
2130 |
TriggerReleaseVoices(pChannel, itNoteOffEventOnKeyList); |
ProcessReleaseTrigger(pChannel, itNoteOffEventOnKeyList, pKey); |
2131 |
} |
} |
2132 |
pKey->ReleaseTrigger = false; |
} else if (itNoteOffEventOnKeyList->Type == Event::type_stop_note) { |
2133 |
|
// This programmatically caused event is caused by a call to |
2134 |
|
// the built-in instrument script function note_off(). In |
2135 |
|
// contrast to a real MIDI note-off event the stop-note |
2136 |
|
// event just intends to release voices of one particular note. |
2137 |
|
NoteBase* pNote = pChannel->pEngine->NoteByID( itNoteOffEventOnKeyList->Param.Note.ID ); |
2138 |
|
if (pNote) { // the requested note is still alive ... |
2139 |
|
itNoteOffEventOnKeyList->Type = Event::type_release_note; // transform event type |
2140 |
|
} else { // note is dead and gone .. |
2141 |
|
pKey->pEvents->free(itNoteOffEventOnKeyList); // remove stop-note event from key again |
2142 |
|
return; // prevent event to be removed a 2nd time below |
2143 |
} |
} |
2144 |
} |
} |
2145 |
|
|
2147 |
if (!pKey->Active && !pKey->VoiceTheftsQueued) |
if (!pKey->Active && !pKey->VoiceTheftsQueued) |
2148 |
pKey->pEvents->free(itNoteOffEventOnKeyList); |
pKey->pEvents->free(itNoteOffEventOnKeyList); |
2149 |
|
|
2150 |
pChannel->listeners.PostProcessNoteOff(iKey, vel); |
if (isRealMIDINoteOffEvent) |
2151 |
|
pChannel->listeners.PostProcessNoteOff(iKey, vel); |
2152 |
|
} |
2153 |
|
|
2154 |
|
/** |
2155 |
|
* Called on sustain pedal up events to check and if required, |
2156 |
|
* launch release trigger voices on the respective active key. |
2157 |
|
* |
2158 |
|
* @param pEngineChannel - engine channel on which this event occurred on |
2159 |
|
* @param itEvent - release trigger event (contains note number) |
2160 |
|
*/ |
2161 |
|
virtual void ProcessReleaseTriggerBySustain(EngineChannel* pEngineChannel, RTList<Event>::Iterator& itEvent) OVERRIDE { |
2162 |
|
EngineChannelBase<V, R, I>* pChannel = static_cast<EngineChannelBase<V, R, I>*>(pEngineChannel); |
2163 |
|
|
2164 |
|
const int iKey = itEvent->Param.Note.Key; |
2165 |
|
if (iKey < 0 || iKey > 127) return; // ignore event, key outside allowed key range |
2166 |
|
|
2167 |
|
MidiKey* pKey = &pChannel->pMIDIKeyInfo[iKey]; |
2168 |
|
|
2169 |
|
ProcessReleaseTrigger(pChannel, itEvent, pKey); |
2170 |
|
} |
2171 |
|
|
2172 |
|
/** |
2173 |
|
* Called on note-off and sustain pedal up events to check and if |
2174 |
|
* required, launch release trigger voices on the respective active |
2175 |
|
* key. |
2176 |
|
* |
2177 |
|
* @param pEngineChannel - engine channel on which this event occurred on |
2178 |
|
* @param itEvent - note off event / release trigger event |
2179 |
|
* @param pKey - key on which the release trigger voices shall be spawned |
2180 |
|
*/ |
2181 |
|
inline void ProcessReleaseTrigger(EngineChannelBase<V, R, I>* pChannel, RTList<Event>::Iterator& itEvent, MidiKey* pKey) { |
2182 |
|
// spawn release triggered voice(s) if needed |
2183 |
|
if (pKey->ReleaseTrigger && pChannel->pInstrument) { |
2184 |
|
// assign a new note to this release event |
2185 |
|
if (LaunchNewNote(pChannel, itEvent)) { |
2186 |
|
// allocate and trigger new release voice(s) |
2187 |
|
TriggerReleaseVoices(pChannel, itEvent); |
2188 |
|
} |
2189 |
|
pKey->ReleaseTrigger = release_trigger_none; |
2190 |
|
} |
2191 |
|
} |
2192 |
|
|
2193 |
|
/** |
2194 |
|
* Called on "kill note" events, which currently only happens on |
2195 |
|
* built-in real-time instrument script function fade_out(). This |
2196 |
|
* method only fulfills one task: moving the even to the Note's own |
2197 |
|
* event list so that its voices can process the kill event sample |
2198 |
|
* accurately. |
2199 |
|
*/ |
2200 |
|
void ProcessKillNote(EngineChannel* pEngineChannel, RTList<Event>::Iterator& itEvent) { |
2201 |
|
EngineChannelBase<V, R, I>* pChannel = static_cast<EngineChannelBase<V, R, I>*>(pEngineChannel); |
2202 |
|
|
2203 |
|
NoteBase* pNote = pChannel->pEngine->NoteByID( itEvent->Param.Note.ID ); |
2204 |
|
if (!pNote || pNote->hostKey < 0 || pNote->hostKey >= 128) return; |
2205 |
|
|
2206 |
|
// move note kill event to its MIDI key |
2207 |
|
MidiKey* pKey = &pChannel->pMIDIKeyInfo[pNote->hostKey]; |
2208 |
|
itEvent.moveToEndOf(pKey->pEvents); |
2209 |
|
} |
2210 |
|
|
2211 |
|
/** |
2212 |
|
* Called on note synthesis parameter change events. These are |
2213 |
|
* internal events caused by calling built-in real-time instrument |
2214 |
|
* script functions like change_vol(), change_tune(), etc. |
2215 |
|
* |
2216 |
|
* This method performs two tasks: |
2217 |
|
* |
2218 |
|
* - It converts the event's relative values changes (Deltas) to |
2219 |
|
* the respective final new synthesis parameter value (AbsValue), |
2220 |
|
* for that particular moment of the event that is. |
2221 |
|
* |
2222 |
|
* - It moves the individual events to the Note's own event list |
2223 |
|
* (or actually to the event list of the MIDI key), so that |
2224 |
|
* voices can process those events sample accurately. |
2225 |
|
* |
2226 |
|
* @param pEngineChannel - engine channel on which this event occurred on |
2227 |
|
* @param itEvent - note synthesis parameter change event |
2228 |
|
*/ |
2229 |
|
virtual void ProcessNoteSynthParam(EngineChannel* pEngineChannel, RTList<Event>::Iterator& itEvent) { |
2230 |
|
EngineChannelBase<V, R, I>* pChannel = static_cast<EngineChannelBase<V, R, I>*>(pEngineChannel); |
2231 |
|
|
2232 |
|
NoteBase* pNote = pChannel->pEngine->NoteByID( itEvent->Param.NoteSynthParam.NoteID ); |
2233 |
|
if (!pNote || pNote->hostKey < 0 || pNote->hostKey >= 128) return; |
2234 |
|
|
2235 |
|
switch (itEvent->Param.NoteSynthParam.Type) { |
2236 |
|
case Event::synth_param_volume: |
2237 |
|
pNote->apply(itEvent, &NoteBase::_Override::Volume); |
2238 |
|
break; |
2239 |
|
case Event::synth_param_volume_time: |
2240 |
|
pNote->Override.VolumeTime = itEvent->Param.NoteSynthParam.AbsValue = itEvent->Param.NoteSynthParam.Delta; |
2241 |
|
break; |
2242 |
|
case Event::synth_param_volume_curve: |
2243 |
|
itEvent->Param.NoteSynthParam.AbsValue = itEvent->Param.NoteSynthParam.Delta; |
2244 |
|
pNote->Override.VolumeCurve = (fade_curve_t) itEvent->Param.NoteSynthParam.AbsValue; |
2245 |
|
break; |
2246 |
|
case Event::synth_param_pitch: |
2247 |
|
pNote->apply(itEvent, &NoteBase::_Override::Pitch); |
2248 |
|
break; |
2249 |
|
case Event::synth_param_pitch_time: |
2250 |
|
pNote->Override.PitchTime = itEvent->Param.NoteSynthParam.AbsValue = itEvent->Param.NoteSynthParam.Delta; |
2251 |
|
break; |
2252 |
|
case Event::synth_param_pitch_curve: |
2253 |
|
itEvent->Param.NoteSynthParam.AbsValue = itEvent->Param.NoteSynthParam.Delta; |
2254 |
|
pNote->Override.PitchCurve = (fade_curve_t) itEvent->Param.NoteSynthParam.AbsValue; |
2255 |
|
break; |
2256 |
|
case Event::synth_param_pan: |
2257 |
|
pNote->apply(itEvent, &NoteBase::_Override::Pan); |
2258 |
|
break; |
2259 |
|
case Event::synth_param_pan_time: |
2260 |
|
pNote->Override.PanTime = itEvent->Param.NoteSynthParam.AbsValue = itEvent->Param.NoteSynthParam.Delta; |
2261 |
|
break; |
2262 |
|
case Event::synth_param_pan_curve: |
2263 |
|
itEvent->Param.NoteSynthParam.AbsValue = itEvent->Param.NoteSynthParam.Delta; |
2264 |
|
pNote->Override.PanCurve = (fade_curve_t) itEvent->Param.NoteSynthParam.AbsValue; |
2265 |
|
break; |
2266 |
|
case Event::synth_param_cutoff: |
2267 |
|
pNote->apply(itEvent, &NoteBase::_Override::Cutoff); |
2268 |
|
break; |
2269 |
|
case Event::synth_param_resonance: |
2270 |
|
pNote->apply(itEvent, &NoteBase::_Override::Resonance); |
2271 |
|
break; |
2272 |
|
case Event::synth_param_attack: |
2273 |
|
pNote->apply(itEvent, &NoteBase::_Override::Attack); |
2274 |
|
break; |
2275 |
|
case Event::synth_param_decay: |
2276 |
|
pNote->apply(itEvent, &NoteBase::_Override::Decay); |
2277 |
|
break; |
2278 |
|
case Event::synth_param_sustain: |
2279 |
|
pNote->apply(itEvent, &NoteBase::_Override::Sustain); |
2280 |
|
break; |
2281 |
|
case Event::synth_param_release: |
2282 |
|
pNote->apply(itEvent, &NoteBase::_Override::Release); |
2283 |
|
break; |
2284 |
|
|
2285 |
|
case Event::synth_param_cutoff_attack: |
2286 |
|
pNote->apply(itEvent, &NoteBase::_Override::CutoffAttack); |
2287 |
|
break; |
2288 |
|
case Event::synth_param_cutoff_decay: |
2289 |
|
pNote->apply(itEvent, &NoteBase::_Override::CutoffDecay); |
2290 |
|
break; |
2291 |
|
case Event::synth_param_cutoff_sustain: |
2292 |
|
pNote->apply(itEvent, &NoteBase::_Override::CutoffSustain); |
2293 |
|
break; |
2294 |
|
case Event::synth_param_cutoff_release: |
2295 |
|
pNote->apply(itEvent, &NoteBase::_Override::CutoffRelease); |
2296 |
|
break; |
2297 |
|
|
2298 |
|
case Event::synth_param_amp_lfo_depth: |
2299 |
|
pNote->apply(itEvent, &NoteBase::_Override::AmpLFODepth); |
2300 |
|
break; |
2301 |
|
case Event::synth_param_amp_lfo_freq: |
2302 |
|
pNote->apply(itEvent, &NoteBase::_Override::AmpLFOFreq); |
2303 |
|
break; |
2304 |
|
case Event::synth_param_cutoff_lfo_depth: |
2305 |
|
pNote->apply(itEvent, &NoteBase::_Override::CutoffLFODepth); |
2306 |
|
break; |
2307 |
|
case Event::synth_param_cutoff_lfo_freq: |
2308 |
|
pNote->apply(itEvent, &NoteBase::_Override::CutoffLFOFreq); |
2309 |
|
break; |
2310 |
|
case Event::synth_param_pitch_lfo_depth: |
2311 |
|
pNote->apply(itEvent, &NoteBase::_Override::PitchLFODepth); |
2312 |
|
break; |
2313 |
|
case Event::synth_param_pitch_lfo_freq: |
2314 |
|
pNote->apply(itEvent, &NoteBase::_Override::PitchLFOFreq); |
2315 |
|
break; |
2316 |
|
} |
2317 |
|
|
2318 |
|
// move note parameter event to its MIDI key |
2319 |
|
MidiKey* pKey = &pChannel->pMIDIKeyInfo[pNote->hostKey]; |
2320 |
|
itEvent.moveToEndOf(pKey->pEvents); |
2321 |
} |
} |
2322 |
|
|
2323 |
/** |
/** |
2324 |
* 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 |
2325 |
* control and status variables. This method is protected by a mutex. |
* control and status variables. This method is protected by a mutex. |
2326 |
*/ |
*/ |
2327 |
virtual void ResetInternal() { |
virtual void ResetInternal() OVERRIDE { |
2328 |
LockGuard lock(ResetInternalMutex); |
LockGuard lock(ResetInternalMutex); |
2329 |
|
|
2330 |
// make sure that the engine does not get any sysex messages |
// make sure that the engine does not get any sysex messages |
2383 |
* @param pEngineChannel - engine channel on which all voices should be killed |
* @param pEngineChannel - engine channel on which all voices should be killed |
2384 |
* @param itKillEvent - event which caused this killing of all voices |
* @param itKillEvent - event which caused this killing of all voices |
2385 |
*/ |
*/ |
2386 |
virtual void KillAllVoices(EngineChannel* pEngineChannel, Pool<Event>::Iterator& itKillEvent) { |
virtual void KillAllVoices(EngineChannel* pEngineChannel, Pool<Event>::Iterator& itKillEvent) OVERRIDE { |
2387 |
EngineChannelBase<V, R, I>* pChannel = static_cast<EngineChannelBase<V, R, I>*>(pEngineChannel); |
EngineChannelBase<V, R, I>* pChannel = static_cast<EngineChannelBase<V, R, I>*>(pEngineChannel); |
2388 |
int count = pChannel->KillAllVoices(itKillEvent); |
int count = pChannel->KillAllVoices(itKillEvent); |
2389 |
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 |
2418 |
bool HandleKeyGroupConflicts |
bool HandleKeyGroupConflicts |
2419 |
) = 0; |
) = 0; |
2420 |
|
|
2421 |
virtual int GetMinFadeOutSamples() { return MinFadeOutSamples; } |
virtual int GetMinFadeOutSamples() OVERRIDE { return MinFadeOutSamples; } |
2422 |
|
|
2423 |
int InitNewVoice ( |
int InitNewVoice ( |
2424 |
EngineChannelBase<V, R, I>* pChannel, |
EngineChannelBase<V, R, I>* pChannel, |
2441 |
} |
} |
2442 |
else { // on success |
else { // on success |
2443 |
--VoiceSpawnsLeft; |
--VoiceSpawnsLeft; |
2444 |
if (!pKey->Active) { // mark as active key |
|
2445 |
pKey->Active = true; |
// should actually be superfluous now, since this is |
2446 |
pKey->itSelf = pChannel->pActiveKeys->allocAppend(); |
// already done in LaunchNewNote() |
2447 |
*pKey->itSelf = itNoteOnEvent->Param.Note.Key; |
pChannel->markKeyAsActive(pKey); |
2448 |
} |
|
2449 |
if (itNewVoice->Type & Voice::type_release_trigger_required) pKey->ReleaseTrigger = true; // mark key for the need of release triggered voice(s) |
if (itNewVoice->Type & Voice::type_release_trigger_required) |
2450 |
|
pKey->ReleaseTrigger |= itNewVoice->GetReleaseTriggerFlags(); // mark key for the need of release triggered voice(s) |
2451 |
return 0; // success |
return 0; // success |
2452 |
} |
} |
2453 |
} |
} |