iPlug2 - C++ Audio Plug-in Framework
Loading...
Searching...
No Matches
MidiSynth.h
Go to the documentation of this file.
1/*
2 ==============================================================================
3
4 This file is part of the iPlug 2 library. Copyright (C) the iPlug 2 developers.
5
6 See LICENSE.txt for more info.
7
8 ==============================================================================
9 */
10
11#pragma once
12
18#include <array>
19#include <vector>
20#include <stdint.h>
21
22#include "ptrlist.h"
23
24#include "IPlugConstants.h"
25#include "IPlugMidi.h"
26#include "IPlugLogger.h"
27
28#include "SynthVoice.h"
29#include "VoiceAllocator.h"
30
31#define DEBUG_VOICE_COUNT 0
32
33BEGIN_IPLUG_NAMESPACE
34
38{
39public:
41 static constexpr int kDefaultBlockSize = 32;
42 static constexpr int kDefaultPitchBendRange = 12;
43
44#pragma mark - MidiSynth class
45
46 MidiSynth(VoiceAllocator::EPolyMode mode, int blockSize = kDefaultBlockSize);
47 ~MidiSynth();
48
49 MidiSynth(const MidiSynth&) = delete;
50 MidiSynth& operator=(const MidiSynth&) = delete;
51
52 void Reset()
53 {
54 mSampleTime = 0;
55 mVoiceAllocator.Clear();
56 }
57
58 void SetSampleRateAndBlockSize(double sampleRate, int blockSize);
59
63 void SetVoicesActive(bool active)
64 {
65 mVoicesAreActive = active;
66 }
67
68 void InitBasicMPE()
69 {
70 SetMPEZones(0, 16);
71 }
72
74 void SetPitchBendRange(int pitchBendRange)
75 {
76 mNonMPEPitchBendRange = pitchBendRange;
77
78 if(!mMPEMode)
79 {
80 for(int i=0; i<16; ++i)
81 {
82 mChannelStates[i].pitchBendRange = pitchBendRange;
83 }
84 }
85 }
86
87 void SetPolyMode(VoiceAllocator::EPolyMode mode)
88 {
89 mVoiceAllocator.mPolyMode = mode;
90 }
91
92 void SetATMode(VoiceAllocator::EATMode mode)
93 {
94 mVoiceAllocator.mATMode = mode;
95 }
96
101 void SetKeyToPitchFn(const std::function<float(int)>& fn)
102 {
103 mVoiceAllocator.SetKeyToPitchFunction(fn);
104 }
105
106 void SetNoteOffset(double offset)
107 {
108 mVoiceAllocator.SetPitchOffset(static_cast<float>(offset));
109 }
110
111 void SetNoteGlideTime(double t)
112 {
113 mVoiceAllocator.SetNoteGlideTime(t);
114 }
115
116 void SetControlGlideTime(double t)
117 {
118 mVoiceAllocator.SetControlGlideTime(t);
119 }
120
121 SynthVoice* GetVoice(int voiceIdx)
122 {
123 return mVoiceAllocator.GetVoice(voiceIdx);
124 }
125
126 void ForEachVoice(std::function<void(SynthVoice& voice)> func)
127 {
128 for (auto v = 0; v < NVoices(); v++)
129 func(*GetVoice(v));
130 }
131
132 size_t NVoices() const
133 {
134 return mVoiceAllocator.GetNVoices();
135 }
136
138 void AddVoice(SynthVoice* pVoice, uint8_t zone)
139 {
140 mVoiceAllocator.AddVoice(pVoice, zone);
141 }
142
143 void AddMidiMsgToQueue(const IMidiMsg& msg)
144 {
145 mMidiQueue.Add(msg);
146 }
147
155 bool ProcessBlock(sample** inputs, sample** outputs, int nInputs, int nOutputs, int nFrames);
156
157private:
158
159 // maintain the state for one MIDI channel including RPN receipt state and pitch bend range.
160 struct ChannelState
161 {
162 uint8_t paramMSB;
163 uint8_t paramLSB;
164 uint8_t valueMSB;
165 uint8_t valueLSB;
166 uint8_t pitchBendRange; // in semitones
167 float pitchBend;
168 float pressure;
169 float timbre;
170 };
171
172 // MPE helper functions
173 const int kMPELowerZoneMasterChannel = 0;
174 const int kMPEUpperZoneMasterChannel = 15;
175 inline bool IsMasterChannel(int c) const { return ((c == 0)||(c == 15)); }
176 bool IsInLowerZone(int c) const { return ((c > 0)&&(c < mMPELowerZoneChannels)); }
177 bool IsInUpperZone(int c) const { return ((c < 15)&&(c > 15 - mMPEUpperZoneChannels)); }
178 int MasterChannelFor(int memberChan) const { return IsInUpperZone(memberChan) ? kMPEUpperZoneMasterChannel : kMPELowerZoneMasterChannel; }
179 int MasterZoneFor(int memberChan) const { return IsInUpperZone(memberChan) ? 1 : 0; }
180
181 // handy functions for writing loops on lower and upper Zone member channels
182 int LowerZoneStart() const { return 1; }
183 int LowerZoneEnd() const { return mMPELowerZoneChannels - 1; }
184 int UpperZoneStart() const { return 15 - mMPEUpperZoneChannels; }
185 int UpperZoneEnd() const { return 15; }
186
187 void SetMPEZones(int channel, int nChans);
188 void SetChannelPitchBendRange(int channel, int range);
189
190 VoiceInputEvent MidiMessageToEventBasic(const IMidiMsg& msg);
191 VoiceInputEvent MidiMessageToEventMPE(const IMidiMsg& msg);
192 VoiceInputEvent MidiMessageToEvent(const IMidiMsg& msg);
193 void HandleRPN(IMidiMsg msg);
194
195 // basic MIDI data
196 VoiceAllocator mVoiceAllocator;
197 uint16_t mUnisonVoices{1};
198 IMidiQueue mMidiQueue;
199 float mVelocityLUT[128];
200 float mAfterTouchLUT[128];
201 ChannelState mChannelStates[16]{};
202 int mBlockSize;
203 int64_t mSampleTime{0};
204 double mSampleRate = DEFAULT_SAMPLE_RATE;
205 bool mVoicesAreActive = false;
206 int mNonMPEPitchBendRange = kDefaultPitchBendRange;
207
208 // the synth will startup in basic MIDI mode. When an MPE Zone setup message is received, MPE mode is entered.
209 // To leave MPE mode, use RPNs to set all MPE zone channel counts to 0 as per the MPE spec.
210 bool mMPEMode{false};
211
212 // MPE state - channels in zone including master channels.
213 int mMPELowerZoneChannels{0};
214 int mMPEUpperZoneChannels{0};
215};
216
217END_IPLUG_NAMESPACE
218
IPlug Constant definitions, Types, magic numbers.
IPlug logging a.k.a tracing functionality.
MIDI and sysex structs/utilites.
A VoiceInputEvent describes a change in input to be applied to one more more voices.
A monophonic/polyphonic synthesiser base class which can be supplied with a custom voice.
Definition: MidiSynth.h:38
void SetVoicesActive(bool active)
If you are using this class in a non-traditional mode of polyphony (e.g.to stack loads of voices) you...
Definition: MidiSynth.h:63
bool ProcessBlock(sample **inputs, sample **outputs, int nInputs, int nOutputs, int nFrames)
Processes a block of audio samples.
Definition: MidiSynth.cpp:402
void SetPitchBendRange(int pitchBendRange)
Set the pitch bend range for non-MPE mode.
Definition: MidiSynth.h:74
void SetKeyToPitchFn(const std::function< float(int)> &fn)
Set this function to something other than the default if you need to implement a tuning table for mic...
Definition: MidiSynth.h:101
void AddVoice(SynthVoice *pVoice, uint8_t zone)
adds a SynthVoice to this MidiSynth, taking ownership of the object.
Definition: MidiSynth.h:138
static constexpr int kDefaultBlockSize
This defines the size in samples of a single block of processing that will be done by the synth.
Definition: MidiSynth.h:41
void AddVoice(SynthVoice *pv, uint8_t zone)
Add a synth voice to the allocator.
Encapsulates a MIDI message and provides helper functions.
Definition: IPlugMidi.h:31