27 : instrument (defaultInstrument)
103template <
typename floatType>
110 jassert (! approximatelyEqual (sampleRate, 0.0));
115 const auto endSample = startSample + numSamples;
147 if (! approximatelyEqual (sampleRate,
newRate))
158 jassert (numSamples > 0);
159 minimumSubBlockSize = numSamples;
171 struct StartAndLength
173 StartAndLength (
int s,
int l) : start (s), length (l) {}
178 std::tuple<const int&, const int&> tie() const noexcept {
return std::tie (start, length); }
180 bool operator== (
const StartAndLength& other)
const noexcept {
return tie() == other.tie(); }
181 bool operator!= (
const StartAndLength& other)
const noexcept {
return tie() != other.tie(); }
183 bool operator< (
const StartAndLength& other)
const noexcept {
return tie() < other.tie(); }
188 std::vector<StartAndLength> blocks;
189 std::vector<MidiMessage> messages;
190 std::vector<CallbackKind> order;
193 class MockSynthesiser final :
public MPESynthesiserBase
198 void handleMidiEvent (
const MidiMessage& m)
override
200 events.messages.emplace_back (m);
201 events.order.emplace_back (CallbackKind::midi);
207 void renderNextSubBlock (AudioBuffer<float>&,
209 int numSamples)
override
211 events.blocks.push_back ({ startSample, numSamples });
212 events.order.emplace_back (CallbackKind::process);
216 static MidiBuffer makeTestBuffer (
const int bufferLength)
220 for (
int i = 0; i != bufferLength; ++i)
221 result.addEvent ({}, i);
227 MpeSynthesiserBaseTests()
228 : UnitTest (
"MPE Synthesiser Base", UnitTestCategories::midi) {}
230 void runTest()
override
232 const auto sumBlockLengths = [] (
const std::vector<StartAndLength>& b)
234 const auto addBlock = [] (
int acc,
const StartAndLength& info) {
return acc + info.length; };
235 return std::accumulate (b.begin(), b.end(), 0, addBlock);
238 beginTest (
"Rendering sparse subblocks works");
240 const int blockSize = 512;
241 const auto midi = [&] { MidiBuffer b; b.addEvent ({}, blockSize / 2);
return b; }();
242 AudioBuffer<float> audio (1, blockSize);
244 const auto processEvents = [&] (
int start,
int length)
246 MockSynthesiser synth;
247 synth.setMinimumRenderingSubdivisionSize (1,
false);
248 synth.setCurrentPlaybackSampleRate (44100);
249 synth.renderNextBlock (audio, midi, start, length);
254 const auto e = processEvents (0, blockSize);
255 expect (e.blocks.size() == 2);
256 expect (e.messages.size() == 1);
257 expect (std::is_sorted (e.blocks.begin(), e.blocks.end()));
258 expect (sumBlockLengths (e.blocks) == blockSize);
259 expect (e.order == std::vector<CallbackKind> { CallbackKind::process,
261 CallbackKind::process });
265 beginTest (
"Rendering subblocks processes only contained midi events");
267 const int blockSize = 512;
268 const auto midi = makeTestBuffer (blockSize);
269 AudioBuffer<float> audio (1, blockSize);
271 const auto processEvents = [&] (
int start,
int length)
273 MockSynthesiser synth;
274 synth.setMinimumRenderingSubdivisionSize (1,
false);
275 synth.setCurrentPlaybackSampleRate (44100);
276 synth.renderNextBlock (audio, midi, start, length);
281 const int subBlockLength = 0;
282 const auto e = processEvents (0, subBlockLength);
283 expect (e.blocks.size() == 0);
284 expect (e.messages.size() == 0);
285 expect (std::is_sorted (e.blocks.begin(), e.blocks.end()));
286 expect (sumBlockLengths (e.blocks) == subBlockLength);
290 const int subBlockLength = 0;
291 const auto e = processEvents (1, subBlockLength);
292 expect (e.blocks.size() == 0);
293 expect (e.messages.size() == 0);
294 expect (std::is_sorted (e.blocks.begin(), e.blocks.end()));
295 expect (sumBlockLengths (e.blocks) == subBlockLength);
299 const int subBlockLength = 1;
300 const auto e = processEvents (1, subBlockLength);
301 expect (e.blocks.size() == 1);
302 expect (e.messages.size() == 1);
303 expect (std::is_sorted (e.blocks.begin(), e.blocks.end()));
304 expect (sumBlockLengths (e.blocks) == subBlockLength);
305 expect (e.order == std::vector<CallbackKind> { CallbackKind::midi,
306 CallbackKind::process });
310 const auto e = processEvents (0, blockSize);
311 expect (e.blocks.size() == blockSize);
312 expect (e.messages.size() == blockSize);
313 expect (std::is_sorted (e.blocks.begin(), e.blocks.end()));
314 expect (sumBlockLengths (e.blocks) == blockSize);
315 expect (e.order.front() == CallbackKind::midi);
319 beginTest (
"Subblocks respect their minimum size");
321 const int blockSize = 512;
322 const auto midi = makeTestBuffer (blockSize);
323 AudioBuffer<float> audio (1, blockSize);
325 const auto blockLengthsAreValid = [] (
const std::vector<StartAndLength>& info,
int minLength,
bool strict)
327 if (info.size() <= 1)
330 const auto lengthIsValid = [&] (
const StartAndLength& s) {
return minLength <= s.length; };
331 const auto begin = strict ? info.begin() : std::next (info.begin());
333 return std::all_of (begin, std::prev (info.end()), lengthIsValid);
336 for (
auto strict : {
false,
true })
338 for (
auto subblockSize : { 1, 16, 32, 64, 1024 })
340 MockSynthesiser synth;
341 synth.setMinimumRenderingSubdivisionSize (subblockSize, strict);
342 synth.setCurrentPlaybackSampleRate (44100);
343 synth.renderNextBlock (audio, midi, 0, blockSize);
345 const auto& e = synth.events;
346 expectWithinAbsoluteError (
float (e.blocks.size()),
347 std::ceil ((
float) blockSize / (
float) subblockSize),
349 expect (e.messages.size() == blockSize);
350 expect (std::is_sorted (e.blocks.begin(), e.blocks.end()));
351 expect (sumBlockLengths (e.blocks) == blockSize);
352 expect (blockLengthsAreValid (e.blocks, subblockSize, strict));
357 MockSynthesiser synth;
358 synth.setMinimumRenderingSubdivisionSize (32,
true);
359 synth.setCurrentPlaybackSampleRate (44100);
360 synth.renderNextBlock (audio, MidiBuffer{}, 0, 16);
362 expect (synth.events.blocks == std::vector<StartAndLength> { { 0, 16 } });
363 expect (synth.events.order == std::vector<CallbackKind> { CallbackKind::process });
364 expect (synth.events.messages.empty());
370 MpeSynthesiserBaseTests mpeSynthesiserBaseTests;
void setPitchbendTrackingMode(TrackingMode modeToUse)
void setLegacyModeChannelRange(Range< int > channelRange)
MPEZoneLayout getZoneLayout() const noexcept
void enableLegacyMode(int pitchbendRange=2, Range< int > channelRange=Range< int >(1, 17))
void setZoneLayout(MPEZoneLayout newLayout)
virtual void processNextMidiEvent(const MidiMessage &message)
void setLegacyModePitchbendRange(int pitchbendRange)
bool isLegacyModeEnabled() const noexcept
void setPressureTrackingMode(TrackingMode modeToUse)
void addListener(Listener *listenerToAdd)
void setTimbreTrackingMode(TrackingMode modeToUse)
Range< int > getLegacyModeChannelRange() const noexcept
int getLegacyModePitchbendRange() const noexcept
void setPitchbendTrackingMode(TrackingMode modeToUse)
virtual void handleMidiEvent(const MidiMessage &)
bool isLegacyModeEnabled() const noexcept
MPEZoneLayout getZoneLayout() const noexcept
void setZoneLayout(MPEZoneLayout newLayout)
int getLegacyModePitchbendRange() const noexcept
MPEInstrument & instrument
void setLegacyModeChannelRange(Range< int > channelRange)
void setLegacyModePitchbendRange(int pitchbendRange)
void enableLegacyMode(int pitchbendRange=2, Range< int > channelRange=Range< int >(1, 17))
void setTimbreTrackingMode(TrackingMode modeToUse)
void setPressureTrackingMode(TrackingMode modeToUse)
Range< int > getLegacyModeChannelRange() const noexcept
virtual void setCurrentPlaybackSampleRate(double sampleRate)
void renderNextBlock(AudioBuffer< floatType > &outputAudio, const MidiBuffer &inputMidi, int startSample, int numSamples)
void setMinimumRenderingSubdivisionSize(int numSamples, bool shouldBeStrict=false) noexcept
virtual void renderNextSubBlock(AudioBuffer< float > &outputAudio, int startSample, int numSamples)=0