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