--- linuxsampler/trunk/src/engines/EngineBase.h 2016/04/19 14:07:53 2879 +++ linuxsampler/trunk/src/engines/EngineBase.h 2016/07/09 14:38:33 2931 @@ -771,8 +771,12 @@ // // 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 for (RTList::Iterator itEvent = pChannel->pEvents->first(), - end = pChannel->pEvents->end(); itEvent != end; ++itEvent) + end = pChannel->pEvents->end(); itEvent != end; ) { + //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). + RTList::Iterator itNext = itEvent; + ++itNext; + switch (itEvent->Type) { case Event::type_note_on: if (pChannel->pScript->handlerNote) @@ -792,6 +796,9 @@ //TODO: ... break; } + + // see HACK comment above + itEvent = itNext; } // this has to be run again, since the newly spawned scripts @@ -866,6 +873,10 @@ dmsg(5,("Engine: Pitchbend received\n")); ProcessPitchbend(static_cast(itEvent->pEngineChannel), itEvent); break; + case Event::type_note_synth_param: + dmsg(5,("Engine: Note Synth Param received\n")); + ProcessNoteSynthParam(itEvent->pEngineChannel, itEvent); + break; } } } @@ -1780,16 +1791,8 @@ // 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 - // spawn release triggered voice(s) if needed - if (pKey->ReleaseTrigger && pChannel->pInstrument) { - // assign a new note to this release event - if (LaunchNewNote(pChannel, &*itNoteOffEventOnKeyList)) { - // allocate and trigger new release voice(s) - TriggerReleaseVoices(pChannel, itNoteOffEventOnKeyList); - } - pKey->ReleaseTrigger = false; - } + ProcessReleaseTrigger(pChannel, itNoteOffEventOnKeyList, pKey); } // if neither a voice was spawned or postponed on this key then remove note off event from key again @@ -1800,6 +1803,102 @@ } /** + * Called on sustain pedal up events to check and if required, + * launch release trigger voices on the respective active key. + * + * @param pEngineChannel - engine channel on which this event occurred on + * @param itEvent - release trigger event (contains note number) + */ + virtual void ProcessReleaseTrigger(EngineChannel* pEngineChannel, RTList::Iterator& itEvent) OVERRIDE { + EngineChannelBase* pChannel = static_cast*>(pEngineChannel); + + const int iKey = itEvent->Param.Note.Key; + if (iKey < 0 || iKey > 127) return; // ignore event, key outside allowed key range + + MidiKey* pKey = &pChannel->pMIDIKeyInfo[iKey]; + + ProcessReleaseTrigger(pChannel, itEvent, pKey); + } + + /** + * Called on note-off and sustain pedal up events to check and if + * required, launch release trigger voices on the respective active + * key. + * + * @param pEngineChannel - engine channel on which this event occurred on + * @param itEvent - note off event / release trigger event + * @param pKey - key on which the release trigger voices shall be spawned + */ + inline void ProcessReleaseTrigger(EngineChannelBase* pChannel, RTList::Iterator& itEvent, MidiKey* pKey) { + // spawn release triggered voice(s) if needed + if (pKey->ReleaseTrigger && pChannel->pInstrument) { + // assign a new note to this release event + if (LaunchNewNote(pChannel, &*itEvent)) { + // allocate and trigger new release voice(s) + TriggerReleaseVoices(pChannel, itEvent); + } + pKey->ReleaseTrigger = false; + } + } + + /** + * Called on note synthesis parameter change events. These are + * internal events caused by calling built-in real-time instrument + * script functions like change_vol(), change_pitch(), etc. + * + * This method performs two tasks: + * + * - It converts the event's relative values changes (Deltas) to + * the respective final new synthesis parameter value (AbsValue), + * for that particular moment of the event that is. + * + * - It moves the individual events to the Note's own event list + * (or actually to the event list of the MIDI key), so that + * voices can process those events sample accurately. + * + * @param pEngineChannel - engine channel on which this event occurred on + * @param itEvent - note synthesis parameter change event + */ + virtual void ProcessNoteSynthParam(EngineChannel* pEngineChannel, RTList::Iterator& itEvent) OVERRIDE { + EngineChannelBase* pChannel = static_cast*>(pEngineChannel); + + NoteBase* pNote = pChannel->pEngine->NoteByID( itEvent->Param.NoteSynthParam.NoteID ); + if (!pNote || pNote->hostKey < 0 || pNote->hostKey >= 128) return; + + const bool& relative = itEvent->Param.NoteSynthParam.Relative; + + switch (itEvent->Param.NoteSynthParam.Type) { + case Event::synth_param_volume: + if (relative) + pNote->Override.Volume *= itEvent->Param.NoteSynthParam.Delta; + else + pNote->Override.Volume = itEvent->Param.NoteSynthParam.Delta; + itEvent->Param.NoteSynthParam.AbsValue = pNote->Override.Volume; + break; + case Event::synth_param_pitch: + if (relative) + pNote->Override.Pitch *= itEvent->Param.NoteSynthParam.Delta; + else + pNote->Override.Pitch = itEvent->Param.NoteSynthParam.Delta; + itEvent->Param.NoteSynthParam.AbsValue = pNote->Override.Pitch; + break; + case Event::synth_param_pan: + if (relative) { + pNote->Override.Pan = RTMath::RelativeSummedAvg(pNote->Override.Pan, itEvent->Param.NoteSynthParam.Delta, ++pNote->Override.PanSources); + } else { + pNote->Override.Pan = itEvent->Param.NoteSynthParam.Delta; + pNote->Override.PanSources = 1; // only relevant on subsequent change_pan() instrument script calls on same note with 'relative' argument being set + } + itEvent->Param.NoteSynthParam.AbsValue = pNote->Override.Pan; + break; + } + + // move note parameter event to its MIDI key + MidiKey* pKey = &pChannel->pMIDIKeyInfo[pNote->hostKey]; + itEvent.moveToEndOf(pKey->pEvents); + } + + /** * Reset all voices and disk thread and clear input event queue and all * control and status variables. This method is protected by a mutex. */