| @@ -0,0 +1,155 @@ | |||||
| /* | |||||
| * DISTRHO Plugin Framework (DPF) | |||||
| * Copyright (C) 2012-2019 Filipe Coelho <falktx@falktx.com> | |||||
| * | |||||
| * Permission to use, copy, modify, and/or distribute this software for any purpose with | |||||
| * or without fee is hereby granted, provided that the above copyright notice and this | |||||
| * permission notice appear in all copies. | |||||
| * | |||||
| * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD | |||||
| * TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN | |||||
| * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL | |||||
| * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER | |||||
| * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN | |||||
| * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |||||
| */ | |||||
| #ifndef DISTRHO_PLUGIN_UTILS_HPP_INCLUDED | |||||
| #define DISTRHO_PLUGIN_UTILS_HPP_INCLUDED | |||||
| #include "DistrhoPlugin.hpp" | |||||
| START_NAMESPACE_DISTRHO | |||||
| // ----------------------------------------------------------------------------------------------------------- | |||||
| /** | |||||
| Handy class to help keep audio buffer in sync with incoming MIDI events. | |||||
| To use it, create a local variable (on the stack) and call nextEvent() until it returns false. | |||||
| @code | |||||
| for (AudioMidiSyncHelper amsh(outputs, frames, midiEvents, midiEventCount); amsh.nextEvent();) | |||||
| { | |||||
| float* const outL = amsh.outputs[0]; | |||||
| float* const outR = amsh.outputs[1]; | |||||
| for (uint32_t i=0; i<amsh.midiEventCount; ++i) | |||||
| { | |||||
| const MidiEvent& ev(amsh.midiEvents[i]); | |||||
| // ... do something with the midi event | |||||
| } | |||||
| renderSynth(outL, outR, amsh.frames); | |||||
| } | |||||
| @endcode | |||||
| Some important notes when using this class: | |||||
| 1. MidiEvent::frame retains its original value, but it is useless, do not use it. | |||||
| 2. The class variables names are be the same as the default ones in the run function. | |||||
| Keep that in mind and try to avoid typos. :) | |||||
| */ | |||||
| class AudioMidiSyncHelper { | |||||
| public: | |||||
| /** Parameters from the run function, adjusted for event sync */ | |||||
| float** outputs; | |||||
| uint32_t frames; | |||||
| const MidiEvent* midiEvents; | |||||
| uint32_t midiEventCount; | |||||
| /** | |||||
| Constructor, using values from the run function. | |||||
| */ | |||||
| AudioMidiSyncHelper(float** const o, uint32_t f, const MidiEvent* m, uint32_t mc) | |||||
| : outputs(o), | |||||
| frames(0), | |||||
| midiEvents(m), | |||||
| midiEventCount(0), | |||||
| remainingFrames(f), | |||||
| remainingMidiEventCount(mc), | |||||
| totalFramesUsed(0) {} | |||||
| /** | |||||
| Process a batch of events untill no more are available. | |||||
| You must not read any more values from this class after this function returns false. | |||||
| */ | |||||
| bool nextEvent() | |||||
| { | |||||
| // nothing else to do | |||||
| if (remainingFrames == 0) | |||||
| return false; | |||||
| // initial setup, need to find first MIDI event | |||||
| if (totalFramesUsed == 0) | |||||
| { | |||||
| // no MIDI events at all in this process cycle | |||||
| if (remainingMidiEventCount == 0) | |||||
| { | |||||
| frames = remainingFrames; | |||||
| remainingFrames = 0; | |||||
| totalFramesUsed += frames; | |||||
| return true; | |||||
| } | |||||
| // render audio until first midi event, if needed | |||||
| if (const uint32_t firstEventFrame = midiEvents[0].frame) | |||||
| { | |||||
| DISTRHO_SAFE_ASSERT_UINT2_RETURN(firstEventFrame < remainingFrames, | |||||
| firstEventFrame, remainingFrames, false); | |||||
| frames = firstEventFrame; | |||||
| remainingFrames -= firstEventFrame; | |||||
| totalFramesUsed += firstEventFrame; | |||||
| return true; | |||||
| } | |||||
| } | |||||
| else | |||||
| { | |||||
| for (uint32_t i=0; i<DISTRHO_PLUGIN_NUM_OUTPUTS; ++i) | |||||
| outputs[i] += frames; | |||||
| } | |||||
| // no more MIDI events available | |||||
| if (remainingMidiEventCount == 0) | |||||
| { | |||||
| frames = remainingFrames; | |||||
| midiEvents = nullptr; | |||||
| midiEventCount = 0; | |||||
| remainingFrames = 0; | |||||
| totalFramesUsed += frames; | |||||
| return true; | |||||
| } | |||||
| // if there were midi events before, increment pointer | |||||
| if (midiEventCount != 0) | |||||
| midiEvents += midiEventCount; | |||||
| const uint32_t firstEventFrame = midiEvents[0].frame; | |||||
| DISTRHO_SAFE_ASSERT_UINT2_RETURN(firstEventFrame >= totalFramesUsed, | |||||
| firstEventFrame, totalFramesUsed, false); | |||||
| midiEventCount = 1; | |||||
| while (midiEventCount < remainingMidiEventCount) | |||||
| { | |||||
| if (midiEvents[midiEventCount].frame == firstEventFrame) | |||||
| ++midiEventCount; | |||||
| else | |||||
| break; | |||||
| } | |||||
| frames = firstEventFrame - totalFramesUsed; | |||||
| remainingFrames -= frames; | |||||
| remainingMidiEventCount -= midiEventCount; | |||||
| totalFramesUsed += frames; | |||||
| return true; | |||||
| } | |||||
| private: | |||||
| /** @internal */ | |||||
| uint32_t remainingFrames; | |||||
| uint32_t remainingMidiEventCount; | |||||
| uint32_t totalFramesUsed; | |||||
| }; | |||||
| // ----------------------------------------------------------------------------------------------------------- | |||||
| END_NAMESPACE_DISTRHO | |||||
| #endif // DISTRHO_PLUGIN_UTILS_HPP_INCLUDED | |||||