|
|
@@ -0,0 +1,161 @@ |
|
|
|
/* |
|
|
|
* 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) |
|
|
|
{ |
|
|
|
frames = midiEvents[0].frame; |
|
|
|
remainingFrames -= frames; |
|
|
|
totalFramesUsed += frames; |
|
|
|
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_RETURN((firstEventFrame - frames) < remainingFrames, false); |
|
|
|
|
|
|
|
midiEventCount = 1; |
|
|
|
while (midiEventCount < remainingMidiEventCount) |
|
|
|
{ |
|
|
|
if (midiEvents[midiEventCount].frame == firstEventFrame) |
|
|
|
++midiEventCount; |
|
|
|
else |
|
|
|
break; |
|
|
|
} |
|
|
|
|
|
|
|
if (totalFramesUsed != 0) |
|
|
|
{ |
|
|
|
for (uint32_t i=0; i < midiEventCount; ++i) |
|
|
|
{ |
|
|
|
DISTRHO_SAFE_ASSERT_UINT2_BREAK(midiEvents[i].frame - totalFramesUsed == 0, |
|
|
|
midiEvents[i].frame, totalFramesUsed); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
frames = remainingFrames - firstEventFrame; |
|
|
|
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 |