#ifndef __MidiPolyphonicExpressionProcessor_h__
#define __MidiPolyphonicExpressionProcessor_h__
#include "MidiProcessor.h"
#include "SignalGenerator.h"
#include "VoiceAllocator.h"
template<class SynthVoice, int VOICES>
class MidiPolyphonicExpressionProcessor : public VoiceAllocator<SynthVoice, VOICES> {
typedef VoiceAllocator<SynthVoice, VOICES> Allocator;
protected:
static const uint16_t TAKEN = 0xffff;
uint8_t notes[VOICES];
uint16_t allocation[VOICES];
uint16_t allocated;
float pressure[VOICES];
float modulation[VOICES];
uint8_t master_channel = 1;
float zone_pitchbend = 0;
float zone_pressure = 0;
float zone_modulation = 0;
float note_pitchbend_range = 48;
void take(uint8_t ch, MidiMessage msg){
release(ch);
notes[ch] = msg.getNote();
allocation[ch] = TAKEN;
Allocator::voice[ch]->noteOn(msg);
}
void release(uint8_t ch){
allocation[ch] = ++allocated;
if(!Allocator::dosustain)
Allocator::voice[ch]->gate(false);
}
public:
MidiPolyphonicExpressionProcessor() {}
virtual ~MidiPolyphonicExpressionProcessor(){};
uint8_t findFreeVoice(MidiMessage msg){
uint8_t note = msg.getNote();
uint16_t minval = USHRT_MAX;
uint8_t minidx = 0;
for(int i=0; i<VOICES; ++i){
if(notes[i] == note){
minidx = i;
break;
}
if(allocation[i] < minval){
minidx = i;
minval = allocation[i];
}
}
return minidx;
}
void releaseVoicesForNote(MidiMessage msg){
uint8_t note = msg.getNote();
for(int i=0; i<VOICES; ++i)
if(notes[i] == note)
release(i);
}
void noteOn(MidiMessage msg){
if(isMasterChannel(msg)){ uint8_t idx = findFreeVoice(msg);
take(idx, msg);
}else{
uint8_t idx = getNoteChannel(msg);
take(idx, msg);
}
}
void noteOff(MidiMessage msg){
if(isMasterChannel(msg)){ releaseVoicesForNote(msg);
}else{
uint8_t ch = getNoteChannel(msg);
if(ch < VOICES)
release(ch);
}
}
void controlChange(MidiMessage msg){
switch(msg.getControllerNumber()){
case MIDI_CC_MODULATION: case MIDI_CC_FREQ_CUTOFF: {
float value = Allocator::mod_range * msg.getControllerValue();
if(isMasterChannel(msg)){ for(int i=0; i<VOICES; ++i)
Allocator::voice[i]->setModulation(value + modulation[i]);
zone_modulation = value;
}else{
uint8_t ch = getNoteChannel(msg);
if(ch < VOICES){
Allocator::voice[ch]->setModulation(zone_modulation + value);
modulation[ch] = value;
}
}
break;
}
default:
Allocator::controlChange(msg);
break;
}
}
void rpn(uint16_t id, uint8_t msb, uint8_t lsb, MidiMessage msg){
switch(id){
case MIDI_RPN_PITCH_BEND_RANGE: {
float semitones = msb + lsb/100.0f; if(isMasterChannel(msg)){
Allocator::setPitchBendRange(semitones);
}else{
note_pitchbend_range = semitones;
}
break;
}
case MIDI_RPN_MPE_CONFIGURATION: {
uint8_t n = msg.getChannel();
uint8_t mm = msg.getControllerValue();
if(mm != 0){
if(n == 0x0) master_channel = 1;
else if(n == 0xf) master_channel = 0xf;
}
break;
}
default:
Allocator::rpn(id, msb, lsb, msg);
break;
}
}
bool isMasterChannel(MidiMessage msg){
return msg.getChannel()+1 == master_channel;
}
uint8_t getNoteChannel(MidiMessage msg){
if(master_channel == 1)
return (msg.getChannel()-1) % VOICES;
else
return (14-msg.getChannel()) % VOICES;
}
void pitchbend(MidiMessage msg){
if(isMasterChannel(msg)){ float value = Allocator::getPitchBendRange()*msg.getPitchBend()/8192.0f;
float delta = value - zone_pitchbend;
for(int i=0; i<VOICES; ++i)
Allocator::voice[i]->setPitchBend(Allocator::voice[i]->getPitchBend()+delta);
zone_pitchbend = value;
}else{
float value = note_pitchbend_range*msg.getPitchBend()/8192.0f;
uint8_t ch = getNoteChannel(msg);
if(ch < VOICES){ Allocator::voice[ch]->setPitchBend(zone_pitchbend + value);
}
}
}
void channelPressure(MidiMessage msg){
float value = msg.getChannelPressure()/128.0f;
if(isMasterChannel(msg)){ for(int i=0; i<VOICES; ++i)
Allocator::voice[i]->setPressure(value + pressure[i]);
zone_pressure = value;
}else{
uint8_t ch = getNoteChannel(msg);
if(ch < VOICES){ Allocator::voice[ch]->setPressure(zone_pressure + value);
pressure[ch] = value;
}
}
}
void polyKeyPressure(MidiMessage msg){
if(isMasterChannel(msg)){
uint8_t note = msg.getNote();
for(int i=0; i<VOICES; ++i)
if(Allocator::voice[i]->getNote() == note)
Allocator::voice[i]->setPressure(msg.getPolyKeyPressure()/128.0f);
}
}
void sustainOff(){
for(int i=0; i<VOICES; ++i){
if(allocation[i] != TAKEN)
Allocator::voice[i]->gate(false);
}
}
};
#endif