OpenShot Audio Library | OpenShotAudio 0.4.0

Loading...
Searching...
No Matches
juce_MPESynthesiserBase.cpp
1/*
2 ==============================================================================
3
4 This file is part of the JUCE library.
5 Copyright (c) 2022 - Raw Material Software Limited
6
7 JUCE is an open source library subject to commercial or open-source
8 licensing.
9
10 The code included in this file is provided under the terms of the ISC license
11 http://www.isc.org/downloads/software-support-policy/isc-license. Permission
12 To use, copy, modify, and/or distribute this software for any purpose with or
13 without fee is hereby granted provided that the above copyright notice and
14 this permission notice appear in all copies.
15
16 JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
17 EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
18 DISCLAIMED.
19
20 ==============================================================================
21*/
22
23 namespace juce
24{
25
26 MPESynthesiserBase::MPESynthesiserBase()
27 : instrument (defaultInstrument)
28{
29 instrument.addListener (this);
30}
31
32 MPESynthesiserBase::MPESynthesiserBase (MPEInstrument& inst)
33 : instrument (inst)
34{
35 instrument.addListener (this);
36}
37
38//==============================================================================
39 MPEZoneLayout MPESynthesiserBase::getZoneLayout() const noexcept
40{
41 return instrument.getZoneLayout();
42}
43
44 void MPESynthesiserBase::setZoneLayout (MPEZoneLayout newLayout)
45{
46 instrument.setZoneLayout (newLayout);
47}
48
49//==============================================================================
50 void MPESynthesiserBase::enableLegacyMode (int pitchbendRange, Range<int> channelRange)
51{
52 instrument.enableLegacyMode (pitchbendRange, channelRange);
53}
54
55 bool MPESynthesiserBase::isLegacyModeEnabled() const noexcept
56{
57 return instrument.isLegacyModeEnabled();
58}
59
60 Range<int> MPESynthesiserBase::getLegacyModeChannelRange() const noexcept
61{
62 return instrument.getLegacyModeChannelRange();
63}
64
65 void MPESynthesiserBase::setLegacyModeChannelRange (Range<int> channelRange)
66{
67 instrument.setLegacyModeChannelRange (channelRange);
68}
69
70 int MPESynthesiserBase::getLegacyModePitchbendRange() const noexcept
71{
72 return instrument.getLegacyModePitchbendRange();
73}
74
75 void MPESynthesiserBase::setLegacyModePitchbendRange (int pitchbendRange)
76{
77 instrument.setLegacyModePitchbendRange (pitchbendRange);
78}
79
80//==============================================================================
81 void MPESynthesiserBase::setPressureTrackingMode (TrackingMode modeToUse)
82{
83 instrument.setPressureTrackingMode (modeToUse);
84}
85
86 void MPESynthesiserBase::setPitchbendTrackingMode (TrackingMode modeToUse)
87{
88 instrument.setPitchbendTrackingMode (modeToUse);
89}
90
91 void MPESynthesiserBase::setTimbreTrackingMode (TrackingMode modeToUse)
92{
93 instrument.setTimbreTrackingMode (modeToUse);
94}
95
96//==============================================================================
97 void MPESynthesiserBase::handleMidiEvent (const MidiMessage& m)
98{
99 instrument.processNextMidiEvent (m);
100}
101
102//==============================================================================
103 template <typename floatType>
104 void MPESynthesiserBase::renderNextBlock (AudioBuffer<floatType>& outputAudio,
105 const MidiBuffer& inputMidi,
106 int startSample,
107 int numSamples)
108{
109 // you must set the sample rate before using this!
110 jassert (! approximatelyEqual (sampleRate, 0.0));
111
112 const ScopedLock sl (noteStateLock);
113
114 auto prevSample = startSample;
115 const auto endSample = startSample + numSamples;
116
117 for (auto it = inputMidi.findNextSamplePosition (startSample); it != inputMidi.cend(); ++it)
118 {
119 const auto metadata = *it;
120
121 if (metadata.samplePosition >= endSample)
122 break;
123
124 const auto smallBlockAllowed = (prevSample == startSample && ! subBlockSubdivisionIsStrict);
125 const auto thisBlockSize = smallBlockAllowed ? 1 : minimumSubBlockSize;
126
127 if (metadata.samplePosition >= prevSample + thisBlockSize)
128 {
129 renderNextSubBlock (outputAudio, prevSample, metadata.samplePosition - prevSample);
130 prevSample = metadata.samplePosition;
131 }
132
133 handleMidiEvent (metadata.getMessage());
134 }
135
136 if (prevSample < endSample)
137 renderNextSubBlock (outputAudio, prevSample, endSample - prevSample);
138}
139
140// explicit instantiation for supported float types:
141 template void MPESynthesiserBase::renderNextBlock<float> (AudioBuffer<float>&, const MidiBuffer&, int, int);
142 template void MPESynthesiserBase::renderNextBlock<double> (AudioBuffer<double>&, const MidiBuffer&, int, int);
143
144//==============================================================================
145 void MPESynthesiserBase::setCurrentPlaybackSampleRate (const double newRate)
146{
147 if (! approximatelyEqual (sampleRate, newRate))
148 {
149 const ScopedLock sl (noteStateLock);
150 instrument.releaseAllNotes();
151 sampleRate = newRate;
152 }
153}
154
155//==============================================================================
156 void MPESynthesiserBase::setMinimumRenderingSubdivisionSize (int numSamples, bool shouldBeStrict) noexcept
157{
158 jassert (numSamples > 0); // it wouldn't make much sense for this to be less than 1
159 minimumSubBlockSize = numSamples;
160 subBlockSubdivisionIsStrict = shouldBeStrict;
161}
162
163#if JUCE_UNIT_TESTS
164
165 namespace
166{
167 class MpeSynthesiserBaseTests final : public UnitTest
168 {
169 enum class CallbackKind { process, midi };
170
171 struct StartAndLength
172 {
173 StartAndLength (int s, int l) : start (s), length (l) {}
174
175 int start = 0;
176 int length = 0;
177
178 std::tuple<const int&, const int&> tie() const noexcept { return std::tie (start, length); }
179
180 bool operator== (const StartAndLength& other) const noexcept { return tie() == other.tie(); }
181 bool operator!= (const StartAndLength& other) const noexcept { return tie() != other.tie(); }
182
183 bool operator< (const StartAndLength& other) const noexcept { return tie() < other.tie(); }
184 };
185
186 struct Events
187 {
188 std::vector<StartAndLength> blocks;
189 std::vector<MidiMessage> messages;
190 std::vector<CallbackKind> order;
191 };
192
193 class MockSynthesiser final : public MPESynthesiserBase
194 {
195 public:
196 Events events;
197
198 void handleMidiEvent (const MidiMessage& m) override
199 {
200 events.messages.emplace_back (m);
201 events.order.emplace_back (CallbackKind::midi);
202 }
203
204 private:
205 using MPESynthesiserBase::renderNextSubBlock;
206
207 void renderNextSubBlock (AudioBuffer<float>&,
208 int startSample,
209 int numSamples) override
210 {
211 events.blocks.push_back ({ startSample, numSamples });
212 events.order.emplace_back (CallbackKind::process);
213 }
214 };
215
216 static MidiBuffer makeTestBuffer (const int bufferLength)
217 {
218 MidiBuffer result;
219
220 for (int i = 0; i != bufferLength; ++i)
221 result.addEvent ({}, i);
222
223 return result;
224 }
225
226 public:
227 MpeSynthesiserBaseTests()
228 : UnitTest ("MPE Synthesiser Base", UnitTestCategories::midi) {}
229
230 void runTest() override
231 {
232 const auto sumBlockLengths = [] (const std::vector<StartAndLength>& b)
233 {
234 const auto addBlock = [] (int acc, const StartAndLength& info) { return acc + info.length; };
235 return std::accumulate (b.begin(), b.end(), 0, addBlock);
236 };
237
238 beginTest ("Rendering sparse subblocks works");
239 {
240 const int blockSize = 512;
241 const auto midi = [&] { MidiBuffer b; b.addEvent ({}, blockSize / 2); return b; }();
242 AudioBuffer<float> audio (1, blockSize);
243
244 const auto processEvents = [&] (int start, int length)
245 {
246 MockSynthesiser synth;
247 synth.setMinimumRenderingSubdivisionSize (1, false);
248 synth.setCurrentPlaybackSampleRate (44100);
249 synth.renderNextBlock (audio, midi, start, length);
250 return synth.events;
251 };
252
253 {
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,
260 CallbackKind::midi,
261 CallbackKind::process });
262 }
263 }
264
265 beginTest ("Rendering subblocks processes only contained midi events");
266 {
267 const int blockSize = 512;
268 const auto midi = makeTestBuffer (blockSize);
269 AudioBuffer<float> audio (1, blockSize);
270
271 const auto processEvents = [&] (int start, int length)
272 {
273 MockSynthesiser synth;
274 synth.setMinimumRenderingSubdivisionSize (1, false);
275 synth.setCurrentPlaybackSampleRate (44100);
276 synth.renderNextBlock (audio, midi, start, length);
277 return synth.events;
278 };
279
280 {
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);
287 }
288
289 {
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);
296 }
297
298 {
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 });
307 }
308
309 {
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);
316 }
317 }
318
319 beginTest ("Subblocks respect their minimum size");
320 {
321 const int blockSize = 512;
322 const auto midi = makeTestBuffer (blockSize);
323 AudioBuffer<float> audio (1, blockSize);
324
325 const auto blockLengthsAreValid = [] (const std::vector<StartAndLength>& info, int minLength, bool strict)
326 {
327 if (info.size() <= 1)
328 return true;
329
330 const auto lengthIsValid = [&] (const StartAndLength& s) { return minLength <= s.length; };
331 const auto begin = strict ? info.begin() : std::next (info.begin());
332 // The final block is allowed to be shorter than the minLength
333 return std::all_of (begin, std::prev (info.end()), lengthIsValid);
334 };
335
336 for (auto strict : { false, true })
337 {
338 for (auto subblockSize : { 1, 16, 32, 64, 1024 })
339 {
340 MockSynthesiser synth;
341 synth.setMinimumRenderingSubdivisionSize (subblockSize, strict);
342 synth.setCurrentPlaybackSampleRate (44100);
343 synth.renderNextBlock (audio, midi, 0, blockSize);
344
345 const auto& e = synth.events;
346 expectWithinAbsoluteError (float (e.blocks.size()),
347 std::ceil ((float) blockSize / (float) subblockSize),
348 1.0f);
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));
353 }
354 }
355
356 {
357 MockSynthesiser synth;
358 synth.setMinimumRenderingSubdivisionSize (32, true);
359 synth.setCurrentPlaybackSampleRate (44100);
360 synth.renderNextBlock (audio, MidiBuffer{}, 0, 16);
361
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());
365 }
366 }
367 }
368 };
369
370 MpeSynthesiserBaseTests mpeSynthesiserBaseTests;
371}
372
373#endif
374
375} // namespace juce
juce::AudioBuffer
Definition juce_AudioSampleBuffer.h:34
juce::GenericScopedLock
Definition juce_ScopedLock.h:55
juce::MPEInstrument
Definition juce_MPEInstrument.h:54
juce::MPEInstrument::setPitchbendTrackingMode
void setPitchbendTrackingMode(TrackingMode modeToUse)
Definition juce_MPEInstrument.cpp:166
juce::MPEInstrument::setLegacyModeChannelRange
void setLegacyModeChannelRange(Range< int > channelRange)
Definition juce_MPEInstrument.cpp:127
juce::MPEInstrument::getZoneLayout
MPEZoneLayout getZoneLayout() const noexcept
Definition juce_MPEInstrument.cpp:63
juce::MPEInstrument::enableLegacyMode
void enableLegacyMode(int pitchbendRange=2, Range< int > channelRange=Range< int >(1, 17))
Definition juce_MPEInstrument.cpp:100
juce::MPEInstrument::TrackingMode
TrackingMode
Definition juce_MPEInstrument.h:120
juce::MPEInstrument::setZoneLayout
void setZoneLayout(MPEZoneLayout newLayout)
Definition juce_MPEInstrument.cpp:85
juce::MPEInstrument::processNextMidiEvent
virtual void processNextMidiEvent(const MidiMessage &message)
Definition juce_MPEInstrument.cpp:188
juce::MPEInstrument::setLegacyModePitchbendRange
void setLegacyModePitchbendRange(int pitchbendRange)
Definition juce_MPEInstrument.cpp:146
juce::MPEInstrument::isLegacyModeEnabled
bool isLegacyModeEnabled() const noexcept
Definition juce_MPEInstrument.cpp:117
juce::MPEInstrument::setPressureTrackingMode
void setPressureTrackingMode(TrackingMode modeToUse)
Definition juce_MPEInstrument.cpp:161
juce::MPEInstrument::addListener
void addListener(Listener *listenerToAdd)
Definition juce_MPEInstrument.cpp:177
juce::MPEInstrument::setTimbreTrackingMode
void setTimbreTrackingMode(TrackingMode modeToUse)
Definition juce_MPEInstrument.cpp:171
juce::MPEInstrument::releaseAllNotes
void releaseAllNotes()
Definition juce_MPEInstrument.cpp:841
juce::MPEInstrument::getLegacyModeChannelRange
Range< int > getLegacyModeChannelRange() const noexcept
Definition juce_MPEInstrument.cpp:122
juce::MPEInstrument::getLegacyModePitchbendRange
int getLegacyModePitchbendRange() const noexcept
Definition juce_MPEInstrument.cpp:141
juce::MPEZoneLayout
Definition juce_MPEZoneLayout.h:123
juce::MidiBuffer
Definition juce_MidiBuffer.h:145
juce::MidiBuffer::findNextSamplePosition
MidiBufferIterator findNextSamplePosition(int samplePosition) const noexcept
Definition juce_MidiBuffer.cpp:203
juce::MidiBuffer::cend
MidiBufferIterator cend() const noexcept
Definition juce_MidiBuffer.h:268
juce::MidiMessage
Definition juce_MidiMessage.h:35
juce::Range
Definition juce_Range.h:40
juce::UnitTest
Definition juce_UnitTest.h:70
juce::MPESynthesiserBase::setPitchbendTrackingMode
void setPitchbendTrackingMode(TrackingMode modeToUse)
Definition juce_MPESynthesiserBase.cpp:86
juce::MPESynthesiserBase::handleMidiEvent
virtual void handleMidiEvent(const MidiMessage &)
Definition juce_MPESynthesiserBase.cpp:97
juce::MPESynthesiserBase::isLegacyModeEnabled
bool isLegacyModeEnabled() const noexcept
Definition juce_MPESynthesiserBase.cpp:55
juce::MPESynthesiserBase::getZoneLayout
MPEZoneLayout getZoneLayout() const noexcept
Definition juce_MPESynthesiserBase.cpp:39
juce::MPESynthesiserBase::setZoneLayout
void setZoneLayout(MPEZoneLayout newLayout)
Definition juce_MPESynthesiserBase.cpp:44
juce::MPESynthesiserBase::getLegacyModePitchbendRange
int getLegacyModePitchbendRange() const noexcept
Definition juce_MPESynthesiserBase.cpp:70
juce::MPESynthesiserBase::instrument
MPEInstrument & instrument
Definition juce_MPESynthesiserBase.h:202
juce::MPESynthesiserBase::setLegacyModeChannelRange
void setLegacyModeChannelRange(Range< int > channelRange)
Definition juce_MPESynthesiserBase.cpp:65
juce::MPESynthesiserBase::setLegacyModePitchbendRange
void setLegacyModePitchbendRange(int pitchbendRange)
Definition juce_MPESynthesiserBase.cpp:75
juce::MPESynthesiserBase::enableLegacyMode
void enableLegacyMode(int pitchbendRange=2, Range< int > channelRange=Range< int >(1, 17))
Definition juce_MPESynthesiserBase.cpp:50
juce::MPESynthesiserBase::setTimbreTrackingMode
void setTimbreTrackingMode(TrackingMode modeToUse)
Definition juce_MPESynthesiserBase.cpp:91
juce::MPESynthesiserBase::setPressureTrackingMode
void setPressureTrackingMode(TrackingMode modeToUse)
Definition juce_MPESynthesiserBase.cpp:81
juce::MPESynthesiserBase::getLegacyModeChannelRange
Range< int > getLegacyModeChannelRange() const noexcept
Definition juce_MPESynthesiserBase.cpp:60
juce::MPESynthesiserBase::setCurrentPlaybackSampleRate
virtual void setCurrentPlaybackSampleRate(double sampleRate)
Definition juce_MPESynthesiserBase.cpp:145
juce::MPESynthesiserBase::renderNextBlock
void renderNextBlock(AudioBuffer< floatType > &outputAudio, const MidiBuffer &inputMidi, int startSample, int numSamples)
Definition juce_MPESynthesiserBase.cpp:104
juce::MPESynthesiserBase::setMinimumRenderingSubdivisionSize
void setMinimumRenderingSubdivisionSize(int numSamples, bool shouldBeStrict=false) noexcept
Definition juce_MPESynthesiserBase.cpp:156
juce::MPESynthesiserBase::renderNextSubBlock
virtual void renderNextSubBlock(AudioBuffer< float > &outputAudio, int startSample, int numSamples)=0
juce::MPESynthesiserBase::MPESynthesiserBase
MPESynthesiserBase()
Definition juce_MPESynthesiserBase.cpp:26