15MidiSynth::MidiSynth(VoiceAllocator::EPolyMode mode,
int blockSize)
16: mBlockSize(blockSize)
20 for(
int i=0; i<128; i++)
22 mVelocityLUT[i] = i / 127.f;
23 mAfterTouchLUT[i] = i / 127.f;
27 for(
int i=0; i<16; ++i)
29 mChannelStates[i] = ChannelState{0};
30 mChannelStates[i].pitchBendRange = kDefaultPitchBendRange;
34MidiSynth::~MidiSynth()
43 event.mSampleOffset = msg.mOffset;
44 event.mAddress.mChannel = msg.
Channel();
49 case IMidiMsg::kNoteOn:
52 event.mAction = (v == 0) ? kNoteOffAction : kNoteOnAction;
53 event.mValue = mVelocityLUT[v];
56 case IMidiMsg::kNoteOff:
59 event.mAction = kNoteOffAction;
60 event.mValue = mVelocityLUT[v];
63 case IMidiMsg::kPolyAftertouch:
65 event.mAction = kPressureAction;
69 case IMidiMsg::kChannelAftertouch:
71 event.mAction = kPressureAction;
75 case IMidiMsg::kPitchWheel:
77 event.mAction = kPitchBendAction;
78 float bendRange = mChannelStates[
event.mAddress.mChannel].pitchBendRange;
79 event.mValue =
static_cast<float>(msg.
PitchWheel()) * bendRange / 12.f;
82 case IMidiMsg::kControlChange:
86 switch(event.mControllerNumber)
89 case IMidiMsg::kCutoffFrequency:
91 event.mAction = kTimbreAction;
94 case IMidiMsg::kSustainOnOff:
96 event.mAction = kSustainAction;
99 case IMidiMsg::kAllNotesOff:
101 event.mAddress.mFlags = kVoicesAll;
102 event.mAction = kNoteOffAction;
108 event.mAction = kControllerAction;
114 case IMidiMsg::kProgramChange:
116 event.mAction = kProgramChangeAction;
117 event.mControllerNumber = msg.
Program();
134 event.mSampleOffset = msg.mOffset;
135 event.mAddress.mChannel = msg.
Channel();
137 event.mAddress.mZone = MasterZoneFor(event.mAddress.mChannel);
141 bool isPitchBend = status == IMidiMsg::kPitchWheel;
142 bool isChannelPressure = status == IMidiMsg::kChannelAftertouch;
143 bool isTimbre = (status == IMidiMsg::kControlChange) && (msg.
ControlChangeIdx() == IMidiMsg::kCutoffFrequency);
144 if(isPitchBend || isChannelPressure || isTimbre)
146 float* pChannelDestValue{};
147 float masterChannelStoredValue{};
150 event.mAction = kPitchBendAction;
151 float bendRange = mChannelStates[
event.mAddress.mChannel].pitchBendRange;
152 event.mValue =
static_cast<float>(msg.
PitchWheel()) * bendRange / 12.f;
154 pChannelDestValue = &(mChannelStates[
event.mAddress.mChannel].pitchBend);
155 masterChannelStoredValue = mChannelStates[MasterChannelFor(event.mAddress.mChannel)].pitchBend;
157 else if(isChannelPressure)
159 event.mAction = kPressureAction;
161 pChannelDestValue = &(mChannelStates[
event.mAddress.mChannel].pressure);
162 masterChannelStoredValue = mChannelStates[MasterChannelFor(event.mAddress.mChannel)].pressure;
166 event.mAction = kTimbreAction;
168 pChannelDestValue = &(mChannelStates[
event.mAddress.mChannel].timbre);
169 masterChannelStoredValue = mChannelStates[MasterChannelFor(event.mAddress.mChannel)].timbre;
172 if(IsMasterChannel(event.mAddress.mChannel))
175 *pChannelDestValue =
event.mValue;
178 event.mAction = kNullAction;
183 event.mValue += masterChannelStoredValue;
186 *pChannelDestValue =
event.mValue;
200 case IMidiMsg::kProgramChange:
202 if(IsMasterChannel(event.mAddress.mChannel))
204 event.mAction = kProgramChangeAction;
205 event.mControllerNumber = msg.
Program();
210 event.mAction = kNullAction;
213 case IMidiMsg::kNoteOn:
216 event.mAction = (v == 0) ? kNoteOffAction : kNoteOnAction;
217 event.mValue = mVelocityLUT[v];
220 case IMidiMsg::kNoteOff:
223 event.mAction = kNoteOffAction;
224 event.mValue = mVelocityLUT[v];
227 case IMidiMsg::kControlChange:
230 switch(event.mControllerNumber)
232 case IMidiMsg::kAllNotesOff:
234 event.mAddress.mFlags = kVoicesAll;
235 event.mAction = kNoteOffAction;
244 event.mAction = kControllerAction;
261 return (mMPEMode ? MidiMessageToEventMPE(msg) : MidiMessageToEventBasic(msg));
265void MidiSynth::SetMPEZones(
int channel,
int nChans)
269 int memberChannels =
Clip(nChans, 0, 15);
270 int totalChannels = memberChannels ? (memberChannels + 1) : 0;
273 mMPELowerZoneChannels = totalChannels;
274 mMPEUpperZoneChannels =
Clip(mMPEUpperZoneChannels, 0, 16 - mMPELowerZoneChannels);
276 else if (channel == 15)
278 mMPEUpperZoneChannels = totalChannels;
279 mMPELowerZoneChannels =
Clip(mMPELowerZoneChannels, 0, 16 - mMPEUpperZoneChannels);
283 bool anyMPEChannelsActive = (mMPELowerZoneChannels || mMPEUpperZoneChannels);
284 if(anyMPEChannelsActive && (!mMPEMode))
288 else if ((!anyMPEChannelsActive) && (mMPEMode))
298 SetChannelPitchBendRange(kMPELowerZoneMasterChannel, 2);
299 SetChannelPitchBendRange(kMPELowerZoneMasterChannel + 1, 48);
301 else if (channel == 15)
303 SetChannelPitchBendRange(kMPEUpperZoneMasterChannel, 2);
304 SetChannelPitchBendRange(kMPEUpperZoneMasterChannel - 1, 48);
310 std::cout <<
"MPE mode: " << (mMPEMode ?
"ON" :
"OFF") <<
"\n";
311 std::cout <<
"MPE channels: \n lo: " << mMPELowerZoneChannels <<
" hi " << mMPEUpperZoneChannels <<
"\n";
314void MidiSynth::SetChannelPitchBendRange(
int channelParam,
int rangeParam)
316 int channelLo, channelHi;
317 if(IsInLowerZone(channelParam))
319 channelLo = LowerZoneStart();
320 channelHi = LowerZoneEnd();
322 else if (IsInUpperZone(channelParam))
324 channelLo = UpperZoneStart();
325 channelHi = UpperZoneEnd();
329 channelLo = channelHi =
Clip(channelParam, 0, 15);
332 int range =
Clip(rangeParam, 0, 96);
334 for(
int i=channelLo; i <= channelHi; ++i)
336 mChannelStates[i].pitchBendRange = range;
342 if(msg.
StatusMsg() != IMidiMsg::kControlChange)
return false;
344 return(cc == 0x64)||(cc == 0x65)||(cc == 0x26)||(cc == 0x06);
347void MidiSynth::HandleRPN(
IMidiMsg msg)
350 ChannelState& state = mChannelStates[channel];
352 uint8_t valueByte = msg.mData2;
357 state.paramLSB = valueByte;
358 state.valueMSB = state.valueLSB = 0xff;
361 state.paramMSB = valueByte;
362 state.valueMSB = state.valueLSB = 0xff;
365 state.valueLSB = valueByte;
370 state.valueMSB = valueByte;
371 param = ((state.paramMSB&0xFF) << 7) + (state.paramLSB&0xFF);
372 if(state.valueLSB != 0xff)
374 value = ((state.valueMSB&0xFF) << 7) + (state.valueLSB&0xFF);
378 value = state.valueMSB&0xFF;
380 std::cout <<
"RPN received: channel " << channel <<
", param " << param <<
", value " << value <<
"\n";
384 SetChannelPitchBendRange(channel, value);
387 if(IsMasterChannel(channel))
389 SetMPEZones(channel, value);
406 if (mVoicesAreActive | !mMidiQueue.Empty())
408 int blockSize = mBlockSize;
409 int samplesRemaining = nFrames;
412 while(samplesRemaining > 0)
414 if(samplesRemaining < blockSize)
415 blockSize = samplesRemaining;
417 while (!mMidiQueue.Empty())
422 if (msg.mOffset > startIndex + blockSize)
break;
424 if(IsRPNMessage(msg))
432 msg.mOffset -= startIndex;
433 mVoiceAllocator.
AddEvent(MidiMessageToEvent(msg));
439 mVoiceAllocator.ProcessVoices(inputs, outputs, nInputs, nOutputs, startIndex, blockSize);
441 samplesRemaining -= blockSize;
442 startIndex += blockSize;
443 mSampleTime += blockSize;
446 bool voicesbusy =
false;
449 for(
int v = 0; v < NVoices(); v++)
451 bool busy = GetVoice(v)->
GetBusy();
454 activeCount += (busy==
true);
456 if(GetVoice(v)->
GetBusy()) printf(
"X");
460 DBGMSG(
"Num Voices busy %i\n", activeCount);
465 mVoicesAreActive = voicesbusy;
467 mMidiQueue.Flush(nFrames);
477void MidiSynth::SetSampleRateAndBlockSize(
double sampleRate,
int blockSize)
481 mSampleRate = sampleRate;
482 mMidiQueue.Resize(blockSize);
483 mVoiceAllocator.SetSampleRateAndBlockSize(sampleRate, blockSize);
485 for(
int v = 0; v < NVoices(); v++)
bool ProcessBlock(sample **inputs, sample **outputs, int nInputs, int nOutputs, int nFrames)
Processes a block of audio samples.
void SetPitchBendRange(int pitchBendRange)
Set the pitch bend range for non-MPE mode.
virtual bool GetBusy() const =0
virtual void SetSampleRateAndBlockSize(double sampleRate, int blockSize)
Implement this if you need to do work when the sample rate or block size changes.
void AddEvent(VoiceInputEvent e)
Add a single event to the input queue for the current processing block.
void ProcessEvents(int samples, int64_t sampleTime)
Process all input events and generate voice outputs.
BEGIN_IPLUG_NAMESPACE T Clip(T x, T lo, T hi)
Clips the value x between lo and hi.
Encapsulates a MIDI message and provides helper functions.
double ControlChange(EControlChangeMsg idx) const
Get the value of a CC message.
int Channel() const
Gets the channel of a MIDI message.
int Velocity() const
Get the velocity value of a NoteOn/NoteOff message.
int Program() const
Get the program index from a Program Change message.
EStatusMsg
Constants for the status byte of a MIDI message.
int ChannelAfterTouch() const
Get the Pressure value from an AfterTouch message.
EControlChangeMsg ControlChangeIdx() const
Gets the controller index of a CC message.
int PolyAfterTouch() const
Get the Pressure value from a PolyAfterTouch message.
double PitchWheel() const
Get the value from a Pitchwheel message.
int NoteNumber() const
Gets the MIDI note number.
EStatusMsg StatusMsg() const
Gets the MIDI Status message.