DISTRHO Plugin Framework
DistrhoPluginUtils.hpp
1 /*
2  * DISTRHO Plugin Framework (DPF)
3  * Copyright (C) 2012-2019 Filipe Coelho <falktx@falktx.com>
4  *
5  * Permission to use, copy, modify, and/or distribute this software for any purpose with
6  * or without fee is hereby granted, provided that the above copyright notice and this
7  * permission notice appear in all copies.
8  *
9  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD
10  * TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN
11  * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
12  * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
13  * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
14  * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15  */
16 
17 #ifndef DISTRHO_PLUGIN_UTILS_HPP_INCLUDED
18 #define DISTRHO_PLUGIN_UTILS_HPP_INCLUDED
19 
20 #include "DistrhoPlugin.hpp"
21 
22 START_NAMESPACE_DISTRHO
23 
24 // -----------------------------------------------------------------------------------------------------------
25 
26 /**
27  Handy class to help keep audio buffer in sync with incoming MIDI events.
28  To use it, create a local variable (on the stack) and call nextEvent() until it returns false.
29  @code
30  for (AudioMidiSyncHelper amsh(outputs, frames, midiEvents, midiEventCount); amsh.nextEvent();)
31  {
32  float* const outL = amsh.outputs[0];
33  float* const outR = amsh.outputs[1];
34 
35  for (uint32_t i=0; i<amsh.midiEventCount; ++i)
36  {
37  const MidiEvent& ev(amsh.midiEvents[i]);
38  // ... do something with the midi event
39  }
40 
41  renderSynth(outL, outR, amsh.frames);
42  }
43  @endcode
44 
45  Some important notes when using this class:
46  1. MidiEvent::frame retains its original value, but it is useless, do not use it.
47  2. The class variables names are be the same as the default ones in the run function.
48  Keep that in mind and try to avoid typos. :)
49  */
51 public:
52  /** Parameters from the run function, adjusted for event sync */
54  uint32_t frames;
55  const MidiEvent* midiEvents;
56  uint32_t midiEventCount;
57 
58  /**
59  Constructor, using values from the run function.
60  */
61  AudioMidiSyncHelper(float** const o, uint32_t f, const MidiEvent* m, uint32_t mc)
62  : outputs(),
63  frames(0),
64  midiEvents(m),
65  midiEventCount(0),
66  remainingFrames(f),
67  remainingMidiEventCount(mc),
68  totalFramesUsed(0)
69  {
70  for (uint i=0; i<DISTRHO_PLUGIN_NUM_OUTPUTS; ++i)
71  outputs[i] = o[i];
72  }
73 
74  /**
75  Process a batch of events untill no more are available.
76  You must not read any more values from this class after this function returns false.
77  */
78  bool nextEvent()
79  {
80  // nothing else to do
81  if (remainingFrames == 0)
82  return false;
83 
84  // initial setup, need to find first MIDI event
85  if (totalFramesUsed == 0)
86  {
87  // no MIDI events at all in this process cycle
88  if (remainingMidiEventCount == 0)
89  {
90  frames = remainingFrames;
91  remainingFrames = 0;
92  totalFramesUsed += frames;
93  return true;
94  }
95 
96  // render audio until first midi event, if needed
97  if (const uint32_t firstEventFrame = midiEvents[0].frame)
98  {
99  DISTRHO_SAFE_ASSERT_UINT2_RETURN(firstEventFrame < remainingFrames,
100  firstEventFrame, remainingFrames, false);
101  frames = firstEventFrame;
102  remainingFrames -= firstEventFrame;
103  totalFramesUsed += firstEventFrame;
104  return true;
105  }
106  }
107  else
108  {
109  for (uint32_t i=0; i<DISTRHO_PLUGIN_NUM_OUTPUTS; ++i)
110  outputs[i] += frames;
111  }
112 
113  // no more MIDI events available
114  if (remainingMidiEventCount == 0)
115  {
116  frames = remainingFrames;
117  midiEvents = nullptr;
118  midiEventCount = 0;
119  remainingFrames = 0;
120  totalFramesUsed += frames;
121  return true;
122  }
123 
124  // if there were midi events before, increment pointer
125  if (midiEventCount != 0)
126  midiEvents += midiEventCount;
127 
128  const uint32_t firstEventFrame = midiEvents[0].frame;
129  DISTRHO_SAFE_ASSERT_UINT2_RETURN(firstEventFrame >= totalFramesUsed,
130  firstEventFrame, totalFramesUsed, false);
131 
132  midiEventCount = 1;
133  while (midiEventCount < remainingMidiEventCount)
134  {
135  if (midiEvents[midiEventCount].frame == firstEventFrame)
136  ++midiEventCount;
137  else
138  break;
139  }
140 
141  frames = firstEventFrame - totalFramesUsed;
142  remainingFrames -= frames;
143  remainingMidiEventCount -= midiEventCount;
144  totalFramesUsed += frames;
145  return true;
146  }
147 
148 private:
149  /** @internal */
150  uint32_t remainingFrames;
151  uint32_t remainingMidiEventCount;
152  uint32_t totalFramesUsed;
153 };
154 
155 // -----------------------------------------------------------------------------------------------------------
156 
157 END_NAMESPACE_DISTRHO
158 
159 #endif // DISTRHO_PLUGIN_UTILS_HPP_INCLUDED
MidiEvent
Definition: DistrhoPlugin.hpp:616
MidiEvent::frame
uint32_t frame
Definition: DistrhoPlugin.hpp:625
AudioMidiSyncHelper::outputs
float * outputs[2]
Definition: DistrhoPluginUtils.hpp:53
AudioMidiSyncHelper::nextEvent
bool nextEvent()
Definition: DistrhoPluginUtils.hpp:78
AudioMidiSyncHelper::AudioMidiSyncHelper
AudioMidiSyncHelper(float **const o, uint32_t f, const MidiEvent *m, uint32_t mc)
Definition: DistrhoPluginUtils.hpp:61
DISTRHO_PLUGIN_NUM_OUTPUTS
#define DISTRHO_PLUGIN_NUM_OUTPUTS
Definition: DistrhoInfo.hpp:482
AudioMidiSyncHelper
Definition: DistrhoPluginUtils.hpp:50