1 /*
2   ==============================================================================
3 
4    This file is part of the JUCE library.
5    Copyright (c) 2020 - 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 //==============================================================================
27 /**
28     Derive from this class to create a basic audio generator capable of MPE.
29     Implement the callbacks of MPEInstrument::Listener (noteAdded, notePressureChanged
30     etc.) to let your audio generator know that MPE notes were triggered, modulated,
31     or released. What to do inside them, and how that influences your audio generator,
32     is up to you!
33 
34     This class uses an instance of MPEInstrument internally to handle the MPE
35     note state logic.
36 
37     This class is a very low-level base class for an MPE instrument. If you need
38     something more sophisticated, have a look at MPESynthesiser. This class extends
39     MPESynthesiserBase by adding the concept of voices that can play notes,
40     a voice stealing algorithm, and much more.
41 
42     @see MPESynthesiser, MPEInstrument
43 
44     @tags{Audio}
45 */
46 struct JUCE_API  MPESynthesiserBase   : public MPEInstrument::Listener
47 {
48 public:
49     //==============================================================================
50     /** Constructor. */
51     MPESynthesiserBase();
52 
53     /** Constructor.
54 
55         If you use this constructor, the synthesiser will take ownership of the
56         provided instrument object, and will use it internally to handle the
57         MPE note state logic.
58         This is useful if you want to use an instance of your own class derived
59         from MPEInstrument for the MPE logic.
60     */
61     MPESynthesiserBase (MPEInstrument* instrument);
62 
63     //==============================================================================
64     /** Returns the synthesiser's internal MPE zone layout.
65         This happens by value, to enforce thread-safety and class invariants.
66     */
67     MPEZoneLayout getZoneLayout() const noexcept;
68 
69     /** Re-sets the synthesiser's internal MPE zone layout to the one passed in.
70         As a side effect, this will discard all currently playing notes,
71         call noteReleased for all of them, and disable legacy mode (if previously enabled).
72     */
73     void setZoneLayout (MPEZoneLayout newLayout);
74 
75     //==============================================================================
76     /** Tells the synthesiser what the sample rate is for the audio it's being
77         used to render.
78     */
79     virtual void setCurrentPlaybackSampleRate (double sampleRate);
80 
81     /** Returns the current target sample rate at which rendering is being done.
82         Subclasses may need to know this so that they can pitch things correctly.
83     */
getSampleRateMPESynthesiserBase84     double getSampleRate() const noexcept          { return sampleRate; }
85 
86     //==============================================================================
87     /** Creates the next block of audio output.
88 
89         Call this to make sound. This will chop up the AudioBuffer into subBlock
90         pieces separated by events in the MIDI buffer, and then call
91         renderNextSubBlock on each one of them. In between you will get calls
92         to noteAdded/Changed/Finished, where you can update parameters that
93         depend on those notes to use for your audio rendering.
94 
95         @param outputAudio      Buffer into which audio will be rendered
96         @param inputMidi        MIDI events to process
97         @param startSample      The first sample to process in both buffers
98         @param numSamples       The number of samples to process
99     */
100     template <typename floatType>
101     void renderNextBlock (AudioBuffer<floatType>& outputAudio,
102                           const MidiBuffer& inputMidi,
103                           int startSample,
104                           int numSamples);
105 
106     //==============================================================================
107     /** Handle incoming MIDI events (called from renderNextBlock).
108 
109         The default implementation provided here simply forwards everything
110         to MPEInstrument::processNextMidiEvent, where it is used to update the
111         MPE notes, zones etc. MIDI messages not relevant for MPE are ignored.
112 
113         This method can be overridden if you need to do custom MIDI handling
114         on top of MPE. The MPESynthesiser class overrides this to implement
115         callbacks for MIDI program changes and non-MPE-related MIDI controller
116         messages.
117     */
118     virtual void handleMidiEvent (const MidiMessage&);
119 
120     //==============================================================================
121     /** Sets a minimum limit on the size to which audio sub-blocks will be divided when rendering.
122 
123         When rendering, the audio blocks that are passed into renderNextBlock() will be split up
124         into smaller blocks that lie between all the incoming midi messages, and it is these smaller
125         sub-blocks that are rendered with multiple calls to renderVoices().
126 
127         Obviously in a pathological case where there are midi messages on every sample, then
128         renderVoices() could be called once per sample and lead to poor performance, so this
129         setting allows you to set a lower limit on the block size.
130 
131         The default setting is 32, which means that midi messages are accurate to about < 1ms
132         accuracy, which is probably fine for most purposes, but you may want to increase or
133         decrease this value for your synth.
134 
135         If shouldBeStrict is true, the audio sub-blocks will strictly never be smaller than numSamples.
136 
137         If shouldBeStrict is false (default), the first audio sub-block in the buffer is allowed
138         to be smaller, to make sure that the first MIDI event in a buffer will always be sample-accurate
139         (this can sometimes help to avoid quantisation or phasing issues).
140     */
141     void setMinimumRenderingSubdivisionSize (int numSamples, bool shouldBeStrict = false) noexcept;
142 
143     //==============================================================================
144     /** Puts the synthesiser into legacy mode.
145 
146         @param pitchbendRange   The note pitchbend range in semitones to use when in legacy mode.
147                                 Must be between 0 and 96, otherwise behaviour is undefined.
148                                 The default pitchbend range in legacy mode is +/- 2 semitones.
149         @param channelRange     The range of MIDI channels to use for notes when in legacy mode.
150                                 The default is to use all MIDI channels (1-16).
151 
152         To get out of legacy mode, set a new MPE zone layout using setZoneLayout.
153     */
154     void enableLegacyMode (int pitchbendRange = 2,
155                            Range<int> channelRange = Range<int> (1, 17));
156 
157     /** Returns true if the instrument is in legacy mode, false otherwise. */
158     bool isLegacyModeEnabled() const noexcept;
159 
160     /** Returns the range of MIDI channels (1-16) to be used for notes when in legacy mode. */
161     Range<int> getLegacyModeChannelRange() const noexcept;
162 
163     /** Re-sets the range of MIDI channels (1-16) to be used for notes when in legacy mode. */
164     void setLegacyModeChannelRange (Range<int> channelRange);
165 
166     /** Returns the pitchbend range in semitones (0-96) to be used for notes when in legacy mode. */
167     int getLegacyModePitchbendRange() const noexcept;
168 
169     /** Re-sets the pitchbend range in semitones (0-96) to be used for notes when in legacy mode. */
170     void setLegacyModePitchbendRange (int pitchbendRange);
171 
172     //==============================================================================
173     using TrackingMode = MPEInstrument::TrackingMode;
174 
175     /** Set the MPE tracking mode for the pressure dimension. */
176     void setPressureTrackingMode (TrackingMode modeToUse);
177 
178     /** Set the MPE tracking mode for the pitchbend dimension. */
179     void setPitchbendTrackingMode (TrackingMode modeToUse);
180 
181     /** Set the MPE tracking mode for the timbre dimension. */
182     void setTimbreTrackingMode (TrackingMode modeToUse);
183 
184 protected:
185     //==============================================================================
186     /** Implement this method to render your audio inside.
187         @see renderNextBlock
188     */
189     virtual void renderNextSubBlock (AudioBuffer<float>& outputAudio,
190                                      int startSample,
191                                      int numSamples) = 0;
192 
193     /** Implement this method if you want to render 64-bit audio as well;
194         otherwise leave blank.
195      */
renderNextSubBlockMPESynthesiserBase196     virtual void renderNextSubBlock (AudioBuffer<double>& /*outputAudio*/,
197                                      int /*startSample*/,
198                                      int /*numSamples*/) {}
199 
200 protected:
201     //==============================================================================
202     /** @internal */
203     std::unique_ptr<MPEInstrument> instrument;
204 
205 private:
206     //==============================================================================
207     CriticalSection noteStateLock;
208     double sampleRate = 0.0;
209     int minimumSubBlockSize = 32;
210     bool subBlockSubdivisionIsStrict = false;
211 
212     JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MPESynthesiserBase)
213 };
214 
215 } // namespace juce
216