mirror of https://github.com/probonopd/MiniDexed
commit
37c152d2c8
@ -0,0 +1,176 @@ |
|||||||
|
/*
|
||||||
|
* DISTHRO 3 Band EQ |
||||||
|
* Ported from https://github.com/DISTRHO/Mini-Series/blob/master/plugins/3BandEQ
|
||||||
|
*
|
||||||
|
* Javier Nonis (https://github.com/jnonis) - 2024
|
||||||
|
*/ |
||||||
|
#ifndef _EFFECT_3BANDEQ_H |
||||||
|
#define _EFFECT_3BANDEQ_H |
||||||
|
|
||||||
|
#include <cmath> |
||||||
|
#include "effect_base.h" |
||||||
|
|
||||||
|
class AudioEffect3BandEQ : public AudioEffect |
||||||
|
{ |
||||||
|
public: |
||||||
|
static constexpr float kAMP_DB = 8.656170245f; |
||||||
|
static constexpr float kDC_ADD = 1e-30f; |
||||||
|
static constexpr float kPI = 3.141592654f; |
||||||
|
|
||||||
|
enum Param |
||||||
|
{ |
||||||
|
BYPASS, |
||||||
|
EQ_LOW, |
||||||
|
EQ_MID, |
||||||
|
EQ_HIGH, |
||||||
|
MASTER, |
||||||
|
LOW_MID_FQ, |
||||||
|
MID_HIGH_FQ, |
||||||
|
UNKNOWN |
||||||
|
}; |
||||||
|
|
||||||
|
AudioEffect3BandEQ(float32_t samplerate) : AudioEffect(samplerate) |
||||||
|
{ |
||||||
|
// Default values
|
||||||
|
fLow = 0.0f; |
||||||
|
fMid = 0.0f; |
||||||
|
fHigh = 0.0f; |
||||||
|
fMaster = 0.0f; |
||||||
|
fLowMidFreq = 440.0f; |
||||||
|
fMidHighFreq = 2000.0f; |
||||||
|
|
||||||
|
// Internal stuff
|
||||||
|
lowVol = midVol = highVol = outVol = 1.0f; |
||||||
|
freqLP = 200.0f; |
||||||
|
freqHP = 2000.0f; |
||||||
|
|
||||||
|
// reset filter values
|
||||||
|
xLP = std::exp(-2.0f * kPI * freqLP / samplerate); |
||||||
|
|
||||||
|
a0LP = 1.0f - xLP; |
||||||
|
b1LP = -xLP; |
||||||
|
|
||||||
|
xHP = std::exp(-2.0f * kPI * freqHP / samplerate); |
||||||
|
a0HP = 1.0f - xHP; |
||||||
|
b1HP = -xHP; |
||||||
|
|
||||||
|
out1LP = out2LP = out1HP = out2HP = 0.0f; |
||||||
|
tmp1LP = tmp2LP = tmp1HP = tmp2HP = 0.0f; |
||||||
|
} |
||||||
|
|
||||||
|
virtual ~AudioEffect3BandEQ() |
||||||
|
{ |
||||||
|
} |
||||||
|
|
||||||
|
virtual unsigned getId() |
||||||
|
{ |
||||||
|
return EFFECT_3BANDEQ; |
||||||
|
} |
||||||
|
|
||||||
|
virtual void setParameter(unsigned param, unsigned value) |
||||||
|
{ |
||||||
|
switch (param) |
||||||
|
{ |
||||||
|
case AudioEffect3BandEQ::Param::BYPASS: |
||||||
|
this->setBypass(value == 1); |
||||||
|
break; |
||||||
|
case AudioEffect3BandEQ::Param::EQ_LOW: |
||||||
|
fLow = (value / 100.0f) * 48.0f - 24.0f; |
||||||
|
lowVol = std::exp( (fLow/48.0f) * 48.0f / kAMP_DB); |
||||||
|
break; |
||||||
|
case AudioEffect3BandEQ::Param::EQ_MID: |
||||||
|
fMid = (value / 100.0f) * 48.0f - 24.0f; |
||||||
|
midVol = std::exp( (fMid/48.0f) * 48.0f / kAMP_DB); |
||||||
|
break; |
||||||
|
case AudioEffect3BandEQ::Param::EQ_HIGH: |
||||||
|
fHigh = (value / 100.0f) * 48.0f - 24.0f; |
||||||
|
highVol = std::exp( (fHigh/48.0f) * 48.0f / kAMP_DB); |
||||||
|
break; |
||||||
|
case AudioEffect3BandEQ::Param::MASTER: |
||||||
|
fMaster = (value / 100.0f) * 48.0f - 24.0f; |
||||||
|
outVol = std::exp( (fMaster/48.0f) * 48.0f / kAMP_DB); |
||||||
|
break; |
||||||
|
case AudioEffect3BandEQ::Param::LOW_MID_FQ: |
||||||
|
fLowMidFreq = std::min((float) value, fMidHighFreq); |
||||||
|
freqLP = fLowMidFreq; |
||||||
|
xLP = std::exp(-2.0f * kPI * freqLP / (float)samplerate); |
||||||
|
a0LP = 1.0f - xLP; |
||||||
|
b1LP = -xLP; |
||||||
|
break; |
||||||
|
case AudioEffect3BandEQ::Param::MID_HIGH_FQ: |
||||||
|
fMidHighFreq = std::max((float) value, fLowMidFreq); |
||||||
|
freqHP = fMidHighFreq; |
||||||
|
xHP = std::exp(-2.0f * kPI * freqHP / (float)samplerate); |
||||||
|
a0HP = 1.0f - xHP; |
||||||
|
b1HP = -xHP; |
||||||
|
break; |
||||||
|
default: |
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
virtual unsigned getParameter(unsigned param) |
||||||
|
{ |
||||||
|
switch (param) |
||||||
|
{ |
||||||
|
case AudioEffect3BandEQ::Param::BYPASS: |
||||||
|
return this->getBypass() ? 1 : 0; |
||||||
|
case AudioEffect3BandEQ::Param::EQ_LOW: |
||||||
|
return roundf(((fLow + 24.0f) / 48.0f) * 100.0f); |
||||||
|
case AudioEffect3BandEQ::Param::EQ_MID: |
||||||
|
return roundf(((fMid + 24.0f) / 48.0f) * 100.0f); |
||||||
|
case AudioEffect3BandEQ::Param::EQ_HIGH: |
||||||
|
return roundf(((fHigh + 24.0f) / 48.0f) * 100.0f); |
||||||
|
case AudioEffect3BandEQ::Param::MASTER: |
||||||
|
return roundf(((fMaster + 24.0f) / 48.0f) * 100.0f); |
||||||
|
case AudioEffect3BandEQ::Param::LOW_MID_FQ: |
||||||
|
return fLowMidFreq; |
||||||
|
case AudioEffect3BandEQ::Param::MID_HIGH_FQ: |
||||||
|
return fMidHighFreq; |
||||||
|
default: |
||||||
|
return 0; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
protected: |
||||||
|
virtual size_t getParametersSize() |
||||||
|
{ |
||||||
|
return AudioEffect3BandEQ::Param::UNKNOWN; |
||||||
|
} |
||||||
|
virtual void doProcess(const float32_t* inblockL, const float32_t* inblockR, float32_t* outblockL, float32_t* outblockR, uint16_t len) |
||||||
|
{ |
||||||
|
const float* in1 = inblockL; |
||||||
|
const float* in2 = inblockR; |
||||||
|
float* out1 = outblockL; |
||||||
|
float* out2 = outblockR; |
||||||
|
|
||||||
|
for (uint32_t i=0; i < len; ++i) |
||||||
|
{ |
||||||
|
tmp1LP = a0LP * in1[i] - b1LP * tmp1LP + kDC_ADD; |
||||||
|
tmp2LP = a0LP * in2[i] - b1LP * tmp2LP + kDC_ADD; |
||||||
|
out1LP = tmp1LP - kDC_ADD; |
||||||
|
out2LP = tmp2LP - kDC_ADD; |
||||||
|
|
||||||
|
tmp1HP = a0HP * in1[i] - b1HP * tmp1HP + kDC_ADD; |
||||||
|
tmp2HP = a0HP * in2[i] - b1HP * tmp2HP + kDC_ADD; |
||||||
|
out1HP = in1[i] - tmp1HP - kDC_ADD; |
||||||
|
out2HP = in2[i] - tmp2HP - kDC_ADD; |
||||||
|
|
||||||
|
out1[i] = (out1LP*lowVol + (in1[i] - out1LP - out1HP)*midVol + out1HP*highVol) * outVol; |
||||||
|
out2[i] = (out2LP*lowVol + (in2[i] - out2LP - out2HP)*midVol + out2HP*highVol) * outVol; |
||||||
|
} |
||||||
|
} |
||||||
|
private: |
||||||
|
float fLow, fMid, fHigh, fMaster, fLowMidFreq, fMidHighFreq; |
||||||
|
|
||||||
|
float lowVol, midVol, highVol, outVol; |
||||||
|
float freqLP, freqHP; |
||||||
|
|
||||||
|
float xLP, a0LP, b1LP; |
||||||
|
float xHP, a0HP, b1HP; |
||||||
|
|
||||||
|
float out1LP, out2LP, out1HP, out2HP; |
||||||
|
float tmp1LP, tmp2LP, tmp1HP, tmp2HP; |
||||||
|
}; |
||||||
|
|
||||||
|
#endif // _EFFECT_3BANDEQ_H
|
@ -0,0 +1,116 @@ |
|||||||
|
/*
|
||||||
|
* MVerb Reverb Port |
||||||
|
* Ported from https://github.com/DISTRHO/MVerb
|
||||||
|
* Original https://github.com/martineastwood/mverb/
|
||||||
|
* |
||||||
|
* Javier Nonis (https://github.com/jnonis) - 2024
|
||||||
|
*/ |
||||||
|
#include "effect_mverb.h" |
||||||
|
|
||||||
|
AudioEffectMVerb::AudioEffectMVerb(float32_t samplerate) : AudioEffect(samplerate) |
||||||
|
{
|
||||||
|
fVerb.setSampleRate(samplerate); |
||||||
|
|
||||||
|
fVerb.setParameter(MVerb<float>::DAMPINGFREQ, 0.5f); |
||||||
|
fVerb.setParameter(MVerb<float>::DENSITY, 0.5f); |
||||||
|
fVerb.setParameter(MVerb<float>::BANDWIDTHFREQ, 0.5f); |
||||||
|
fVerb.setParameter(MVerb<float>::DECAY, 0.5f); |
||||||
|
fVerb.setParameter(MVerb<float>::PREDELAY, 0.5f); |
||||||
|
fVerb.setParameter(MVerb<float>::SIZE, 0.75f); |
||||||
|
fVerb.setParameter(MVerb<float>::GAIN, 1.0f); |
||||||
|
fVerb.setParameter(MVerb<float>::MIX, 0.5f); |
||||||
|
fVerb.setParameter(MVerb<float>::EARLYMIX, 0.5f); |
||||||
|
fVerb.reset(); |
||||||
|
} |
||||||
|
|
||||||
|
AudioEffectMVerb::~AudioEffectMVerb() |
||||||
|
{ |
||||||
|
} |
||||||
|
|
||||||
|
void AudioEffectMVerb::initializeSendFX() |
||||||
|
{ |
||||||
|
this->setParameter(AudioEffectMVerb::Param::MIX, 100); |
||||||
|
} |
||||||
|
|
||||||
|
void AudioEffectMVerb::setParameter(unsigned param, unsigned value) |
||||||
|
{ |
||||||
|
switch (param) |
||||||
|
{ |
||||||
|
case AudioEffectMVerb::Param::BYPASS: |
||||||
|
this->setBypass(value == 1); |
||||||
|
break; |
||||||
|
case AudioEffectMVerb::Param::DAMPINGFREQ: |
||||||
|
fVerb.setParameter(MVerb<float>::DAMPINGFREQ, (float) value / 100.0f); |
||||||
|
break; |
||||||
|
case AudioEffectMVerb::Param::DENSITY: |
||||||
|
fVerb.setParameter(MVerb<float>::DENSITY, (float) value / 100.0f); |
||||||
|
break; |
||||||
|
case AudioEffectMVerb::Param::BANDWIDTHFREQ: |
||||||
|
fVerb.setParameter(MVerb<float>::BANDWIDTHFREQ, (float) value / 100.0f); |
||||||
|
break; |
||||||
|
case AudioEffectMVerb::Param::DECAY: |
||||||
|
fVerb.setParameter(MVerb<float>::DECAY, (float) value / 100.0f); |
||||||
|
break; |
||||||
|
case AudioEffectMVerb::Param::PREDELAY: |
||||||
|
fVerb.setParameter(MVerb<float>::PREDELAY, (float) value / 100.0f); |
||||||
|
break; |
||||||
|
case AudioEffectMVerb::Param::SIZE: |
||||||
|
fVerb.setParameter(MVerb<float>::SIZE, (float) value / 100.0f); |
||||||
|
break; |
||||||
|
case AudioEffectMVerb::Param::GAIN: |
||||||
|
fVerb.setParameter(MVerb<float>::GAIN, (float) value / 100.0f); |
||||||
|
break; |
||||||
|
case AudioEffectMVerb::Param::MIX: |
||||||
|
fVerb.setParameter(MVerb<float>::MIX, (float) value / 100.0f); |
||||||
|
break; |
||||||
|
case AudioEffectMVerb::Param::EARLYMIX: |
||||||
|
fVerb.setParameter(MVerb<float>::EARLYMIX, (float) value / 100.0f); |
||||||
|
break; |
||||||
|
default: |
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
unsigned AudioEffectMVerb::getParameter(unsigned param) |
||||||
|
{ |
||||||
|
switch (param) |
||||||
|
{ |
||||||
|
case AudioEffectMVerb::Param::BYPASS: |
||||||
|
return this->getBypass() ? 1 : 0; |
||||||
|
case AudioEffectMVerb::Param::DAMPINGFREQ: |
||||||
|
return roundf(fVerb.getParameter(MVerb<float>::DAMPINGFREQ) * 100); |
||||||
|
case AudioEffectMVerb::Param::DENSITY: |
||||||
|
return roundf(fVerb.getParameter(MVerb<float>::DENSITY) * 100); |
||||||
|
case AudioEffectMVerb::Param::BANDWIDTHFREQ: |
||||||
|
return roundf(fVerb.getParameter(MVerb<float>::BANDWIDTHFREQ) * 100); |
||||||
|
case AudioEffectMVerb::Param::DECAY: |
||||||
|
return roundf(fVerb.getParameter(MVerb<float>::DECAY) * 100); |
||||||
|
case AudioEffectMVerb::Param::PREDELAY: |
||||||
|
return roundf(fVerb.getParameter(MVerb<float>::PREDELAY) * 100); |
||||||
|
case AudioEffectMVerb::Param::SIZE: |
||||||
|
return roundf(fVerb.getParameter(MVerb<float>::SIZE) * 100); |
||||||
|
case AudioEffectMVerb::Param::GAIN: |
||||||
|
return roundf(fVerb.getParameter(MVerb<float>::GAIN) * 100); |
||||||
|
case AudioEffectMVerb::Param::MIX: |
||||||
|
return roundf(fVerb.getParameter(MVerb<float>::MIX) * 100); |
||||||
|
case AudioEffectMVerb::Param::EARLYMIX: |
||||||
|
return roundf(fVerb.getParameter(MVerb<float>::EARLYMIX) * 100); |
||||||
|
default: |
||||||
|
return 0; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
void AudioEffectMVerb::doProcess(const float32_t* inblockL, const float32_t* inblockR, float32_t* outblockL, float32_t* outblockR, uint16_t len) |
||||||
|
{ |
||||||
|
const float32_t* inputs[2]; |
||||||
|
inputs[0] = inblockL; |
||||||
|
inputs[1] = inblockR; |
||||||
|
|
||||||
|
float32_t* outputs[2]; |
||||||
|
outputs[0] = outblockL; |
||||||
|
outputs[1] = outblockR; |
||||||
|
|
||||||
|
fVerb.process(inputs, outputs, static_cast<int>(len)); |
||||||
|
} |
||||||
|
|
@ -0,0 +1,54 @@ |
|||||||
|
/*
|
||||||
|
* MVerb Reverb Port |
||||||
|
* Ported from https://github.com/DISTRHO/MVerb
|
||||||
|
* Original https://github.com/martineastwood/mverb/
|
||||||
|
* |
||||||
|
* Javier Nonis (https://github.com/jnonis) - 2024
|
||||||
|
*/ |
||||||
|
#ifndef _EFFECT_MVERB_H |
||||||
|
#define _EFFECT_MVERB_H |
||||||
|
|
||||||
|
#include "effect_base.h" |
||||||
|
#include "mverb/MVerb.h" |
||||||
|
|
||||||
|
class AudioEffectMVerb : public AudioEffect |
||||||
|
{ |
||||||
|
public: |
||||||
|
enum Param |
||||||
|
{ |
||||||
|
BYPASS, |
||||||
|
DAMPINGFREQ, |
||||||
|
DENSITY, |
||||||
|
BANDWIDTHFREQ, |
||||||
|
DECAY, |
||||||
|
PREDELAY, |
||||||
|
SIZE, |
||||||
|
GAIN, |
||||||
|
MIX, |
||||||
|
EARLYMIX, |
||||||
|
UNKNOWN |
||||||
|
}; |
||||||
|
|
||||||
|
AudioEffectMVerb(float32_t samplerate); |
||||||
|
virtual ~AudioEffectMVerb(); |
||||||
|
|
||||||
|
virtual unsigned getId() |
||||||
|
{ |
||||||
|
return EFFECT_MVERB; |
||||||
|
} |
||||||
|
|
||||||
|
virtual void initializeSendFX(); |
||||||
|
virtual void setParameter(unsigned param, unsigned value); |
||||||
|
virtual unsigned getParameter(unsigned param); |
||||||
|
protected: |
||||||
|
virtual size_t getParametersSize() |
||||||
|
{ |
||||||
|
return AudioEffectMVerb::Param::UNKNOWN; |
||||||
|
} |
||||||
|
virtual void doProcess(const float32_t* inblockL, const float32_t* inblockR, float32_t* outblockL, float32_t* outblockR, uint16_t len); |
||||||
|
|
||||||
|
private: |
||||||
|
MVerb<float> fVerb; |
||||||
|
}; |
||||||
|
|
||||||
|
#endif // _EFFECT_MVERB_H
|
@ -0,0 +1,169 @@ |
|||||||
|
#include "midi_arp.h" |
||||||
|
#include <stdio.h> |
||||||
|
|
||||||
|
MidiArp::MidiArp(float32_t samplerate, CDexedAdapter* synth) : MidiEffect(samplerate, synth) |
||||||
|
{ |
||||||
|
arpeggiator.transmitHostInfo(0, 4, 1, 1, 120.0); |
||||||
|
arpeggiator.setSampleRate(samplerate); |
||||||
|
|
||||||
|
arpeggiator.setBpm(120); |
||||||
|
|
||||||
|
this->setParameter(MidiArp::Param::LATCH, 0); |
||||||
|
this->setParameter(MidiArp::Param::ARP_MODE, 0); |
||||||
|
this->setParameter(MidiArp::Param::DIVISION, 9); |
||||||
|
this->setParameter(MidiArp::Param::NOTE_LENGTH, 70); |
||||||
|
this->setParameter(MidiArp::Param::VELOCITY, 110); |
||||||
|
this->setParameter(MidiArp::Param::OCTAVE_SPREAD, 1); |
||||||
|
this->setParameter(MidiArp::Param::OCTAVE_MODE, 4); |
||||||
|
} |
||||||
|
|
||||||
|
MidiArp::~MidiArp() |
||||||
|
{ |
||||||
|
} |
||||||
|
|
||||||
|
void MidiArp::setTempo(unsigned tempo) |
||||||
|
{ |
||||||
|
arpeggiator.setBpm(tempo); |
||||||
|
} |
||||||
|
|
||||||
|
void MidiArp::setParameter(unsigned param, unsigned value) |
||||||
|
{ |
||||||
|
switch (param) |
||||||
|
{ |
||||||
|
case MidiArp::Param::BYPASS: |
||||||
|
this->setBypass(value == 1); |
||||||
|
break; |
||||||
|
case MidiArp::Param::LATCH: |
||||||
|
this->arpeggiator.setLatchMode(value == 1); |
||||||
|
break; |
||||||
|
case MidiArp::Param::SYNC: |
||||||
|
this->arpeggiator.setSyncMode(value); |
||||||
|
break; |
||||||
|
case MidiArp::Param::ARP_MODE: |
||||||
|
this->arpeggiator.setArpMode(value); |
||||||
|
break; |
||||||
|
case MidiArp::Param::DIVISION: |
||||||
|
this->arpeggiator.setDivision(value); |
||||||
|
break; |
||||||
|
case MidiArp::Param::NOTE_LENGTH: |
||||||
|
this->arpeggiator.setNoteLength((float) value / 100.0f); |
||||||
|
break; |
||||||
|
case MidiArp::Param::VELOCITY: |
||||||
|
this->arpeggiator.setVelocity(value); |
||||||
|
break; |
||||||
|
case MidiArp::Param::OCTAVE_SPREAD: |
||||||
|
this->arpeggiator.setOctaveSpread(value); |
||||||
|
break; |
||||||
|
case MidiArp::Param::OCTAVE_MODE: |
||||||
|
this->arpeggiator.setOctaveMode(value); |
||||||
|
break; |
||||||
|
case MidiArp::Param::PANIC: |
||||||
|
this->arpeggiator.setPanic(value == 1); |
||||||
|
break; |
||||||
|
default: |
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
unsigned MidiArp::getParameter(unsigned param) |
||||||
|
{ |
||||||
|
switch (param) |
||||||
|
{ |
||||||
|
case MidiArp::Param::BYPASS: |
||||||
|
return this->getBypass() ? 1 : 0; |
||||||
|
case MidiArp::Param::LATCH: |
||||||
|
return this->arpeggiator.getLatchMode() ? 1 : 0; |
||||||
|
case MidiArp::Param::SYNC: |
||||||
|
return this->arpeggiator.getSyncMode(); |
||||||
|
case MidiArp::Param::ARP_MODE: |
||||||
|
return this->arpeggiator.getArpMode(); |
||||||
|
case MidiArp::Param::DIVISION: |
||||||
|
return this->arpeggiator.getDivision(); |
||||||
|
case MidiArp::Param::NOTE_LENGTH: |
||||||
|
return roundf(this->arpeggiator.getNoteLength() * 100); |
||||||
|
case MidiArp::Param::VELOCITY: |
||||||
|
return this->arpeggiator.getVelocity(); |
||||||
|
case MidiArp::Param::OCTAVE_SPREAD: |
||||||
|
return this->arpeggiator.getOctaveSpread(); |
||||||
|
case MidiArp::Param::OCTAVE_MODE: |
||||||
|
return this->arpeggiator.getOctaveMode(); |
||||||
|
case MidiArp::Param::PANIC: |
||||||
|
return this->arpeggiator.getPanic(); |
||||||
|
default: |
||||||
|
return 0; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
void MidiArp::keydown(int16_t pitch, uint8_t velocity) |
||||||
|
{ |
||||||
|
MidiEvent event; |
||||||
|
event.data[0] = MIDI_NOTE_ON << 4; |
||||||
|
event.data[1] = pitch; |
||||||
|
event.data[2] = velocity; |
||||||
|
event.size = 3; |
||||||
|
event.frame = 0; |
||||||
|
this->events.push_back(event); |
||||||
|
} |
||||||
|
|
||||||
|
void MidiArp::keyup(int16_t pitch) |
||||||
|
{ |
||||||
|
MidiEvent event; |
||||||
|
event.data[0] = MIDI_NOTE_OFF << 4; |
||||||
|
event.data[1] = pitch; |
||||||
|
event.data[2] = 0; |
||||||
|
event.size = 3; |
||||||
|
event.frame = 0; |
||||||
|
this->events.push_back(event); |
||||||
|
} |
||||||
|
|
||||||
|
void MidiArp::doProcess(uint16_t len) |
||||||
|
{ |
||||||
|
arpeggiator.emptyMidiBuffer(); |
||||||
|
|
||||||
|
// Check if host supports Bar-Beat-Tick position
|
||||||
|
arpeggiator.setSyncMode(0); |
||||||
|
/*
|
||||||
|
const TimePosition& position = getTimePosition(); |
||||||
|
if (!position.bbt.valid) { |
||||||
|
// set-arpeggiator in free running mode
|
||||||
|
arpeggiator.setSyncMode(0); |
||||||
|
} else { |
||||||
|
arpeggiator.setSyncMode(syncMode); |
||||||
|
arpeggiator.transmitHostInfo(position.playing, position.bbt.beatsPerBar, position.bbt.beat, position.bbt.barBeat, static_cast<float>(position.bbt.beatsPerMinute)); |
||||||
|
} |
||||||
|
*/ |
||||||
|
|
||||||
|
arpeggiator.process(events.data(), events.size(), len); |
||||||
|
events.clear(); |
||||||
|
events.shrink_to_fit(); |
||||||
|
|
||||||
|
struct MidiBuffer buffer = arpeggiator.getMidiBuffer(); |
||||||
|
for (unsigned x = 0; x < buffer.numBufferedEvents + buffer.numBufferedThroughEvents; x++) { |
||||||
|
MidiEvent event = buffer.bufferedEvents[x]; |
||||||
|
unsigned eventType = event.data[0] >> 4; |
||||||
|
|
||||||
|
switch (eventType) |
||||||
|
{ |
||||||
|
case MIDI_NOTE_ON: |
||||||
|
if (event.data[2] > 0) |
||||||
|
{ |
||||||
|
if (event.data[2] <= 127) |
||||||
|
{ |
||||||
|
this->synth->keydown(event.data[1], event.data[2]); |
||||||
|
} |
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
this->synth->keyup(event.data[1]); |
||||||
|
} |
||||||
|
break; |
||||||
|
|
||||||
|
case MIDI_NOTE_OFF: |
||||||
|
this->synth->keyup(event.data[1]); |
||||||
|
break; |
||||||
|
|
||||||
|
default: |
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,116 @@ |
|||||||
|
/*
|
||||||
|
* MOD Arpeggiator port |
||||||
|
* Ported from https://ithub.com/moddevices/mod-arpeggiator-lv2
|
||||||
|
*
|
||||||
|
* Javier Nonis (https://github.com/jnonis) - 2024
|
||||||
|
*/ |
||||||
|
#ifndef _MIDI_ARP_H |
||||||
|
#define _MIDI_ARP_H |
||||||
|
|
||||||
|
#include "midi_effect_base.h" |
||||||
|
#include <vector> |
||||||
|
#include <arm_math.h> |
||||||
|
#include "modarpeggiator/common/commons.h" |
||||||
|
#include "modarpeggiator/arpeggiator.hpp" |
||||||
|
#include "modarpeggiator/common/clock.hpp" |
||||||
|
#include "modarpeggiator/common/pattern.hpp" |
||||||
|
|
||||||
|
class MidiArp : public MidiEffect |
||||||
|
{ |
||||||
|
public: |
||||||
|
// ID must be unique for each MidiEffect
|
||||||
|
static const unsigned ID = 1; |
||||||
|
static constexpr const char* NAME = "Arp"; |
||||||
|
|
||||||
|
enum Param |
||||||
|
{ |
||||||
|
BYPASS, |
||||||
|
LATCH, |
||||||
|
SYNC, |
||||||
|
ARP_MODE, |
||||||
|
DIVISION, |
||||||
|
NOTE_LENGTH, |
||||||
|
VELOCITY, |
||||||
|
OCTAVE_SPREAD, |
||||||
|
OCTAVE_MODE, |
||||||
|
PANIC, |
||||||
|
UNKNOWN |
||||||
|
}; |
||||||
|
|
||||||
|
enum Mode |
||||||
|
{ |
||||||
|
UP, |
||||||
|
DOWN, |
||||||
|
UP_DOWN, |
||||||
|
UP_DOWN_ALT, |
||||||
|
PLAYED, |
||||||
|
RANDOM, |
||||||
|
MODE_UNKNOWN |
||||||
|
}; |
||||||
|
|
||||||
|
enum Division |
||||||
|
{ |
||||||
|
D_1_1, |
||||||
|
D_1_2, |
||||||
|
D_1_3, |
||||||
|
D_1_4, |
||||||
|
D_1_4D, |
||||||
|
D_1_4T, |
||||||
|
D_1_8, |
||||||
|
D_1_8D, |
||||||
|
D_1_8T, |
||||||
|
D_1_16, |
||||||
|
D_1_16D, |
||||||
|
D_1_16T, |
||||||
|
D_1_32, |
||||||
|
D_UNKNOWN |
||||||
|
}; |
||||||
|
|
||||||
|
enum OctMode |
||||||
|
{ |
||||||
|
OM_UP, |
||||||
|
OM_DOWN, |
||||||
|
OM_UP_DOWN, |
||||||
|
OM_DOWN_UP, |
||||||
|
OM_UP_CYCLE, |
||||||
|
OM_UNKNOWN |
||||||
|
}; |
||||||
|
|
||||||
|
|
||||||
|
MidiArp(float32_t samplerate, CDexedAdapter* synth); |
||||||
|
virtual ~MidiArp(); |
||||||
|
|
||||||
|
virtual unsigned getId() |
||||||
|
{ |
||||||
|
return MidiArp::ID; |
||||||
|
} |
||||||
|
|
||||||
|
virtual std::string getName() |
||||||
|
{ |
||||||
|
return MidiArp::NAME; |
||||||
|
} |
||||||
|
|
||||||
|
virtual void setTempo(unsigned tempo); |
||||||
|
virtual void setParameter(unsigned param, unsigned value); |
||||||
|
virtual unsigned getParameter(unsigned param); |
||||||
|
|
||||||
|
virtual void keydown(int16_t pitch, uint8_t velocity); |
||||||
|
virtual void keyup(int16_t pitch); |
||||||
|
|
||||||
|
protected: |
||||||
|
virtual size_t getParametersSize() |
||||||
|
{ |
||||||
|
return MidiArp::Param::UNKNOWN; |
||||||
|
} |
||||||
|
|
||||||
|
virtual void doProcess(uint16_t len); |
||||||
|
|
||||||
|
private: |
||||||
|
static const unsigned MIDI_NOTE_OFF = 0b1000; |
||||||
|
static const unsigned MIDI_NOTE_ON = 0b1001; |
||||||
|
|
||||||
|
Arpeggiator arpeggiator; |
||||||
|
std::vector<MidiEvent> events; |
||||||
|
}; |
||||||
|
|
||||||
|
#endif // _MIDI_ARP_H
|
@ -0,0 +1,112 @@ |
|||||||
|
/*
|
||||||
|
* Base MidiEffect interface |
||||||
|
* Javier Nonis (https://github.com/jnonis) - 2024
|
||||||
|
*/ |
||||||
|
#ifndef _MIDI_EFFECT_H |
||||||
|
#define _MIDI_EFFECT_H |
||||||
|
|
||||||
|
#include <string> |
||||||
|
#include <vector> |
||||||
|
#include "dexedadapter.h" |
||||||
|
|
||||||
|
class MidiEffect |
||||||
|
{ |
||||||
|
public: |
||||||
|
// ID must be unique for each MidiEffect
|
||||||
|
static const unsigned ID = 0; |
||||||
|
static constexpr const char* NAME = "None"; |
||||||
|
|
||||||
|
MidiEffect(float32_t samplerate, CDexedAdapter* synth) |
||||||
|
{ |
||||||
|
this->samplerate = samplerate; |
||||||
|
this->synth = synth; |
||||||
|
} |
||||||
|
|
||||||
|
virtual ~MidiEffect() |
||||||
|
{ |
||||||
|
} |
||||||
|
|
||||||
|
virtual unsigned getId() |
||||||
|
{ |
||||||
|
return MidiEffect::ID; |
||||||
|
} |
||||||
|
|
||||||
|
virtual std::string getName() |
||||||
|
{ |
||||||
|
return MidiEffect::NAME; |
||||||
|
} |
||||||
|
|
||||||
|
void setBypass(bool bypass) |
||||||
|
{ |
||||||
|
this->bypass = bypass; |
||||||
|
} |
||||||
|
|
||||||
|
bool getBypass() |
||||||
|
{ |
||||||
|
return this->getId() == MidiEffect::ID ? true : this->bypass; |
||||||
|
} |
||||||
|
|
||||||
|
virtual void setTempo(unsigned tempo) |
||||||
|
{ |
||||||
|
} |
||||||
|
|
||||||
|
virtual void setParameter(unsigned param, unsigned value) |
||||||
|
{ |
||||||
|
} |
||||||
|
|
||||||
|
virtual unsigned getParameter(unsigned param) |
||||||
|
{ |
||||||
|
return 0; |
||||||
|
} |
||||||
|
|
||||||
|
void setParameters(std::vector<unsigned> params) |
||||||
|
{ |
||||||
|
for (size_t i = 0; i < params.size(); i++) |
||||||
|
{ |
||||||
|
this->setParameter(i, params[i]); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
std::vector<unsigned> getParameters() |
||||||
|
{ |
||||||
|
size_t len = this->getParametersSize(); |
||||||
|
std::vector<unsigned> params; |
||||||
|
for (size_t i = 0; i < len; i++) |
||||||
|
{ |
||||||
|
params.push_back(getParameter(i)); |
||||||
|
} |
||||||
|
return params; |
||||||
|
} |
||||||
|
|
||||||
|
virtual void keydown(int16_t pitch, uint8_t velocity) |
||||||
|
{ |
||||||
|
} |
||||||
|
|
||||||
|
virtual void keyup(int16_t pitch) |
||||||
|
{ |
||||||
|
} |
||||||
|
|
||||||
|
void process(uint16_t len) |
||||||
|
{ |
||||||
|
if (this->getBypass()) |
||||||
|
{ |
||||||
|
return; |
||||||
|
} |
||||||
|
this->doProcess(len); |
||||||
|
} |
||||||
|
protected: |
||||||
|
bool bypass = false; |
||||||
|
float32_t samplerate; |
||||||
|
CDexedAdapter* synth; |
||||||
|
|
||||||
|
virtual size_t getParametersSize() |
||||||
|
{ |
||||||
|
return 0; |
||||||
|
} |
||||||
|
|
||||||
|
virtual void doProcess(uint16_t len) |
||||||
|
{ |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
#endif // _MIDI_EFFECT_H
|
@ -0,0 +1,107 @@ |
|||||||
|
#ifndef _MIDI_EFFECTS_H |
||||||
|
#define _MIDI_EFFECTS_H |
||||||
|
|
||||||
|
#include "midi_effect_base.h" |
||||||
|
#include "midi_arp.h" |
||||||
|
|
||||||
|
inline MidiEffect* newMidiEffect(unsigned type, float32_t samplerate, CDexedAdapter* synth) |
||||||
|
{ |
||||||
|
switch (type) |
||||||
|
{ |
||||||
|
case MidiArp::ID: |
||||||
|
return new MidiArp(samplerate, synth); |
||||||
|
default: |
||||||
|
return new MidiEffect(samplerate, synth); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
inline std::string getMidiFXTypeName(int type) |
||||||
|
{ |
||||||
|
switch (type) |
||||||
|
{ |
||||||
|
case MidiArp::ID: |
||||||
|
return MidiArp::NAME; |
||||||
|
case MidiEffect::ID: |
||||||
|
default: |
||||||
|
return MidiEffect::NAME; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
inline std::string ToMidiFXType (int value) |
||||||
|
{ |
||||||
|
return getMidiFXTypeName(value); |
||||||
|
} |
||||||
|
|
||||||
|
inline std::string ToArpDivision (int value) |
||||||
|
{ |
||||||
|
switch (value) |
||||||
|
{ |
||||||
|
case MidiArp::Division::D_1_1: |
||||||
|
return "1/1"; |
||||||
|
case MidiArp::Division::D_1_2: |
||||||
|
return "1/2"; |
||||||
|
case MidiArp::Division::D_1_3: |
||||||
|
return "1/3"; |
||||||
|
case MidiArp::Division::D_1_4: |
||||||
|
return "1/4"; |
||||||
|
case MidiArp::Division::D_1_4D: |
||||||
|
return "1/4."; |
||||||
|
case MidiArp::Division::D_1_4T: |
||||||
|
return "1/4T"; |
||||||
|
case MidiArp::Division::D_1_8: |
||||||
|
return "1/8"; |
||||||
|
case MidiArp::Division::D_1_8D: |
||||||
|
return "1/8."; |
||||||
|
case MidiArp::Division::D_1_8T: |
||||||
|
return "1/8T"; |
||||||
|
case MidiArp::Division::D_1_16: |
||||||
|
return "1/16"; |
||||||
|
case MidiArp::Division::D_1_16D: |
||||||
|
return "1/16."; |
||||||
|
case MidiArp::Division::D_1_16T: |
||||||
|
return "1/16T"; |
||||||
|
case MidiArp::Division::D_1_32: |
||||||
|
default: |
||||||
|
return "1/32"; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
inline std::string ToArpMode (int value) |
||||||
|
{ |
||||||
|
switch (value) |
||||||
|
{ |
||||||
|
case MidiArp::Mode::UP: |
||||||
|
return "Up"; |
||||||
|
case MidiArp::Mode::DOWN: |
||||||
|
return "Down"; |
||||||
|
case MidiArp::Mode::UP_DOWN: |
||||||
|
return "Up-Down"; |
||||||
|
case MidiArp::Mode::UP_DOWN_ALT: |
||||||
|
return "Up-Down Alt"; |
||||||
|
case MidiArp::Mode::PLAYED: |
||||||
|
return "Played"; |
||||||
|
case MidiArp::Mode::RANDOM: |
||||||
|
default: |
||||||
|
return "Random"; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
inline std::string ToArpOctMode (int value) |
||||||
|
{ |
||||||
|
switch (value) |
||||||
|
{ |
||||||
|
case MidiArp::OctMode::OM_UP: |
||||||
|
return "Up"; |
||||||
|
case MidiArp::OctMode::OM_DOWN: |
||||||
|
return "Down"; |
||||||
|
case MidiArp::OctMode::OM_UP_DOWN: |
||||||
|
return "Up-Down"; |
||||||
|
case MidiArp::OctMode::OM_DOWN_UP: |
||||||
|
return "Down-Up"; |
||||||
|
case MidiArp::OctMode::OM_UP_CYCLE: |
||||||
|
default: |
||||||
|
return "Up-Cycle"; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
#endif // _MIDI_EFFECTS_H
|
@ -0,0 +1,648 @@ |
|||||||
|
#include "arpeggiator.hpp" |
||||||
|
|
||||||
|
Arpeggiator::Arpeggiator() : |
||||||
|
notesPressed(0), |
||||||
|
activeNotes(0), |
||||||
|
notePlayed(0), |
||||||
|
octaveMode(0), |
||||||
|
octaveSpread(1), |
||||||
|
arpMode(0), |
||||||
|
noteLength(0.8), |
||||||
|
pitch(0), |
||||||
|
previousMidiNote(0), |
||||||
|
velocity(80), |
||||||
|
previousSyncMode(0), |
||||||
|
activeNotesIndex(0), |
||||||
|
activeNotesBypassed(0), |
||||||
|
timeOutTime(1000), |
||||||
|
firstNoteTimer(0), |
||||||
|
barBeat(0.0), |
||||||
|
pluginEnabled(true), |
||||||
|
first(true), |
||||||
|
arpEnabled(true), |
||||||
|
latchMode(false), |
||||||
|
previousLatch(false), |
||||||
|
latchPlaying(false), |
||||||
|
trigger(false), |
||||||
|
firstNote(false), |
||||||
|
quantizedStart(false), |
||||||
|
resetPattern(false), |
||||||
|
midiNotesCopied(false), |
||||||
|
panic(false), |
||||||
|
division(0), |
||||||
|
sampleRate(48000), |
||||||
|
bpm(0) |
||||||
|
{ |
||||||
|
clock.transmitHostInfo(0, 4, 1, 1, 120.0); |
||||||
|
clock.setSampleRate(static_cast<float>(48000.0)); |
||||||
|
clock.setDivision(7); |
||||||
|
|
||||||
|
arpPattern = new Pattern*[6]; |
||||||
|
|
||||||
|
arpPattern[0] = new PatternUp(); |
||||||
|
arpPattern[1] = new PatternDown(); |
||||||
|
arpPattern[2] = new PatternUpDown(); |
||||||
|
arpPattern[3] = new PatternUpDownAlt(); |
||||||
|
arpPattern[4] = new PatternUp(); |
||||||
|
arpPattern[5] = new PatternRandom(); |
||||||
|
|
||||||
|
|
||||||
|
octavePattern = new Pattern*[5]; |
||||||
|
|
||||||
|
octavePattern[0] = new PatternUp(); |
||||||
|
octavePattern[1] = new PatternDown(); |
||||||
|
octavePattern[2] = new PatternUpDown(); |
||||||
|
octavePattern[3] = new PatternUpDownAlt(); |
||||||
|
octavePattern[4] = new PatternCycle(); |
||||||
|
|
||||||
|
for (unsigned i = 0; i < NUM_VOICES; i++) { |
||||||
|
midiNotes[i][0] = EMPTY_SLOT; |
||||||
|
midiNotes[i][1] = 0; |
||||||
|
midiNotesBypassed[i] = EMPTY_SLOT; |
||||||
|
} |
||||||
|
for (unsigned i = 0; i < NUM_VOICES; i++) { |
||||||
|
noteOffBuffer[i][MIDI_NOTE] = EMPTY_SLOT; |
||||||
|
noteOffBuffer[i][MIDI_CHANNEL] = 0; |
||||||
|
noteOffBuffer[i][TIMER] = 0; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
Arpeggiator::~Arpeggiator() |
||||||
|
{ |
||||||
|
|
||||||
|
delete arpPattern[0]; |
||||||
|
delete arpPattern[1]; |
||||||
|
delete arpPattern[2]; |
||||||
|
delete arpPattern[3]; |
||||||
|
delete arpPattern[4]; |
||||||
|
delete arpPattern[5]; |
||||||
|
delete octavePattern[0]; |
||||||
|
delete octavePattern[1]; |
||||||
|
delete octavePattern[2]; |
||||||
|
delete octavePattern[3]; |
||||||
|
delete octavePattern[4]; |
||||||
|
|
||||||
|
delete[] arpPattern; |
||||||
|
arpPattern = nullptr; |
||||||
|
delete[] octavePattern; |
||||||
|
octavePattern = nullptr; |
||||||
|
} |
||||||
|
|
||||||
|
void Arpeggiator::setArpEnabled(bool arpEnabled) |
||||||
|
{ |
||||||
|
this->arpEnabled = arpEnabled; |
||||||
|
} |
||||||
|
|
||||||
|
void Arpeggiator::setLatchMode(bool latchMode) |
||||||
|
{ |
||||||
|
this->latchMode = latchMode; |
||||||
|
} |
||||||
|
|
||||||
|
void Arpeggiator::setSampleRate(float newSampleRate) |
||||||
|
{ |
||||||
|
if (newSampleRate != sampleRate) { |
||||||
|
clock.setSampleRate(newSampleRate); |
||||||
|
sampleRate = newSampleRate; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
void Arpeggiator::setSyncMode(int mode) |
||||||
|
{ |
||||||
|
|
||||||
|
switch (mode) |
||||||
|
{ |
||||||
|
case FREE_RUNNING: |
||||||
|
clock.setSyncMode(FREE_RUNNING); |
||||||
|
quantizedStart = false; |
||||||
|
break; |
||||||
|
case HOST_BPM_SYNC: |
||||||
|
clock.setSyncMode(HOST_BPM_SYNC); |
||||||
|
quantizedStart = false; |
||||||
|
break; |
||||||
|
case HOST_QUANTIZED_SYNC: |
||||||
|
clock.setSyncMode(HOST_QUANTIZED_SYNC); |
||||||
|
quantizedStart = true; |
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
void Arpeggiator::setBpm(double newBpm) |
||||||
|
{ |
||||||
|
if (newBpm != bpm) { |
||||||
|
clock.setInternalBpmValue(static_cast<float>(newBpm)); |
||||||
|
bpm = newBpm; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
void Arpeggiator::setDivision(int newDivision) |
||||||
|
{ |
||||||
|
if (newDivision != division) { |
||||||
|
clock.setDivision(newDivision); |
||||||
|
division = newDivision; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
void Arpeggiator::setVelocity(uint8_t velocity) |
||||||
|
{ |
||||||
|
this->velocity = velocity; |
||||||
|
} |
||||||
|
|
||||||
|
void Arpeggiator::setNoteLength(float noteLength) |
||||||
|
{ |
||||||
|
this->noteLength = noteLength; |
||||||
|
} |
||||||
|
|
||||||
|
void Arpeggiator::setOctaveSpread(int octaveSpread) |
||||||
|
{ |
||||||
|
this->octaveSpread = octaveSpread; |
||||||
|
} |
||||||
|
|
||||||
|
void Arpeggiator::setArpMode(int arpMode) |
||||||
|
{ |
||||||
|
arpPattern[arpMode]->setStep(arpPattern[this->arpMode]->getStep()); |
||||||
|
arpPattern[arpMode]->setDirection(arpPattern[this->arpMode]->getDirection()); |
||||||
|
|
||||||
|
this->arpMode = arpMode; |
||||||
|
} |
||||||
|
|
||||||
|
void Arpeggiator::setOctaveMode(int octaveMode) |
||||||
|
{ |
||||||
|
octavePattern[octaveMode]->setStep(octavePattern[this->octaveMode]->getStep()); |
||||||
|
octavePattern[octaveMode]->setDirection(octavePattern[this->octaveMode]->getDirection()); |
||||||
|
|
||||||
|
this->octaveMode = octaveMode; |
||||||
|
} |
||||||
|
|
||||||
|
void Arpeggiator::setPanic(bool panic) |
||||||
|
{ |
||||||
|
this->panic = panic; |
||||||
|
} |
||||||
|
|
||||||
|
bool Arpeggiator::getArpEnabled() const |
||||||
|
{ |
||||||
|
return arpEnabled; |
||||||
|
} |
||||||
|
|
||||||
|
bool Arpeggiator::getLatchMode() const |
||||||
|
{ |
||||||
|
return latchMode; |
||||||
|
} |
||||||
|
|
||||||
|
float Arpeggiator::getSampleRate() const |
||||||
|
{ |
||||||
|
return clock.getSampleRate(); |
||||||
|
} |
||||||
|
|
||||||
|
int Arpeggiator::getSyncMode() const |
||||||
|
{ |
||||||
|
return clock.getSyncMode(); |
||||||
|
} |
||||||
|
|
||||||
|
float Arpeggiator::getBpm() const |
||||||
|
{ |
||||||
|
return clock.getInternalBpmValue(); |
||||||
|
} |
||||||
|
|
||||||
|
int Arpeggiator::getDivision() const |
||||||
|
{ |
||||||
|
return clock.getDivision(); |
||||||
|
} |
||||||
|
|
||||||
|
uint8_t Arpeggiator::getVelocity() const |
||||||
|
{ |
||||||
|
return velocity; |
||||||
|
} |
||||||
|
|
||||||
|
float Arpeggiator::getNoteLength() const |
||||||
|
{ |
||||||
|
return noteLength; |
||||||
|
} |
||||||
|
|
||||||
|
int Arpeggiator::getOctaveSpread() const |
||||||
|
{ |
||||||
|
return octaveSpread; |
||||||
|
} |
||||||
|
|
||||||
|
int Arpeggiator::getArpMode() const |
||||||
|
{ |
||||||
|
return arpMode; |
||||||
|
} |
||||||
|
|
||||||
|
int Arpeggiator::getOctaveMode() const |
||||||
|
{ |
||||||
|
return octaveMode; |
||||||
|
} |
||||||
|
|
||||||
|
bool Arpeggiator::getPanic() const |
||||||
|
{ |
||||||
|
return panic; |
||||||
|
} |
||||||
|
|
||||||
|
void Arpeggiator::transmitHostInfo(const bool playing, const float beatsPerBar, |
||||||
|
const int beat, const float barBeat, const double bpm) |
||||||
|
{ |
||||||
|
clock.transmitHostInfo(playing, beatsPerBar, beat, barBeat, bpm); |
||||||
|
this->barBeat = barBeat; |
||||||
|
} |
||||||
|
|
||||||
|
void Arpeggiator::reset() |
||||||
|
{ |
||||||
|
clock.reset(); |
||||||
|
clock.setNumBarsElapsed(0); |
||||||
|
|
||||||
|
for (unsigned a = 0; a < NUM_ARP_MODES; a++) { |
||||||
|
arpPattern[arpMode]->reset(); |
||||||
|
} |
||||||
|
for (unsigned o = 0; o < NUM_OCTAVE_MODES; o++) { |
||||||
|
octavePattern[o]->reset(); |
||||||
|
} |
||||||
|
|
||||||
|
activeNotesIndex = 0; |
||||||
|
firstNoteTimer = 0; |
||||||
|
notePlayed = 0; |
||||||
|
activeNotes = 0; |
||||||
|
notesPressed = 0; |
||||||
|
activeNotesBypassed = 0; |
||||||
|
latchPlaying = false; |
||||||
|
firstNote = false; |
||||||
|
first = true; |
||||||
|
|
||||||
|
for (unsigned i = 0; i < NUM_VOICES; i++) { |
||||||
|
midiNotes[i][MIDI_NOTE] = EMPTY_SLOT; |
||||||
|
midiNotes[i][MIDI_CHANNEL] = 0; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
void Arpeggiator::emptyMidiBuffer() |
||||||
|
{ |
||||||
|
midiHandler.emptyMidiBuffer(); |
||||||
|
} |
||||||
|
|
||||||
|
void Arpeggiator::allNotesOff() |
||||||
|
{ |
||||||
|
for (unsigned i = 0; i < NUM_VOICES; i++) { |
||||||
|
midiNotesBypassed[i] = EMPTY_SLOT; |
||||||
|
} |
||||||
|
notesPressed = 0; |
||||||
|
activeNotes = 0; |
||||||
|
reset(); |
||||||
|
} |
||||||
|
|
||||||
|
struct MidiBuffer Arpeggiator::getMidiBuffer() |
||||||
|
{ |
||||||
|
return midiHandler.getMidiBuffer(); |
||||||
|
} |
||||||
|
|
||||||
|
void Arpeggiator::process(const MidiEvent* events, uint32_t eventCount, uint32_t n_frames) |
||||||
|
{ |
||||||
|
struct MidiEvent midiEvent; |
||||||
|
struct MidiEvent midiThroughEvent; |
||||||
|
|
||||||
|
if (!arpEnabled && !latchMode) { |
||||||
|
|
||||||
|
reset(); |
||||||
|
|
||||||
|
for (unsigned clear_notes = 0; clear_notes < NUM_VOICES; clear_notes++) { |
||||||
|
midiNotes[clear_notes][MIDI_NOTE] = EMPTY_SLOT; |
||||||
|
midiNotes[clear_notes][MIDI_CHANNEL] = 0; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
if (!latchMode && previousLatch && notesPressed <= 0) { |
||||||
|
reset(); |
||||||
|
} |
||||||
|
if (latchMode != previousLatch) { |
||||||
|
previousLatch = latchMode; |
||||||
|
} |
||||||
|
|
||||||
|
if (panic) { |
||||||
|
reset(); |
||||||
|
panic = false; |
||||||
|
} |
||||||
|
|
||||||
|
for (uint32_t i=0; i<eventCount; ++i) { |
||||||
|
|
||||||
|
uint8_t status = events[i].data[0] & 0xF0; |
||||||
|
|
||||||
|
uint8_t midiNote = events[i].data[1]; |
||||||
|
uint8_t noteToFind; |
||||||
|
uint8_t foundNote; |
||||||
|
size_t searchNote; |
||||||
|
|
||||||
|
if (arpEnabled) { |
||||||
|
|
||||||
|
if (!latchPlaying && (midiNote == 0x7b && events[i].size == 3)) { |
||||||
|
allNotesOff(); |
||||||
|
} |
||||||
|
|
||||||
|
midiNotesCopied = false; |
||||||
|
|
||||||
|
bool voiceFound; |
||||||
|
bool pitchFound; |
||||||
|
bool noteOffFoundInBuffer; |
||||||
|
size_t findFreeVoice; |
||||||
|
size_t findActivePitch; |
||||||
|
|
||||||
|
uint8_t channel = events[i].data[0] & 0x0F; |
||||||
|
|
||||||
|
switch(status) { |
||||||
|
case MIDI_NOTEON: |
||||||
|
if (activeNotes > NUM_VOICES - 1) { |
||||||
|
reset(); |
||||||
|
} else { |
||||||
|
if (first) { |
||||||
|
firstNote = true; |
||||||
|
} |
||||||
|
if (notesPressed == 0) { |
||||||
|
if (!latchPlaying) { //TODO check if there needs to be an exception when using sync
|
||||||
|
octavePattern[octaveMode]->reset(); |
||||||
|
clock.reset(); |
||||||
|
notePlayed = 0; |
||||||
|
} |
||||||
|
if (latchMode) { |
||||||
|
latchPlaying = true; |
||||||
|
activeNotes = 0; |
||||||
|
for (unsigned i = 0; i < NUM_VOICES; i++) { |
||||||
|
midiNotes[i][MIDI_NOTE] = EMPTY_SLOT; |
||||||
|
midiNotes[i][MIDI_CHANNEL] = 0; |
||||||
|
} |
||||||
|
} |
||||||
|
resetPattern = true; |
||||||
|
} |
||||||
|
|
||||||
|
findFreeVoice = 0; |
||||||
|
findActivePitch = 0; |
||||||
|
voiceFound = false; |
||||||
|
pitchFound = false; |
||||||
|
|
||||||
|
while (findActivePitch < NUM_VOICES && !pitchFound) |
||||||
|
{ |
||||||
|
if (midiNotes[findActivePitch][MIDI_NOTE] == (uint32_t)midiNote) { |
||||||
|
pitchFound = true; |
||||||
|
} |
||||||
|
findActivePitch++; |
||||||
|
} |
||||||
|
|
||||||
|
if (!pitchFound) { |
||||||
|
while (findFreeVoice < NUM_VOICES && !voiceFound) |
||||||
|
{ |
||||||
|
if (midiNotes[findFreeVoice][MIDI_NOTE] == EMPTY_SLOT) { |
||||||
|
midiNotes[findFreeVoice][MIDI_NOTE] = midiNote; |
||||||
|
midiNotes[findFreeVoice][MIDI_CHANNEL] = channel; |
||||||
|
voiceFound = true; |
||||||
|
} |
||||||
|
findFreeVoice++; |
||||||
|
} |
||||||
|
notesPressed++; |
||||||
|
activeNotes++; |
||||||
|
} |
||||||
|
|
||||||
|
if (arpMode != ARP_PLAYED) |
||||||
|
utils.quicksort(midiNotes, 0, NUM_VOICES - 1); |
||||||
|
if (midiNote < midiNotes[notePlayed - 1][MIDI_NOTE] && notePlayed > 0) { |
||||||
|
notePlayed++; |
||||||
|
} |
||||||
|
} |
||||||
|
break; |
||||||
|
case MIDI_NOTEOFF: |
||||||
|
searchNote = 0; |
||||||
|
foundNote = 0; |
||||||
|
noteOffFoundInBuffer = false; |
||||||
|
noteToFind = midiNote; |
||||||
|
|
||||||
|
if (!latchMode) { |
||||||
|
latchPlaying = false; |
||||||
|
} else { |
||||||
|
latchPlaying = true; |
||||||
|
} |
||||||
|
|
||||||
|
while (searchNote < NUM_VOICES) |
||||||
|
{ |
||||||
|
if (midiNotes[searchNote][MIDI_NOTE] == noteToFind) |
||||||
|
{ |
||||||
|
foundNote = searchNote; |
||||||
|
noteOffFoundInBuffer = true; |
||||||
|
searchNote = NUM_VOICES; |
||||||
|
} |
||||||
|
searchNote++; |
||||||
|
} |
||||||
|
|
||||||
|
if (noteOffFoundInBuffer) { |
||||||
|
|
||||||
|
notesPressed = (notesPressed > 0) ? notesPressed - 1 : 0; |
||||||
|
|
||||||
|
if (!latchPlaying) { |
||||||
|
activeNotes = notesPressed; |
||||||
|
} |
||||||
|
|
||||||
|
if (!latchMode) { |
||||||
|
midiNotes[foundNote][MIDI_NOTE] = EMPTY_SLOT; |
||||||
|
midiNotes[foundNote][MIDI_CHANNEL] = 0; |
||||||
|
if (arpMode != ARP_PLAYED) |
||||||
|
utils.quicksort(midiNotes, 0, NUM_VOICES - 1); |
||||||
|
} |
||||||
|
} else { |
||||||
|
midiThroughEvent.frame = events[i].frame; |
||||||
|
midiThroughEvent.size = events[i].size; |
||||||
|
for (unsigned d = 0; d < midiThroughEvent.size; d++) { |
||||||
|
midiThroughEvent.data[d] = events[i].data[d]; |
||||||
|
} |
||||||
|
midiHandler.appendMidiThroughMessage(midiThroughEvent); |
||||||
|
} |
||||||
|
if (activeNotes == 0 && !latchPlaying && !latchMode) { |
||||||
|
reset(); |
||||||
|
} |
||||||
|
break; |
||||||
|
default: |
||||||
|
midiThroughEvent.frame = events[i].frame; |
||||||
|
midiThroughEvent.size = events[i].size; |
||||||
|
for (unsigned d = 0; d < midiThroughEvent.size; d++) { |
||||||
|
midiThroughEvent.data[d] = events[i].data[d]; |
||||||
|
} |
||||||
|
midiHandler.appendMidiThroughMessage(midiThroughEvent); |
||||||
|
break; |
||||||
|
} |
||||||
|
} else { //if arpeggiator is off
|
||||||
|
|
||||||
|
if (!midiNotesCopied) { |
||||||
|
for (unsigned b = 0; b < NUM_VOICES; b++) { |
||||||
|
midiNotesBypassed[b] = midiNotes[b][MIDI_NOTE]; |
||||||
|
} |
||||||
|
midiNotesCopied = true; |
||||||
|
} |
||||||
|
|
||||||
|
if (latchMode) { |
||||||
|
|
||||||
|
uint8_t noteToFind = midiNote; |
||||||
|
size_t searchNote = 0; |
||||||
|
|
||||||
|
switch (status) |
||||||
|
{ |
||||||
|
case MIDI_NOTEOFF: |
||||||
|
while (searchNote < NUM_VOICES) |
||||||
|
{ |
||||||
|
if (midiNotesBypassed[searchNote] == noteToFind) { |
||||||
|
midiNotesBypassed[searchNote] = EMPTY_SLOT; |
||||||
|
searchNote = NUM_VOICES; |
||||||
|
notesPressed = (notesPressed > 0) ? notesPressed - 1 : 0; |
||||||
|
} |
||||||
|
searchNote++; |
||||||
|
} |
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
if (midiNote == 0x7b && events[i].size == 3) { |
||||||
|
allNotesOff(); |
||||||
|
} |
||||||
|
//send MIDI message through
|
||||||
|
midiHandler.appendMidiThroughMessage(events[i]); |
||||||
|
first = true; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
arpPattern[arpMode]->setPatternSize(activeNotes); |
||||||
|
|
||||||
|
int patternSize; |
||||||
|
|
||||||
|
switch (arpMode) |
||||||
|
{ |
||||||
|
case ARP_UP_DOWN: |
||||||
|
patternSize = (activeNotes >= 3) ? activeNotes + (activeNotes - 2) : activeNotes; |
||||||
|
break; |
||||||
|
case ARP_UP_DOWN_ALT: |
||||||
|
patternSize = (activeNotes >= 3) ? activeNotes * 2 : activeNotes; |
||||||
|
break; |
||||||
|
default: |
||||||
|
patternSize = activeNotes; |
||||||
|
break; |
||||||
|
} |
||||||
|
|
||||||
|
switch (octaveMode) |
||||||
|
{ |
||||||
|
case ONE_OCT_UP_PER_CYCLE: |
||||||
|
octavePattern[octaveMode]->setPatternSize(patternSize); |
||||||
|
octavePattern[octaveMode]->setCycleRange(octaveSpread); |
||||||
|
break; |
||||||
|
default: |
||||||
|
octavePattern[octaveMode]->setPatternSize(octaveSpread); |
||||||
|
break; |
||||||
|
} |
||||||
|
|
||||||
|
for (unsigned s = 0; s < n_frames; s++) { |
||||||
|
|
||||||
|
bool timeOut = (firstNoteTimer > (int)timeOutTime) ? false : true; |
||||||
|
|
||||||
|
if (firstNote) { |
||||||
|
clock.closeGate(); //close gate to prevent opening before timeOut
|
||||||
|
firstNoteTimer++; |
||||||
|
} |
||||||
|
|
||||||
|
if (clock.getSyncMode() <= 1 && first) { |
||||||
|
clock.setPos(0); |
||||||
|
clock.reset(); |
||||||
|
} |
||||||
|
|
||||||
|
clock.tick(); |
||||||
|
|
||||||
|
if ((clock.getGate() && !timeOut)) { |
||||||
|
|
||||||
|
if (arpEnabled) { |
||||||
|
|
||||||
|
if (resetPattern) { |
||||||
|
octavePattern[octaveMode]->reset(); |
||||||
|
if (octaveMode == ARP_DOWN) { |
||||||
|
octavePattern[octaveMode]->setStep(activeNotes - 1); //TODO maybe put this in reset()
|
||||||
|
} |
||||||
|
|
||||||
|
arpPattern[arpMode]->reset(); |
||||||
|
if (arpMode == ARP_DOWN) { |
||||||
|
arpPattern[arpMode]->setStep(activeNotes - 1); |
||||||
|
} |
||||||
|
|
||||||
|
resetPattern = false; |
||||||
|
|
||||||
|
notePlayed = arpPattern[arpMode]->getStep(); |
||||||
|
} |
||||||
|
|
||||||
|
if (first) { |
||||||
|
//send all notes off, on current active MIDI channel
|
||||||
|
midiEvent.size = 3; |
||||||
|
midiEvent.data[2] = 0; |
||||||
|
|
||||||
|
midiEvent.frame = s; |
||||||
|
midiEvent.data[0] = 0xb0 | midiNotes[notePlayed][MIDI_CHANNEL]; |
||||||
|
midiEvent.data[1] = 0x40; // sustain pedal
|
||||||
|
midiHandler.appendMidiMessage(midiEvent); |
||||||
|
midiEvent.data[1] = 0x7b; // all notes off
|
||||||
|
midiHandler.appendMidiMessage(midiEvent); |
||||||
|
|
||||||
|
first = false; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
size_t searchedVoices = 0; |
||||||
|
bool noteFound = false; |
||||||
|
|
||||||
|
while (!noteFound && searchedVoices < NUM_VOICES && activeNotes > 0 && arpEnabled) |
||||||
|
{ |
||||||
|
notePlayed = (notePlayed < 0) ? 0 : notePlayed; |
||||||
|
|
||||||
|
if (midiNotes[notePlayed][MIDI_NOTE] > 0 |
||||||
|
&& midiNotes[notePlayed][MIDI_NOTE] < 128) |
||||||
|
{ |
||||||
|
//create MIDI note on message
|
||||||
|
uint8_t midiNote = midiNotes[notePlayed][MIDI_NOTE]; |
||||||
|
uint8_t channel = midiNotes[notePlayed][MIDI_CHANNEL]; |
||||||
|
|
||||||
|
if (arpEnabled) { |
||||||
|
|
||||||
|
uint8_t octave = octavePattern[octaveMode]->getStep() * 12; |
||||||
|
octavePattern[octaveMode]->goToNextStep(); |
||||||
|
|
||||||
|
midiNote = midiNote + octave; |
||||||
|
|
||||||
|
midiEvent.frame = s; |
||||||
|
midiEvent.size = 3; |
||||||
|
midiEvent.data[0] = MIDI_NOTEON | channel; |
||||||
|
midiEvent.data[1] = midiNote; |
||||||
|
midiEvent.data[2] = velocity; |
||||||
|
|
||||||
|
midiHandler.appendMidiMessage(midiEvent); |
||||||
|
|
||||||
|
noteOffBuffer[activeNotesIndex][MIDI_NOTE] = (uint32_t)midiNote; |
||||||
|
noteOffBuffer[activeNotesIndex][MIDI_CHANNEL] = (uint32_t)channel; |
||||||
|
activeNotesIndex = (activeNotesIndex + 1) % NUM_NOTE_OFF_SLOTS; |
||||||
|
noteFound = true; |
||||||
|
firstNote = false; |
||||||
|
} |
||||||
|
} |
||||||
|
arpPattern[arpMode]->goToNextStep(); |
||||||
|
notePlayed = arpPattern[arpMode]->getStep(); |
||||||
|
searchedVoices++; |
||||||
|
} |
||||||
|
clock.closeGate(); |
||||||
|
} |
||||||
|
|
||||||
|
for (size_t i = 0; i < NUM_NOTE_OFF_SLOTS; i++) { |
||||||
|
if (noteOffBuffer[i][MIDI_NOTE] != EMPTY_SLOT) { |
||||||
|
noteOffBuffer[i][TIMER] += 1; |
||||||
|
if (noteOffBuffer[i][TIMER] > static_cast<uint32_t>(clock.getPeriod() * noteLength)) { |
||||||
|
midiEvent.frame = s; |
||||||
|
midiEvent.size = 3; |
||||||
|
midiEvent.data[0] = MIDI_NOTEOFF | noteOffBuffer[i][MIDI_CHANNEL]; |
||||||
|
midiEvent.data[1] = static_cast<uint8_t>(noteOffBuffer[i][MIDI_NOTE]); |
||||||
|
midiEvent.data[2] = 0; |
||||||
|
|
||||||
|
midiHandler.appendMidiMessage(midiEvent); |
||||||
|
|
||||||
|
noteOffBuffer[i][MIDI_NOTE] = EMPTY_SLOT; |
||||||
|
noteOffBuffer[i][MIDI_CHANNEL] = 0; |
||||||
|
noteOffBuffer[i][TIMER] = 0; |
||||||
|
|
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
midiHandler.mergeBuffers(); |
||||||
|
} |
@ -0,0 +1,122 @@ |
|||||||
|
#ifndef _H_ARPEGGIATOR_ |
||||||
|
#define _H_ARPEGGIATOR_ |
||||||
|
|
||||||
|
#include <cstdint> |
||||||
|
|
||||||
|
#include "common/commons.h" |
||||||
|
#include "common/clock.hpp" |
||||||
|
#include "common/pattern.hpp" |
||||||
|
#include "common/midiHandler.hpp" |
||||||
|
#include "utils.hpp" |
||||||
|
|
||||||
|
#define NUM_VOICES 32 |
||||||
|
#define NUM_NOTE_OFF_SLOTS 32 |
||||||
|
#define PLUGIN_URI "http://moddevices.com/plugins/mod-devel/arpeggiator"
|
||||||
|
|
||||||
|
#define MIDI_NOTEOFF 0x80 |
||||||
|
#define MIDI_NOTEON 0x90 |
||||||
|
|
||||||
|
#define MIDI_NOTE 0 |
||||||
|
#define MIDI_CHANNEL 1 |
||||||
|
#define TIMER 2 |
||||||
|
|
||||||
|
#define NUM_ARP_MODES 6 |
||||||
|
#define NUM_OCTAVE_MODES 5 |
||||||
|
|
||||||
|
#define NUM_MIDI_CHANNELS 16 |
||||||
|
|
||||||
|
#define ONE_OCT_UP_PER_CYCLE 4 |
||||||
|
|
||||||
|
class Arpeggiator { |
||||||
|
public: |
||||||
|
enum ArpModes { |
||||||
|
ARP_UP = 0, |
||||||
|
ARP_DOWN, |
||||||
|
ARP_UP_DOWN, |
||||||
|
ARP_UP_DOWN_ALT, |
||||||
|
ARP_PLAYED, |
||||||
|
ARP_RANDOM |
||||||
|
}; |
||||||
|
Arpeggiator(); |
||||||
|
~Arpeggiator(); |
||||||
|
void setArpEnabled(bool arpEnabled); |
||||||
|
void setLatchMode(bool latchMode); |
||||||
|
void setSampleRate(float sampleRate); |
||||||
|
void setSyncMode(int mode); |
||||||
|
void setBpm(double bpm); |
||||||
|
void setDivision(int division); |
||||||
|
void setVelocity(uint8_t velocity); |
||||||
|
void setNoteLength(float noteLength); |
||||||
|
void setOctaveSpread(int octaveSpread); |
||||||
|
void setArpMode(int arpMode); |
||||||
|
void setOctaveMode(int octaveMode); |
||||||
|
void setPanic(bool panic); |
||||||
|
bool getArpEnabled() const; |
||||||
|
bool getLatchMode() const; |
||||||
|
float getSampleRate() const; |
||||||
|
int getSyncMode() const; |
||||||
|
float getBpm() const; |
||||||
|
int getDivision() const; |
||||||
|
uint8_t getVelocity() const; |
||||||
|
float getNoteLength() const; |
||||||
|
int getOctaveSpread() const; |
||||||
|
int getArpMode() const; |
||||||
|
int getOctaveMode() const; |
||||||
|
bool getPanic() const; |
||||||
|
void transmitHostInfo(const bool playing, const float beatsPerBar, |
||||||
|
const int beat, const float barBeat, const double bpm); |
||||||
|
void reset(); |
||||||
|
void emptyMidiBuffer(); |
||||||
|
void allNotesOff(); |
||||||
|
struct MidiBuffer getMidiBuffer(); |
||||||
|
void process(const MidiEvent* event, uint32_t eventCount, uint32_t n_frames); |
||||||
|
private: |
||||||
|
uint8_t midiNotes[NUM_VOICES][2]; |
||||||
|
uint8_t midiNotesBypassed[NUM_VOICES]; |
||||||
|
uint32_t noteOffBuffer[NUM_NOTE_OFF_SLOTS][3]; |
||||||
|
|
||||||
|
int notesPressed; |
||||||
|
int activeNotes; |
||||||
|
int notePlayed; |
||||||
|
|
||||||
|
int octaveMode; |
||||||
|
int octaveSpread; |
||||||
|
int arpMode; |
||||||
|
|
||||||
|
float noteLength; |
||||||
|
|
||||||
|
uint8_t pitch; |
||||||
|
uint8_t previousMidiNote; |
||||||
|
uint8_t velocity; |
||||||
|
int previousSyncMode; |
||||||
|
int activeNotesIndex; |
||||||
|
int activeNotesBypassed; |
||||||
|
int timeOutTime; |
||||||
|
int firstNoteTimer; |
||||||
|
float barBeat; |
||||||
|
|
||||||
|
bool pluginEnabled; |
||||||
|
bool first; |
||||||
|
bool arpEnabled; |
||||||
|
bool latchMode; |
||||||
|
bool previousLatch; |
||||||
|
bool latchPlaying; |
||||||
|
bool trigger; |
||||||
|
bool firstNote; |
||||||
|
bool quantizedStart; |
||||||
|
bool resetPattern; |
||||||
|
bool midiNotesCopied; |
||||||
|
bool panic; |
||||||
|
|
||||||
|
int division; |
||||||
|
float sampleRate; |
||||||
|
double bpm; |
||||||
|
|
||||||
|
ArpUtils utils; |
||||||
|
Pattern **arpPattern; |
||||||
|
Pattern **octavePattern; |
||||||
|
MidiHandler midiHandler; |
||||||
|
PluginClock clock; |
||||||
|
}; |
||||||
|
|
||||||
|
#endif //_H_ARPEGGIATOR_
|
@ -0,0 +1,241 @@ |
|||||||
|
#include "clock.hpp" |
||||||
|
|
||||||
|
PluginClock::PluginClock() : |
||||||
|
gate(false), |
||||||
|
trigger(false), |
||||||
|
beatSync(true), |
||||||
|
phaseReset(false), |
||||||
|
playing(false), |
||||||
|
previousPlaying(false), |
||||||
|
endOfBar(false), |
||||||
|
init(false), |
||||||
|
period(0), |
||||||
|
halfWavelength(0), |
||||||
|
quarterWaveLength(0), |
||||||
|
pos(0), |
||||||
|
beatsPerBar(1.0), |
||||||
|
bpm(120.0), |
||||||
|
internalBpm(120.0), |
||||||
|
previousBpm(0), |
||||||
|
sampleRate(48000.0), |
||||||
|
division(1), |
||||||
|
hostBarBeat(0.0), |
||||||
|
beatTick(0.0), |
||||||
|
syncMode(1), |
||||||
|
previousSyncMode(0), |
||||||
|
hostTick(0), |
||||||
|
hostBeat(0), |
||||||
|
barLength(4), |
||||||
|
numBarsElapsed(0), |
||||||
|
previousBeat(0), |
||||||
|
arpMode(0) |
||||||
|
{ |
||||||
|
} |
||||||
|
|
||||||
|
PluginClock::~PluginClock() |
||||||
|
{ |
||||||
|
} |
||||||
|
|
||||||
|
void PluginClock::transmitHostInfo(const bool playing, const float beatsPerBar, |
||||||
|
const int hostBeat, const float hostBarBeat, const float hostBpm) |
||||||
|
{ |
||||||
|
this->beatsPerBar = beatsPerBar; |
||||||
|
this->hostBeat = hostBeat; |
||||||
|
this->hostBarBeat = hostBarBeat; |
||||||
|
this->hostBpm = hostBpm; |
||||||
|
this->playing = playing; |
||||||
|
|
||||||
|
if (playing && !previousPlaying && beatSync) { |
||||||
|
syncClock(); |
||||||
|
} |
||||||
|
if (playing != previousPlaying) { |
||||||
|
previousPlaying = playing; |
||||||
|
} |
||||||
|
|
||||||
|
if (!init) { |
||||||
|
calcPeriod(); |
||||||
|
init = true; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
void PluginClock::setSyncMode(int mode) |
||||||
|
{ |
||||||
|
switch (mode) |
||||||
|
{ |
||||||
|
case FREE_RUNNING: |
||||||
|
beatSync = false; |
||||||
|
break; |
||||||
|
case HOST_BPM_SYNC: |
||||||
|
beatSync = false; |
||||||
|
break; |
||||||
|
case HOST_QUANTIZED_SYNC: |
||||||
|
beatSync = true; |
||||||
|
break; |
||||||
|
} |
||||||
|
|
||||||
|
this->syncMode = mode; |
||||||
|
} |
||||||
|
|
||||||
|
void PluginClock::setInternalBpmValue(float internalBpm) |
||||||
|
{ |
||||||
|
this->internalBpm = internalBpm; |
||||||
|
} |
||||||
|
|
||||||
|
void PluginClock::setBpm(float bpm) |
||||||
|
{ |
||||||
|
this->bpm = bpm; |
||||||
|
calcPeriod(); |
||||||
|
} |
||||||
|
|
||||||
|
void PluginClock::setSampleRate(float sampleRate) |
||||||
|
{ |
||||||
|
this->sampleRate = sampleRate; |
||||||
|
calcPeriod(); |
||||||
|
} |
||||||
|
|
||||||
|
void PluginClock::setDivision(int setDivision) |
||||||
|
{ |
||||||
|
this->division = setDivision; |
||||||
|
this->divisionValue = divisionValues[setDivision]; |
||||||
|
|
||||||
|
calcPeriod(); |
||||||
|
} |
||||||
|
|
||||||
|
void PluginClock::syncClock() |
||||||
|
{ |
||||||
|
pos = static_cast<uint32_t>(fmod(sampleRate * (60.0f / bpm) * (hostBarBeat + (numBarsElapsed * beatsPerBar)), sampleRate * (60.0f / (bpm * (divisionValue / 2.0f))))); |
||||||
|
} |
||||||
|
|
||||||
|
void PluginClock::setPos(uint32_t pos) |
||||||
|
{ |
||||||
|
this->pos = pos; |
||||||
|
} |
||||||
|
|
||||||
|
void PluginClock::setNumBarsElapsed(uint32_t numBarsElapsed) |
||||||
|
{ |
||||||
|
this->numBarsElapsed = numBarsElapsed; |
||||||
|
} |
||||||
|
|
||||||
|
void PluginClock::calcPeriod() |
||||||
|
{ |
||||||
|
period = static_cast<uint32_t>(sampleRate * (60.0f / (bpm * (divisionValue / 2.0f)))); |
||||||
|
halfWavelength = static_cast<uint32_t>(period / 2.0f); |
||||||
|
quarterWaveLength = static_cast<uint32_t>(halfWavelength / 2.0f); |
||||||
|
period = (period <= 0) ? 1 : period; |
||||||
|
} |
||||||
|
|
||||||
|
void PluginClock::closeGate() |
||||||
|
{ |
||||||
|
gate = false; |
||||||
|
} |
||||||
|
|
||||||
|
void PluginClock::reset() |
||||||
|
{ |
||||||
|
trigger = false; |
||||||
|
} |
||||||
|
|
||||||
|
float PluginClock::getSampleRate() const |
||||||
|
{ |
||||||
|
return sampleRate; |
||||||
|
} |
||||||
|
|
||||||
|
bool PluginClock::getGate() const |
||||||
|
{ |
||||||
|
return gate; |
||||||
|
} |
||||||
|
|
||||||
|
int PluginClock::getSyncMode() const |
||||||
|
{ |
||||||
|
return syncMode; |
||||||
|
} |
||||||
|
|
||||||
|
float PluginClock::getInternalBpmValue() const |
||||||
|
{ |
||||||
|
return internalBpm; |
||||||
|
} |
||||||
|
|
||||||
|
int PluginClock::getDivision() const |
||||||
|
{ |
||||||
|
return division; |
||||||
|
} |
||||||
|
|
||||||
|
uint32_t PluginClock::getPeriod() const |
||||||
|
{ |
||||||
|
return period; |
||||||
|
} |
||||||
|
|
||||||
|
uint32_t PluginClock::getPos() const |
||||||
|
{ |
||||||
|
return pos; |
||||||
|
} |
||||||
|
|
||||||
|
void PluginClock::tick() |
||||||
|
{ |
||||||
|
int beat = static_cast<int>(hostBarBeat); |
||||||
|
|
||||||
|
if (beatsPerBar <= 1) { |
||||||
|
if (hostBarBeat > 0.99 && !endOfBar) { |
||||||
|
endOfBar = true; |
||||||
|
} |
||||||
|
else if (hostBarBeat < 0.1 && endOfBar) { |
||||||
|
numBarsElapsed++; |
||||||
|
endOfBar = false; |
||||||
|
} |
||||||
|
} else { |
||||||
|
if (beat != previousBeat) { |
||||||
|
numBarsElapsed = (beat == 0) ? numBarsElapsed + 1 : numBarsElapsed; |
||||||
|
previousBeat = beat; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
float threshold = 0.009; //TODO might not be needed
|
||||||
|
|
||||||
|
switch (syncMode) |
||||||
|
{ |
||||||
|
case FREE_RUNNING: |
||||||
|
if ((internalBpm != previousBpm) || (syncMode != previousSyncMode)) { |
||||||
|
setBpm(internalBpm); |
||||||
|
previousBpm = internalBpm; |
||||||
|
previousSyncMode = syncMode; |
||||||
|
} |
||||||
|
break; |
||||||
|
case HOST_BPM_SYNC: |
||||||
|
if ((hostBpm != previousBpm && (fabs(previousBpm - hostBpm) > threshold)) || (syncMode != previousSyncMode)) { |
||||||
|
setBpm(hostBpm); |
||||||
|
previousBpm = hostBpm; |
||||||
|
previousSyncMode = syncMode; |
||||||
|
} |
||||||
|
break; |
||||||
|
case HOST_QUANTIZED_SYNC: //TODO fix this duplicate
|
||||||
|
if ((hostBpm != previousBpm && (fabs(previousBpm - hostBpm) > threshold)) || (syncMode != previousSyncMode)) { |
||||||
|
setBpm(hostBpm); |
||||||
|
if (playing) { |
||||||
|
syncClock(); |
||||||
|
} |
||||||
|
previousBpm = hostBpm; |
||||||
|
previousSyncMode = syncMode; |
||||||
|
} |
||||||
|
break; |
||||||
|
} |
||||||
|
|
||||||
|
if (pos > period) { |
||||||
|
pos = 0; |
||||||
|
} |
||||||
|
|
||||||
|
if (pos < quarterWaveLength && !trigger) { |
||||||
|
gate = true; |
||||||
|
trigger = true; |
||||||
|
} else if (pos > halfWavelength && trigger) { |
||||||
|
if (playing && beatSync) { |
||||||
|
syncClock(); |
||||||
|
} |
||||||
|
trigger = false; |
||||||
|
} |
||||||
|
|
||||||
|
if (playing && beatSync) { |
||||||
|
syncClock(); //hard-sync to host position
|
||||||
|
} |
||||||
|
else if (!beatSync) { |
||||||
|
pos++; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,80 @@ |
|||||||
|
#ifndef _H_CLOCK_ |
||||||
|
#define _H_CLOCK_ |
||||||
|
|
||||||
|
#include <cstdint> |
||||||
|
#include <math.h> |
||||||
|
|
||||||
|
enum SyncMode { |
||||||
|
FREE_RUNNING = 0, |
||||||
|
HOST_BPM_SYNC, |
||||||
|
HOST_QUANTIZED_SYNC |
||||||
|
}; |
||||||
|
|
||||||
|
class PluginClock { |
||||||
|
public: |
||||||
|
PluginClock(); |
||||||
|
~PluginClock(); |
||||||
|
void transmitHostInfo(const bool playing, const float beatstPerBar, |
||||||
|
const int hostBeat, const float hostBarBeat, const float hostBpm); |
||||||
|
void setSampleRate(float sampleRate); |
||||||
|
void setSyncMode(int mode); |
||||||
|
void setInternalBpmValue(float internalBpm); |
||||||
|
void setDivision(int division); |
||||||
|
void syncClock(); |
||||||
|
void setPos(uint32_t pos); |
||||||
|
void setNumBarsElapsed(uint32_t numBarsElapsed); |
||||||
|
void calcPeriod(); |
||||||
|
void closeGate(); |
||||||
|
void reset(); |
||||||
|
bool getGate() const; |
||||||
|
float getSampleRate() const; |
||||||
|
int getSyncMode() const; |
||||||
|
float getInternalBpmValue() const; |
||||||
|
int getDivision() const; |
||||||
|
uint32_t getPeriod() const; |
||||||
|
uint32_t getPos() const; |
||||||
|
void tick(); |
||||||
|
|
||||||
|
private: |
||||||
|
void setBpm(float bpm); |
||||||
|
|
||||||
|
bool gate; |
||||||
|
bool trigger; |
||||||
|
bool beatSync; |
||||||
|
bool phaseReset; |
||||||
|
bool playing; |
||||||
|
bool previousPlaying; |
||||||
|
bool endOfBar; |
||||||
|
bool init; |
||||||
|
|
||||||
|
uint32_t period; |
||||||
|
uint32_t halfWavelength; |
||||||
|
uint32_t quarterWaveLength; |
||||||
|
uint32_t pos; |
||||||
|
|
||||||
|
float beatsPerBar; |
||||||
|
float bpm; |
||||||
|
float internalBpm; |
||||||
|
float hostBpm; |
||||||
|
float previousBpm; |
||||||
|
float sampleRate; |
||||||
|
int division; |
||||||
|
float divisionValue; |
||||||
|
|
||||||
|
float hostBarBeat; |
||||||
|
float beatTick; |
||||||
|
int syncMode; |
||||||
|
int previousSyncMode; |
||||||
|
int hostTick; |
||||||
|
int hostBeat; |
||||||
|
int barLength; |
||||||
|
int numBarsElapsed; |
||||||
|
int previousBeat; |
||||||
|
|
||||||
|
int arpMode; |
||||||
|
|
||||||
|
// "1/1" "1/2" "1/3" "1/4" "1/4." "1/4T" "1/8" "1/8." "1/8T" "1/16" "1/16." "1/16T" "1/32"
|
||||||
|
float divisionValues[13] {0.5, 1, 1.5, 2.0, 2.66666, 3.0, 4.0, 5.33333, 6.0, 8.0, 10.66666, 12.0, 16.0}; |
||||||
|
}; |
||||||
|
|
||||||
|
#endif |
@ -0,0 +1,33 @@ |
|||||||
|
#ifndef _H_COMMONS_ |
||||||
|
#define _H_COMMONS_ |
||||||
|
|
||||||
|
#include <stdint.h> |
||||||
|
|
||||||
|
/**
|
||||||
|
MIDI event. |
||||||
|
*/ |
||||||
|
struct MidiEvent { |
||||||
|
/**
|
||||||
|
Size of internal data. |
||||||
|
*/ |
||||||
|
static const uint32_t kDataSize = 4; |
||||||
|
|
||||||
|
/**
|
||||||
|
Time offset in frames. |
||||||
|
*/ |
||||||
|
uint32_t frame; |
||||||
|
|
||||||
|
/**
|
||||||
|
Number of bytes used. |
||||||
|
*/ |
||||||
|
uint32_t size; |
||||||
|
|
||||||
|
/**
|
||||||
|
MIDI data.@n |
||||||
|
If size > kDataSize, dataExt is used (otherwise null). |
||||||
|
*/ |
||||||
|
uint8_t data[kDataSize]; |
||||||
|
const uint8_t* dataExt; |
||||||
|
}; |
||||||
|
|
||||||
|
#endif |
@ -0,0 +1,48 @@ |
|||||||
|
#include "midiHandler.hpp" |
||||||
|
#include <stdio.h> |
||||||
|
|
||||||
|
MidiHandler::MidiHandler() |
||||||
|
{ |
||||||
|
buffer.bufferedEvents = new MidiEvent[MIDI_BUFFER_SIZE]; |
||||||
|
buffer.bufferedMidiThroughEvents = new MidiEvent[MIDI_BUFFER_SIZE]; |
||||||
|
buffer.midiOutputBuffer = new MidiEvent[MIDI_BUFFER_SIZE]; |
||||||
|
emptyMidiBuffer(); |
||||||
|
} |
||||||
|
|
||||||
|
MidiHandler::~MidiHandler() |
||||||
|
{ |
||||||
|
delete buffer.bufferedEvents; |
||||||
|
delete buffer.bufferedMidiThroughEvents; |
||||||
|
delete buffer.midiOutputBuffer; |
||||||
|
} |
||||||
|
|
||||||
|
void MidiHandler::emptyMidiBuffer() |
||||||
|
{ |
||||||
|
buffer.numBufferedEvents = 0; |
||||||
|
buffer.numBufferedThroughEvents = 0; |
||||||
|
} |
||||||
|
|
||||||
|
void MidiHandler::appendMidiMessage(MidiEvent event) |
||||||
|
{ |
||||||
|
buffer.bufferedEvents[buffer.numBufferedEvents] = event; |
||||||
|
buffer.numBufferedEvents = (buffer.numBufferedEvents + 1) % buffer.maxBufferSize; |
||||||
|
} |
||||||
|
|
||||||
|
void MidiHandler::appendMidiThroughMessage(MidiEvent event) |
||||||
|
{ |
||||||
|
buffer.bufferedMidiThroughEvents[buffer.numBufferedThroughEvents] = event; |
||||||
|
buffer.numBufferedThroughEvents = (buffer.numBufferedThroughEvents + 1) % buffer.maxBufferSize; |
||||||
|
} |
||||||
|
|
||||||
|
void MidiHandler::mergeBuffers() |
||||||
|
{ |
||||||
|
for (unsigned e = 0; e < buffer.numBufferedThroughEvents; e++) { |
||||||
|
buffer.bufferedEvents[e + buffer.numBufferedEvents] = buffer.bufferedMidiThroughEvents[e]; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
struct MidiBuffer MidiHandler::getMidiBuffer() |
||||||
|
{ |
||||||
|
mergeBuffers(); |
||||||
|
return buffer; |
||||||
|
} |
@ -0,0 +1,60 @@ |
|||||||
|
#ifndef _H_MIDI_HANDLER_ |
||||||
|
#define _H_MIDI_HANDLER_ |
||||||
|
|
||||||
|
#include "commons.h" |
||||||
|
|
||||||
|
#include <arm_math.h> |
||||||
|
#include <cstdint> |
||||||
|
|
||||||
|
#define MIDI_BUFFER_SIZE 2048 |
||||||
|
#define EMPTY_SLOT 200 |
||||||
|
|
||||||
|
#define MIDI_NOTEOFF 0x80 |
||||||
|
#define MIDI_NOTEON 0x90 |
||||||
|
#define MIDI_SYSTEM_EXCLUSIVE 0xF0 |
||||||
|
#define MIDI_MTC_QUARTER_FRAME 0xF1 |
||||||
|
#define MIDI_SONG_POSITION_POINTER 0xF2 |
||||||
|
#define MIDI_SONG_SELECT 0xF3 |
||||||
|
#define MIDI_UNDEFINED_F4 0xF4 |
||||||
|
#define MIDI_UNDEFINED_F5 0xF5 |
||||||
|
#define MIDI_TUNE_REQUEST 0xF6 |
||||||
|
#define MIDI_END_OF_EXCLUSIVE 0xF7 |
||||||
|
#define MIDI_TIMING_CLOCK 0xF8 |
||||||
|
#define MIDI_UNDEFINED_F9 0xF9 |
||||||
|
#define MIDI_START 0xFA |
||||||
|
#define MIDI_CONTINUE 0xFB |
||||||
|
#define MIDI_STOP 0xFC |
||||||
|
#define MIDI_UNDEFINED_FD 0xFD |
||||||
|
#define MIDI_ACTIVE_SENSING 0xFE |
||||||
|
#define MIDI_SYSTEM_RESET 0xFF |
||||||
|
|
||||||
|
struct MidiBuffer { |
||||||
|
unsigned maxBufferSize = MIDI_BUFFER_SIZE; |
||||||
|
|
||||||
|
MidiEvent* bufferedEvents; |
||||||
|
unsigned numBufferedEvents; |
||||||
|
|
||||||
|
MidiEvent* bufferedMidiThroughEvents; |
||||||
|
unsigned numBufferedThroughEvents; |
||||||
|
|
||||||
|
MidiEvent* midiOutputBuffer; |
||||||
|
unsigned numOutputEvents; |
||||||
|
}; |
||||||
|
|
||||||
|
class MidiHandler { |
||||||
|
public: |
||||||
|
MidiHandler(); |
||||||
|
~MidiHandler(); |
||||||
|
void emptyMidiBuffer(); |
||||||
|
void appendMidiMessage(MidiEvent event); |
||||||
|
void appendMidiThroughMessage(MidiEvent event); |
||||||
|
void resetBuffer(); |
||||||
|
int getNumEvents(); |
||||||
|
void mergeBuffers(); |
||||||
|
MidiEvent getMidiEvent(int index); |
||||||
|
struct MidiBuffer getMidiBuffer(); |
||||||
|
private: |
||||||
|
MidiBuffer buffer; |
||||||
|
}; |
||||||
|
|
||||||
|
#endif //_H_MIDI_HANDLER_
|
@ -0,0 +1,242 @@ |
|||||||
|
#include "pattern.hpp" |
||||||
|
|
||||||
|
Pattern::Pattern() : size(1), step(0), range(1) |
||||||
|
{ |
||||||
|
} |
||||||
|
|
||||||
|
Pattern::~Pattern() |
||||||
|
{ |
||||||
|
} |
||||||
|
|
||||||
|
void Pattern::setPatternSize(int size) |
||||||
|
{ |
||||||
|
this->size = size; |
||||||
|
} |
||||||
|
|
||||||
|
void Pattern::setStep(int step) |
||||||
|
{ |
||||||
|
this->step = step; |
||||||
|
} |
||||||
|
|
||||||
|
void Pattern::setCycleRange(int range) |
||||||
|
{ |
||||||
|
this->range = range; |
||||||
|
} |
||||||
|
|
||||||
|
int Pattern::getSize() |
||||||
|
{ |
||||||
|
return size; |
||||||
|
} |
||||||
|
|
||||||
|
int Pattern::getStep() |
||||||
|
{ |
||||||
|
return step; |
||||||
|
} |
||||||
|
|
||||||
|
int Pattern::getDirection() |
||||||
|
{ |
||||||
|
return direction; |
||||||
|
} |
||||||
|
|
||||||
|
PatternUp::PatternUp() |
||||||
|
{ |
||||||
|
reset(); |
||||||
|
} |
||||||
|
|
||||||
|
PatternUp::~PatternUp() |
||||||
|
{ |
||||||
|
} |
||||||
|
|
||||||
|
void PatternUp::setDirection(int direction) |
||||||
|
{ |
||||||
|
this->direction = abs(direction); |
||||||
|
} |
||||||
|
|
||||||
|
void PatternUp::reset() |
||||||
|
{ |
||||||
|
step = 0; |
||||||
|
direction = 1; |
||||||
|
} |
||||||
|
|
||||||
|
void PatternUp::goToNextStep() |
||||||
|
{ |
||||||
|
if (size > 0) { |
||||||
|
step = (step + 1) % size; |
||||||
|
} else { |
||||||
|
step = 0; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
PatternDown::PatternDown() |
||||||
|
{ |
||||||
|
reset(); |
||||||
|
} |
||||||
|
|
||||||
|
PatternDown::~PatternDown() |
||||||
|
{ |
||||||
|
} |
||||||
|
|
||||||
|
void PatternDown::setDirection(int direction) |
||||||
|
{ |
||||||
|
this->direction = abs(direction) * -1; |
||||||
|
} |
||||||
|
|
||||||
|
void PatternDown::reset() |
||||||
|
{ |
||||||
|
step = 0; |
||||||
|
direction = -1; |
||||||
|
} |
||||||
|
|
||||||
|
void PatternDown::goToNextStep() |
||||||
|
{ |
||||||
|
if (size > 0) { |
||||||
|
step = (step + direction < 0) ? size - 1 : step + direction; |
||||||
|
} else { |
||||||
|
step = 0; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
PatternUpDown::PatternUpDown() |
||||||
|
{ |
||||||
|
reset(); |
||||||
|
} |
||||||
|
|
||||||
|
PatternUpDown::~PatternUpDown() |
||||||
|
{ |
||||||
|
} |
||||||
|
|
||||||
|
void PatternUpDown::setDirection(int direction) |
||||||
|
{ |
||||||
|
this->direction = direction; |
||||||
|
} |
||||||
|
|
||||||
|
void PatternUpDown::reset() |
||||||
|
{ |
||||||
|
step = 0; |
||||||
|
direction = 1; |
||||||
|
} |
||||||
|
|
||||||
|
void PatternUpDown::goToNextStep() |
||||||
|
{ |
||||||
|
if (size > 1) { |
||||||
|
int nextStep = step + direction; |
||||||
|
direction = (nextStep >= size) ? -1 : direction; |
||||||
|
direction = (nextStep < 0) ? 1 : direction; |
||||||
|
step += direction; |
||||||
|
} else { |
||||||
|
step = 0; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
PatternUpDownAlt::PatternUpDownAlt() |
||||||
|
{ |
||||||
|
reset(); |
||||||
|
} |
||||||
|
|
||||||
|
PatternUpDownAlt::~PatternUpDownAlt() |
||||||
|
{ |
||||||
|
} |
||||||
|
|
||||||
|
void PatternUpDownAlt::setDirection(int direction) |
||||||
|
{ |
||||||
|
this->direction = direction; |
||||||
|
} |
||||||
|
|
||||||
|
void PatternUpDownAlt::reset() |
||||||
|
{ |
||||||
|
step = 0; |
||||||
|
direction = 1; |
||||||
|
checked = false; |
||||||
|
skip = false; |
||||||
|
} |
||||||
|
|
||||||
|
void PatternUpDownAlt::goToNextStep() |
||||||
|
{ |
||||||
|
if (size > 1) { |
||||||
|
int nextStep = step + direction; |
||||||
|
|
||||||
|
if (!checked) { |
||||||
|
if (nextStep >= size) { |
||||||
|
direction = -1; |
||||||
|
skip = true; |
||||||
|
checked = true; |
||||||
|
} |
||||||
|
if (nextStep < 0) { |
||||||
|
direction = 1; |
||||||
|
skip = true; |
||||||
|
checked = true; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
if (!skip) { |
||||||
|
step += direction; |
||||||
|
checked = false; |
||||||
|
} |
||||||
|
skip = false; |
||||||
|
} else { |
||||||
|
step = 0; |
||||||
|
//TODO init other values
|
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
PatternRandom::PatternRandom() |
||||||
|
{ |
||||||
|
reset(); |
||||||
|
} |
||||||
|
|
||||||
|
PatternRandom::~PatternRandom() |
||||||
|
{ |
||||||
|
} |
||||||
|
|
||||||
|
void PatternRandom::setDirection(int direction) |
||||||
|
{ |
||||||
|
this->direction = direction; |
||||||
|
} |
||||||
|
|
||||||
|
void PatternRandom::reset() |
||||||
|
{ |
||||||
|
goToNextStep(); |
||||||
|
} |
||||||
|
|
||||||
|
void PatternRandom::goToNextStep() |
||||||
|
{ |
||||||
|
step = rand() % size; |
||||||
|
} |
||||||
|
|
||||||
|
PatternCycle::PatternCycle() |
||||||
|
{ |
||||||
|
reset(); |
||||||
|
} |
||||||
|
|
||||||
|
PatternCycle::~PatternCycle() |
||||||
|
{ |
||||||
|
} |
||||||
|
|
||||||
|
void PatternCycle::setDirection(int direction) |
||||||
|
{ |
||||||
|
this->direction = abs(direction); |
||||||
|
} |
||||||
|
|
||||||
|
void PatternCycle::reset() |
||||||
|
{ |
||||||
|
step = 0; |
||||||
|
tempStep = 0; |
||||||
|
direction = 1; |
||||||
|
} |
||||||
|
|
||||||
|
void PatternCycle::goToNextStep() |
||||||
|
{ |
||||||
|
if (size >= 1) { |
||||||
|
int nextStep = tempStep + direction; |
||||||
|
|
||||||
|
if (range > 0 && size > 0) { |
||||||
|
if (nextStep >= size) { |
||||||
|
step = (step + 1) % range; |
||||||
|
} |
||||||
|
tempStep = (tempStep + direction) % size; |
||||||
|
} |
||||||
|
} else { |
||||||
|
step = 0; |
||||||
|
tempStep = 0; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,93 @@ |
|||||||
|
#ifndef _H_PATTERN_ |
||||||
|
#define _H_PATTERN_ |
||||||
|
|
||||||
|
#include <stdlib.h> |
||||||
|
#include <time.h> |
||||||
|
|
||||||
|
class Pattern { |
||||||
|
|
||||||
|
enum { |
||||||
|
ARP_UP = 0, |
||||||
|
ARP_DOWN |
||||||
|
}; |
||||||
|
|
||||||
|
public: |
||||||
|
Pattern(); |
||||||
|
virtual ~Pattern(); |
||||||
|
void setPatternSize(int size); |
||||||
|
void setStep(int step); |
||||||
|
void setCycleRange(int range); |
||||||
|
int getSize(); |
||||||
|
int getStepSize(); |
||||||
|
int getStep(); |
||||||
|
int getDirection(); |
||||||
|
virtual void setDirection(int direction) = 0; |
||||||
|
virtual void reset() = 0; |
||||||
|
virtual void goToNextStep() = 0; |
||||||
|
protected: |
||||||
|
int size; |
||||||
|
int step; |
||||||
|
int direction; |
||||||
|
int range; |
||||||
|
}; |
||||||
|
|
||||||
|
class PatternUp : public Pattern { |
||||||
|
public: |
||||||
|
PatternUp(); |
||||||
|
~PatternUp(); |
||||||
|
void setDirection(int direction) override; |
||||||
|
void reset() override; |
||||||
|
void goToNextStep() override; |
||||||
|
}; |
||||||
|
|
||||||
|
class PatternDown : public Pattern { |
||||||
|
public: |
||||||
|
PatternDown(); |
||||||
|
~PatternDown(); |
||||||
|
void setDirection(int direction) override; |
||||||
|
void reset() override; |
||||||
|
void goToNextStep() override; |
||||||
|
}; |
||||||
|
|
||||||
|
class PatternUpDown : public Pattern { |
||||||
|
public: |
||||||
|
PatternUpDown(); |
||||||
|
~PatternUpDown(); |
||||||
|
void setDirection(int direction) override; |
||||||
|
void reset() override; |
||||||
|
void goToNextStep() override; |
||||||
|
}; |
||||||
|
|
||||||
|
class PatternUpDownAlt : public Pattern { |
||||||
|
public: |
||||||
|
PatternUpDownAlt(); |
||||||
|
~PatternUpDownAlt(); |
||||||
|
void setDirection(int direction) override; |
||||||
|
void reset() override; |
||||||
|
void goToNextStep() override; |
||||||
|
private: |
||||||
|
bool checked; |
||||||
|
bool skip; |
||||||
|
}; |
||||||
|
|
||||||
|
class PatternRandom : public Pattern { |
||||||
|
public: |
||||||
|
PatternRandom(); |
||||||
|
~PatternRandom(); |
||||||
|
void setDirection(int direction) override; |
||||||
|
void reset() override; |
||||||
|
void goToNextStep() override; |
||||||
|
}; |
||||||
|
|
||||||
|
class PatternCycle : public Pattern { |
||||||
|
public: |
||||||
|
PatternCycle(); |
||||||
|
~PatternCycle(); |
||||||
|
void setDirection(int direction) override; |
||||||
|
void reset() override; |
||||||
|
void goToNextStep() override; |
||||||
|
private: |
||||||
|
int tempStep; |
||||||
|
}; |
||||||
|
|
||||||
|
#endif // _H_PATTERN_
|
@ -0,0 +1,339 @@ |
|||||||
|
#include "plugin.hpp" |
||||||
|
|
||||||
|
START_NAMESPACE_DISTRHO |
||||||
|
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
|
||||||
|
PluginArpeggiator::PluginArpeggiator() : |
||||||
|
Plugin(paramCount, 0, 0), // paramCount params, 0 program(s), 0 states
|
||||||
|
syncMode(1) |
||||||
|
{ |
||||||
|
arpeggiator.transmitHostInfo(0, 4, 1, 1, 120.0); |
||||||
|
arpeggiator.setSampleRate(static_cast<float>(getSampleRate())); |
||||||
|
arpeggiator.setDivision(7); |
||||||
|
} |
||||||
|
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
// Init
|
||||||
|
|
||||||
|
void PluginArpeggiator::initParameter(uint32_t index, Parameter& parameter) |
||||||
|
{ |
||||||
|
if (index >= paramCount) return; |
||||||
|
|
||||||
|
switch (index) { |
||||||
|
case paramSyncMode: |
||||||
|
parameter.hints = kParameterIsAutomable | kParameterIsInteger; |
||||||
|
parameter.name = "Sync"; |
||||||
|
parameter.symbol = "sync"; |
||||||
|
parameter.ranges.def = 0; |
||||||
|
parameter.ranges.min = 0; |
||||||
|
parameter.ranges.max = 2; |
||||||
|
parameter.enumValues.count = 3; |
||||||
|
parameter.enumValues.restrictedMode = true; |
||||||
|
{ |
||||||
|
ParameterEnumerationValue* const channels = new ParameterEnumerationValue[13]; |
||||||
|
parameter.enumValues.values = channels; |
||||||
|
channels[0].label = "Free Running"; |
||||||
|
channels[0].value = 0; |
||||||
|
channels[1].label = "Host Sync"; |
||||||
|
channels[1].value = 1; |
||||||
|
channels[2].label = "Host Sync (Quantized Start)"; |
||||||
|
channels[2].value = 2; |
||||||
|
} |
||||||
|
break; |
||||||
|
case paramBpm: |
||||||
|
parameter.hints = kParameterIsAutomable; |
||||||
|
parameter.name = "Bpm"; |
||||||
|
parameter.symbol = "Bpm"; |
||||||
|
parameter.ranges.def = 120.f; |
||||||
|
parameter.ranges.min = 20.f; |
||||||
|
parameter.ranges.max = 280.f; |
||||||
|
break; |
||||||
|
case paramDivision: |
||||||
|
parameter.hints = kParameterIsAutomable | kParameterIsInteger; |
||||||
|
parameter.name = "Divison"; |
||||||
|
parameter.symbol = "Divisons"; |
||||||
|
parameter.ranges.def = 9; |
||||||
|
parameter.ranges.min = 0; |
||||||
|
parameter.ranges.max = 12; |
||||||
|
parameter.enumValues.count = 13; |
||||||
|
parameter.enumValues.restrictedMode = true; |
||||||
|
{ |
||||||
|
ParameterEnumerationValue* const channels = new ParameterEnumerationValue[13]; |
||||||
|
parameter.enumValues.values = channels; |
||||||
|
channels[0].label = "1/1"; |
||||||
|
channels[0].value = 0; |
||||||
|
channels[1].label = "1/2"; |
||||||
|
channels[1].value = 1; |
||||||
|
channels[2].label = "1/3"; |
||||||
|
channels[2].value = 2; |
||||||
|
channels[3].label = "1/4"; |
||||||
|
channels[3].value = 3; |
||||||
|
channels[4].label = "1/4."; |
||||||
|
channels[4].value = 4; |
||||||
|
channels[5].label = "1/4T"; |
||||||
|
channels[5].value = 5; |
||||||
|
channels[6].label = "1/8"; |
||||||
|
channels[6].value = 6; |
||||||
|
channels[7].label = "1/8."; |
||||||
|
channels[7].value = 7; |
||||||
|
channels[8].label = "1/8T"; |
||||||
|
channels[8].value = 8; |
||||||
|
channels[9].label = "1/16"; |
||||||
|
channels[9].value = 9; |
||||||
|
channels[10].label = "1/16."; |
||||||
|
channels[10].value = 10; |
||||||
|
channels[11].label = "1/16T"; |
||||||
|
channels[11].value = 11; |
||||||
|
channels[12].label = "1/32"; |
||||||
|
channels[12].value = 12; |
||||||
|
} |
||||||
|
break; |
||||||
|
case paramVelocity: |
||||||
|
parameter.hints = kParameterIsAutomable | kParameterIsInteger; |
||||||
|
parameter.name = "Velocity"; |
||||||
|
parameter.symbol = "velocity"; |
||||||
|
parameter.ranges.def = 0; |
||||||
|
parameter.ranges.min = 0; |
||||||
|
parameter.ranges.max = 127; |
||||||
|
break; |
||||||
|
case paramNoteLength: |
||||||
|
parameter.hints = kParameterIsAutomable; |
||||||
|
parameter.name = "Note Length"; |
||||||
|
parameter.symbol = "noteLength"; |
||||||
|
parameter.unit = ""; |
||||||
|
parameter.ranges.def = 0.f; |
||||||
|
parameter.ranges.min = 0.f; |
||||||
|
parameter.ranges.max = 1.f; |
||||||
|
break; |
||||||
|
case paramOctaveSpread: |
||||||
|
parameter.hints = kParameterIsAutomable | kParameterIsInteger; |
||||||
|
parameter.name = "Octave Spread"; |
||||||
|
parameter.symbol = "octaveSpread"; |
||||||
|
parameter.ranges.def = 0; |
||||||
|
parameter.ranges.min = 0; |
||||||
|
parameter.ranges.max = 3; |
||||||
|
parameter.enumValues.count = 4; |
||||||
|
parameter.enumValues.restrictedMode = true; |
||||||
|
{ |
||||||
|
ParameterEnumerationValue* const channels = new ParameterEnumerationValue[4]; |
||||||
|
parameter.enumValues.values = channels; |
||||||
|
channels[0].label = "1 oct"; |
||||||
|
channels[0].value = 0; |
||||||
|
channels[1].label = "2 oct"; |
||||||
|
channels[1].value = 1; |
||||||
|
channels[2].label = "3 oct"; |
||||||
|
channels[2].value = 2; |
||||||
|
channels[3].label = "4 oct"; |
||||||
|
channels[3].value = 3; |
||||||
|
} |
||||||
|
break; |
||||||
|
case paramArpMode: |
||||||
|
parameter.hints = kParameterIsAutomable | kParameterIsInteger; |
||||||
|
parameter.name = "Arp Mode"; |
||||||
|
parameter.symbol = "arpMode"; |
||||||
|
parameter.ranges.def = 0; |
||||||
|
parameter.ranges.min = 0; |
||||||
|
parameter.ranges.max = 5; |
||||||
|
parameter.enumValues.count = 6; |
||||||
|
parameter.enumValues.restrictedMode = true; |
||||||
|
{ |
||||||
|
ParameterEnumerationValue* const channels = new ParameterEnumerationValue[6]; |
||||||
|
parameter.enumValues.values = channels; |
||||||
|
channels[0].label = "Up"; |
||||||
|
channels[0].value = 0; |
||||||
|
channels[1].label = "Down"; |
||||||
|
channels[1].value = 1; |
||||||
|
channels[2].label = "Up-Down"; |
||||||
|
channels[2].value = 2; |
||||||
|
channels[3].label = "Up-Down (alt)"; |
||||||
|
channels[3].value = 3; |
||||||
|
channels[4].label = "Played"; |
||||||
|
channels[4].value = 4; |
||||||
|
channels[5].label = "Random"; |
||||||
|
channels[5].value = 5; |
||||||
|
} |
||||||
|
break; |
||||||
|
case paramOctaveMode: |
||||||
|
parameter.hints = kParameterIsAutomable | kParameterIsInteger; |
||||||
|
parameter.name = "Octave Mode"; |
||||||
|
parameter.symbol = "octaveMode"; |
||||||
|
parameter.ranges.def = 0; |
||||||
|
parameter.ranges.min = 0; |
||||||
|
parameter.ranges.max = 4; |
||||||
|
parameter.enumValues.count = 5; |
||||||
|
parameter.enumValues.restrictedMode = true; |
||||||
|
{ |
||||||
|
ParameterEnumerationValue* const channels = new ParameterEnumerationValue[5]; |
||||||
|
parameter.enumValues.values = channels; |
||||||
|
channels[0].label = "Up"; |
||||||
|
channels[0].value = 0; |
||||||
|
channels[1].label = "Down"; |
||||||
|
channels[1].value = 1; |
||||||
|
channels[2].label = "Up-Down"; |
||||||
|
channels[2].value = 2; |
||||||
|
channels[3].label = "Down-up"; |
||||||
|
channels[3].value = 3; |
||||||
|
channels[4].label = "1 Up / Cycle"; |
||||||
|
channels[4].value = 4; |
||||||
|
} |
||||||
|
break; |
||||||
|
case paramLatch: |
||||||
|
parameter.hints = kParameterIsAutomable | kParameterIsBoolean; |
||||||
|
parameter.name = "Latch"; |
||||||
|
parameter.symbol = "latch"; |
||||||
|
parameter.unit = ""; |
||||||
|
parameter.ranges.def = 0.f; |
||||||
|
parameter.ranges.min = 0.f; |
||||||
|
parameter.ranges.max = 1.f; |
||||||
|
break; |
||||||
|
case paramPanic: |
||||||
|
parameter.hints = kParameterIsAutomable | kParameterIsTrigger; |
||||||
|
parameter.name = "Panic"; |
||||||
|
parameter.symbol = "Panic"; |
||||||
|
parameter.unit = ""; |
||||||
|
parameter.ranges.def = 0; |
||||||
|
parameter.ranges.min = 0; |
||||||
|
parameter.ranges.max = 1; |
||||||
|
break; |
||||||
|
case paramEnabled: |
||||||
|
parameter.hints = kParameterIsBoolean; |
||||||
|
parameter.name = "Enabled"; |
||||||
|
parameter.symbol = "enabled"; |
||||||
|
parameter.unit = ""; |
||||||
|
parameter.ranges.def = 1.f; |
||||||
|
parameter.ranges.min = 0.f; |
||||||
|
parameter.ranges.max = 1.f; |
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
// Internal data
|
||||||
|
|
||||||
|
/**
|
||||||
|
Optional callback to inform the plugin about a sample rate change. |
||||||
|
*/ |
||||||
|
void PluginArpeggiator::sampleRateChanged(double newSampleRate) |
||||||
|
{ |
||||||
|
(void) newSampleRate; |
||||||
|
|
||||||
|
arpeggiator.setSampleRate(static_cast<float>(newSampleRate)); |
||||||
|
} |
||||||
|
|
||||||
|
/**
|
||||||
|
Get the current value of a parameter. |
||||||
|
*/ |
||||||
|
float PluginArpeggiator::getParameterValue(uint32_t index) const |
||||||
|
{ |
||||||
|
switch (index) |
||||||
|
{ |
||||||
|
case paramSyncMode: |
||||||
|
return arpeggiator.getSyncMode(); |
||||||
|
case paramBpm: |
||||||
|
return arpeggiator.getBpm(); |
||||||
|
case paramDivision: |
||||||
|
return arpeggiator.getDivision(); |
||||||
|
case paramVelocity: |
||||||
|
return arpeggiator.getVelocity(); |
||||||
|
case paramNoteLength: |
||||||
|
return arpeggiator.getNoteLength(); |
||||||
|
case paramOctaveSpread: |
||||||
|
return arpeggiator.getOctaveSpread(); |
||||||
|
case paramArpMode: |
||||||
|
return arpeggiator.getArpMode(); |
||||||
|
case paramOctaveMode: |
||||||
|
return arpeggiator.getOctaveMode(); |
||||||
|
case paramLatch: |
||||||
|
return arpeggiator.getLatchMode(); |
||||||
|
case paramPanic: |
||||||
|
return arpeggiator.getPanic(); |
||||||
|
case paramEnabled: |
||||||
|
return arpeggiator.getArpEnabled(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/**
|
||||||
|
Change a parameter value. |
||||||
|
*/ |
||||||
|
void PluginArpeggiator::setParameterValue(uint32_t index, float value) |
||||||
|
{ |
||||||
|
switch (index) |
||||||
|
{ |
||||||
|
case paramSyncMode: |
||||||
|
syncMode = static_cast<int>(value); |
||||||
|
break; |
||||||
|
case paramBpm: |
||||||
|
arpeggiator.setBpm(value); |
||||||
|
break; |
||||||
|
case paramDivision: |
||||||
|
arpeggiator.setDivision(static_cast<int>(value)); |
||||||
|
break; |
||||||
|
case paramVelocity: |
||||||
|
arpeggiator.setVelocity(static_cast<int>(value)); |
||||||
|
break; |
||||||
|
case paramNoteLength: |
||||||
|
arpeggiator.setNoteLength(value); |
||||||
|
break; |
||||||
|
case paramOctaveSpread: |
||||||
|
arpeggiator.setOctaveSpread(static_cast<int>(value)); |
||||||
|
break; |
||||||
|
case paramArpMode: |
||||||
|
arpeggiator.setArpMode(static_cast<int>(value)); |
||||||
|
break; |
||||||
|
case paramOctaveMode: |
||||||
|
arpeggiator.setOctaveMode(static_cast<int>(value)); |
||||||
|
break; |
||||||
|
case paramLatch: |
||||||
|
arpeggiator.setLatchMode(static_cast<bool>(value)); |
||||||
|
break; |
||||||
|
case paramPanic: |
||||||
|
arpeggiator.setPanic(static_cast<bool>(value)); |
||||||
|
break; |
||||||
|
case paramEnabled: |
||||||
|
arpeggiator.setArpEnabled(static_cast<bool>(value)); |
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
// Process
|
||||||
|
|
||||||
|
void PluginArpeggiator::activate() |
||||||
|
{ |
||||||
|
// plugin is activated
|
||||||
|
} |
||||||
|
|
||||||
|
void PluginArpeggiator::run(const float**, float**, uint32_t n_frames, |
||||||
|
const MidiEvent* events, uint32_t eventCount) |
||||||
|
{ |
||||||
|
arpeggiator.emptyMidiBuffer(); |
||||||
|
|
||||||
|
// Check if host supports Bar-Beat-Tick position
|
||||||
|
const TimePosition& position = getTimePosition(); |
||||||
|
if (!position.bbt.valid) { |
||||||
|
// set-arpeggiator in free running mode
|
||||||
|
arpeggiator.setSyncMode(0); |
||||||
|
} else { |
||||||
|
arpeggiator.setSyncMode(syncMode); |
||||||
|
arpeggiator.transmitHostInfo(position.playing, position.bbt.beatsPerBar, position.bbt.beat, position.bbt.barBeat, static_cast<float>(position.bbt.beatsPerMinute)); |
||||||
|
} |
||||||
|
|
||||||
|
arpeggiator.process(events, eventCount, n_frames); |
||||||
|
|
||||||
|
struct MidiBuffer buffer = arpeggiator.getMidiBuffer(); |
||||||
|
for (unsigned x = 0; x < buffer.numBufferedEvents + buffer.numBufferedThroughEvents; x++) { |
||||||
|
writeMidiEvent(buffer.bufferedEvents[x]); //needs to be one struct or array?
|
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
|
||||||
|
Plugin* createPlugin() |
||||||
|
{ |
||||||
|
return new PluginArpeggiator(); |
||||||
|
} |
||||||
|
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
|
||||||
|
END_NAMESPACE_DISTRHO |
@ -0,0 +1,104 @@ |
|||||||
|
#ifndef _H_PLUGIN_ARPEGGIATOR_ |
||||||
|
#define _H_PLUGIN_ARPEGGIATOR_ |
||||||
|
|
||||||
|
#include "arpeggiator.hpp" |
||||||
|
#include "common/commons.h" |
||||||
|
#include "common/clock.hpp" |
||||||
|
#include "common/pattern.hpp" |
||||||
|
|
||||||
|
START_NAMESPACE_DISTRHO |
||||||
|
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
|
||||||
|
class PluginArpeggiator : public Plugin { |
||||||
|
public: |
||||||
|
enum Parameters { |
||||||
|
paramSyncMode = 0, |
||||||
|
paramBpm, |
||||||
|
paramDivision, |
||||||
|
paramVelocity, |
||||||
|
paramNoteLength, |
||||||
|
paramOctaveSpread, |
||||||
|
paramArpMode, |
||||||
|
paramOctaveMode, |
||||||
|
paramLatch, |
||||||
|
paramPanic, |
||||||
|
paramEnabled, |
||||||
|
paramCount |
||||||
|
}; |
||||||
|
|
||||||
|
PluginArpeggiator(); |
||||||
|
|
||||||
|
protected: |
||||||
|
// -------------------------------------------------------------------
|
||||||
|
// Information
|
||||||
|
|
||||||
|
const char* getLabel() const noexcept override { |
||||||
|
return "Arpeggiator"; |
||||||
|
} |
||||||
|
|
||||||
|
const char* getDescription() const override { |
||||||
|
return "A MIDI arpeggiator"; |
||||||
|
} |
||||||
|
|
||||||
|
const char* getMaker() const noexcept override { |
||||||
|
return "MOD"; |
||||||
|
} |
||||||
|
|
||||||
|
const char* getHomePage() const override { |
||||||
|
return ""; |
||||||
|
} |
||||||
|
|
||||||
|
const char* getLicense() const noexcept override { |
||||||
|
return "https://spdx.org/licenses/GPL-2.0-or-later"; |
||||||
|
} |
||||||
|
|
||||||
|
uint32_t getVersion() const noexcept override { |
||||||
|
return d_version(1, 1, 2); |
||||||
|
} |
||||||
|
|
||||||
|
int64_t getUniqueId() const noexcept override { |
||||||
|
return d_cconst('M', 'O', 'A', 'P'); |
||||||
|
} |
||||||
|
|
||||||
|
// -------------------------------------------------------------------
|
||||||
|
// Init
|
||||||
|
|
||||||
|
void initParameter(uint32_t index, Parameter& parameter) override; |
||||||
|
|
||||||
|
// -------------------------------------------------------------------
|
||||||
|
// Internal data
|
||||||
|
|
||||||
|
float getParameterValue(uint32_t index) const override; |
||||||
|
void setParameterValue(uint32_t index, float value) override; |
||||||
|
|
||||||
|
// -------------------------------------------------------------------
|
||||||
|
// Optional
|
||||||
|
|
||||||
|
// Optional callback to inform the plugin about a sample rate change.
|
||||||
|
void sampleRateChanged(double newSampleRate) override; |
||||||
|
|
||||||
|
// -------------------------------------------------------------------
|
||||||
|
// Process
|
||||||
|
|
||||||
|
void activate() override; |
||||||
|
|
||||||
|
void run(const float**, float**, uint32_t, |
||||||
|
const MidiEvent* midiEvents, uint32_t midiEventCount) override; |
||||||
|
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------
|
||||||
|
|
||||||
|
private: |
||||||
|
Arpeggiator arpeggiator; |
||||||
|
float fParams[paramCount]; |
||||||
|
int syncMode; |
||||||
|
|
||||||
|
DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(PluginArpeggiator) |
||||||
|
}; |
||||||
|
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
|
||||||
|
END_NAMESPACE_DISTRHO |
||||||
|
|
||||||
|
#endif //_H_PLUGIN_ARPEGGIATOR_
|
@ -0,0 +1,43 @@ |
|||||||
|
#include "utils.hpp" |
||||||
|
|
||||||
|
ArpUtils::ArpUtils() |
||||||
|
{ |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
ArpUtils::~ArpUtils() |
||||||
|
{ |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
void ArpUtils::swap(uint8_t *a, uint8_t *b) |
||||||
|
{ |
||||||
|
int temp = *a; |
||||||
|
*a = *b; |
||||||
|
*b = temp; |
||||||
|
} |
||||||
|
|
||||||
|
//got the code for the quick sort algorithm here https://medium.com/human-in-a-machine-world/quicksort-the-best-sorting-algorithm-6ab461b5a9d0
|
||||||
|
void ArpUtils::quicksort(uint8_t arr[][2], int l, int r) |
||||||
|
{ |
||||||
|
if (l >= r) |
||||||
|
{ |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
int pivot = arr[r][0]; |
||||||
|
|
||||||
|
int cnt = l; |
||||||
|
|
||||||
|
for (int i = l; i <= r; i++) |
||||||
|
{ |
||||||
|
if (arr[i][0] <= pivot) |
||||||
|
{ |
||||||
|
swap(&arr[cnt][0], &arr[i][0]); |
||||||
|
swap(&arr[cnt][1], &arr[i][1]); |
||||||
|
cnt++; |
||||||
|
} |
||||||
|
} |
||||||
|
quicksort(arr, l, cnt-2); |
||||||
|
quicksort(arr, cnt, r); |
||||||
|
} |
@ -0,0 +1,15 @@ |
|||||||
|
#ifndef _H_UTILS_ |
||||||
|
#define _H_UTILS_ |
||||||
|
|
||||||
|
#include <cstdint> |
||||||
|
|
||||||
|
class ArpUtils { |
||||||
|
public: |
||||||
|
ArpUtils(); |
||||||
|
~ArpUtils(); |
||||||
|
void quicksort(uint8_t arr[][2], int l, int r); |
||||||
|
private: |
||||||
|
void swap(uint8_t *a, uint8_t *b); |
||||||
|
}; |
||||||
|
|
||||||
|
#endif //_H_UTILS_
|
@ -0,0 +1,842 @@ |
|||||||
|
// Copyright (c) 2010 Martin Eastwood
|
||||||
|
// This code is distributed under the terms of the GNU General Public License
|
||||||
|
|
||||||
|
// MVerb is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// at your option) any later version.
|
||||||
|
//
|
||||||
|
// MVerb is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with this MVerb. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
#ifndef _EMVERB_H |
||||||
|
#define _EMVERB_H |
||||||
|
|
||||||
|
#include <cmath> |
||||||
|
#include <cstring> |
||||||
|
|
||||||
|
//forward declaration
|
||||||
|
template<typename T, int maxLength> class Allpass; |
||||||
|
template<typename T, int maxLength> class StaticAllpassFourTap; |
||||||
|
template<typename T, int maxLength> class StaticDelayLine; |
||||||
|
template<typename T, int maxLength> class StaticDelayLineFourTap; |
||||||
|
template<typename T, int maxLength> class StaticDelayLineEightTap; |
||||||
|
template<typename T, int OverSampleCount> class StateVariable; |
||||||
|
|
||||||
|
template<typename T> |
||||||
|
class MVerb |
||||||
|
{ |
||||||
|
private: |
||||||
|
Allpass<T, 96000> allpass[4]; |
||||||
|
StaticAllpassFourTap<T, 96000> allpassFourTap[4]; |
||||||
|
StateVariable<T,4> bandwidthFilter[2]; |
||||||
|
StateVariable<T,4> damping[2]; |
||||||
|
StaticDelayLine<T, 96000> predelay; |
||||||
|
StaticDelayLineFourTap<T, 96000> staticDelayLine[4]; |
||||||
|
StaticDelayLineEightTap<T, 96000> earlyReflectionsDelayLine[2]; |
||||||
|
T SampleRate, DampingFreq, Density1, Density2, BandwidthFreq, PreDelayTime, Decay, Gain, Mix, EarlyMix, Size; |
||||||
|
T MixSmooth, EarlyLateSmooth, BandwidthSmooth, DampingSmooth, PredelaySmooth, SizeSmooth, DensitySmooth, DecaySmooth; |
||||||
|
T PreviousLeftTank, PreviousRightTank; |
||||||
|
int ControlRate, ControlRateCounter; |
||||||
|
|
||||||
|
public: |
||||||
|
enum |
||||||
|
{ |
||||||
|
DAMPINGFREQ=0, |
||||||
|
DENSITY, |
||||||
|
BANDWIDTHFREQ, |
||||||
|
DECAY, |
||||||
|
PREDELAY, |
||||||
|
SIZE, |
||||||
|
GAIN, |
||||||
|
MIX, |
||||||
|
EARLYMIX, |
||||||
|
NUM_PARAMS |
||||||
|
}; |
||||||
|
|
||||||
|
MVerb(){ |
||||||
|
DampingFreq = 0.9; |
||||||
|
BandwidthFreq = 0.9; |
||||||
|
SampleRate = 44100.; |
||||||
|
Decay = 0.5; |
||||||
|
Gain = 1.; |
||||||
|
Mix = 1.; |
||||||
|
Size = 1.; |
||||||
|
EarlyMix = 1.; |
||||||
|
PreviousLeftTank = 0.; |
||||||
|
PreviousRightTank = 0.; |
||||||
|
PreDelayTime = 100 * (SampleRate / 1000); |
||||||
|
MixSmooth = EarlyLateSmooth = BandwidthSmooth = DampingSmooth = PredelaySmooth = SizeSmooth = DecaySmooth = DensitySmooth = 0.; |
||||||
|
ControlRate = SampleRate / 1000; |
||||||
|
ControlRateCounter = 0; |
||||||
|
reset(); |
||||||
|
} |
||||||
|
|
||||||
|
~MVerb(){ |
||||||
|
//nowt to do here
|
||||||
|
} |
||||||
|
|
||||||
|
void process(const T **inputs, T **outputs, int sampleFrames){ |
||||||
|
T OneOverSampleFrames = 1. / sampleFrames; |
||||||
|
T MixDelta = (Mix - MixSmooth) * OneOverSampleFrames; |
||||||
|
T EarlyLateDelta = (EarlyMix - EarlyLateSmooth) * OneOverSampleFrames; |
||||||
|
T BandwidthDelta = (((BandwidthFreq * 18400.) + 100.) - BandwidthSmooth) * OneOverSampleFrames; |
||||||
|
T DampingDelta = (((DampingFreq * 18400.) + 100.) - DampingSmooth) * OneOverSampleFrames; |
||||||
|
T PredelayDelta = ((PreDelayTime * 200 * (SampleRate / 1000)) - PredelaySmooth) * OneOverSampleFrames; |
||||||
|
T SizeDelta = (Size - SizeSmooth) * OneOverSampleFrames; |
||||||
|
T DecayDelta = (((0.7995f * Decay) + 0.005) - DecaySmooth) * OneOverSampleFrames; |
||||||
|
T DensityDelta = (((0.7995f * Density1) + 0.005) - DensitySmooth) * OneOverSampleFrames; |
||||||
|
for(int i=0;i<sampleFrames;++i){ |
||||||
|
T left = inputs[0][i]; |
||||||
|
T right = inputs[1][i]; |
||||||
|
MixSmooth += MixDelta; |
||||||
|
EarlyLateSmooth += EarlyLateDelta; |
||||||
|
BandwidthSmooth += BandwidthDelta; |
||||||
|
DampingSmooth += DampingDelta; |
||||||
|
PredelaySmooth += PredelayDelta; |
||||||
|
SizeSmooth += SizeDelta; |
||||||
|
DecaySmooth += DecayDelta; |
||||||
|
DensitySmooth += DensityDelta; |
||||||
|
if (ControlRateCounter >= ControlRate){ |
||||||
|
ControlRateCounter = 0; |
||||||
|
bandwidthFilter[0].Frequency(BandwidthSmooth); |
||||||
|
bandwidthFilter[1].Frequency(BandwidthSmooth); |
||||||
|
damping[0].Frequency(DampingSmooth); |
||||||
|
damping[1].Frequency(DampingSmooth); |
||||||
|
} |
||||||
|
++ControlRateCounter; |
||||||
|
predelay.SetLength(PredelaySmooth); |
||||||
|
Density2 = DecaySmooth + 0.15; |
||||||
|
if (Density2 > 0.5) |
||||||
|
Density2 = 0.5; |
||||||
|
if (Density2 < 0.25) |
||||||
|
Density2 = 0.25; |
||||||
|
allpassFourTap[1].SetFeedback(Density2); |
||||||
|
allpassFourTap[3].SetFeedback(Density2); |
||||||
|
allpassFourTap[0].SetFeedback(Density1); |
||||||
|
allpassFourTap[2].SetFeedback(Density1); |
||||||
|
T bandwidthLeft = bandwidthFilter[0](left) ; |
||||||
|
T bandwidthRight = bandwidthFilter[1](right) ; |
||||||
|
T earlyReflectionsL = earlyReflectionsDelayLine[0] ( bandwidthLeft * 0.5 + bandwidthRight * 0.3 ) |
||||||
|
+ earlyReflectionsDelayLine[0].GetIndex(2) * 0.6 |
||||||
|
+ earlyReflectionsDelayLine[0].GetIndex(3) * 0.4 |
||||||
|
+ earlyReflectionsDelayLine[0].GetIndex(4) * 0.3 |
||||||
|
+ earlyReflectionsDelayLine[0].GetIndex(5) * 0.3 |
||||||
|
+ earlyReflectionsDelayLine[0].GetIndex(6) * 0.1 |
||||||
|
+ earlyReflectionsDelayLine[0].GetIndex(7) * 0.1 |
||||||
|
+ ( bandwidthLeft * 0.4 + bandwidthRight * 0.2 ) * 0.5 ; |
||||||
|
T earlyReflectionsR = earlyReflectionsDelayLine[1] ( bandwidthLeft * 0.3 + bandwidthRight * 0.5 ) |
||||||
|
+ earlyReflectionsDelayLine[1].GetIndex(2) * 0.6 |
||||||
|
+ earlyReflectionsDelayLine[1].GetIndex(3) * 0.4 |
||||||
|
+ earlyReflectionsDelayLine[1].GetIndex(4) * 0.3 |
||||||
|
+ earlyReflectionsDelayLine[1].GetIndex(5) * 0.3 |
||||||
|
+ earlyReflectionsDelayLine[1].GetIndex(6) * 0.1 |
||||||
|
+ earlyReflectionsDelayLine[1].GetIndex(7) * 0.1 |
||||||
|
+ ( bandwidthLeft * 0.2 + bandwidthRight * 0.4 ) * 0.5 ; |
||||||
|
T predelayMonoInput = predelay(( bandwidthRight + bandwidthLeft ) * 0.5f); |
||||||
|
T smearedInput = predelayMonoInput; |
||||||
|
for(int j=0;j<4;j++) |
||||||
|
smearedInput = allpass[j] ( smearedInput ); |
||||||
|
T leftTank = allpassFourTap[0] ( smearedInput + PreviousRightTank ) ; |
||||||
|
leftTank = staticDelayLine[0] (leftTank); |
||||||
|
leftTank = damping[0](leftTank); |
||||||
|
leftTank = allpassFourTap[1](leftTank); |
||||||
|
leftTank = staticDelayLine[1](leftTank); |
||||||
|
T rightTank = allpassFourTap[2] (smearedInput + PreviousLeftTank) ; |
||||||
|
rightTank = staticDelayLine[2](rightTank); |
||||||
|
rightTank = damping[1] (rightTank); |
||||||
|
rightTank = allpassFourTap[3](rightTank); |
||||||
|
rightTank = staticDelayLine[3](rightTank); |
||||||
|
PreviousLeftTank = leftTank * DecaySmooth; |
||||||
|
PreviousRightTank = rightTank * DecaySmooth; |
||||||
|
T accumulatorL = (0.6*staticDelayLine[2].GetIndex(1)) |
||||||
|
+(0.6*staticDelayLine[2].GetIndex(2)) |
||||||
|
-(0.6*allpassFourTap[3].GetIndex(1)) |
||||||
|
+(0.6*staticDelayLine[3].GetIndex(1)) |
||||||
|
-(0.6*staticDelayLine[0].GetIndex(1)) |
||||||
|
-(0.6*allpassFourTap[1].GetIndex(1)) |
||||||
|
-(0.6*staticDelayLine[1].GetIndex(1)); |
||||||
|
T accumulatorR = (0.6*staticDelayLine[0].GetIndex(2)) |
||||||
|
+(0.6*staticDelayLine[0].GetIndex(3)) |
||||||
|
-(0.6*allpassFourTap[1].GetIndex(2)) |
||||||
|
+(0.6*staticDelayLine[1].GetIndex(2)) |
||||||
|
-(0.6*staticDelayLine[2].GetIndex(3)) |
||||||
|
-(0.6*allpassFourTap[3].GetIndex(2)) |
||||||
|
-(0.6*staticDelayLine[3].GetIndex(2)); |
||||||
|
accumulatorL = ((accumulatorL * EarlyMix) + ((1 - EarlyMix) * earlyReflectionsL)); |
||||||
|
accumulatorR = ((accumulatorR * EarlyMix) + ((1 - EarlyMix) * earlyReflectionsR)); |
||||||
|
left = ( left + MixSmooth * ( accumulatorL - left ) ) * Gain; |
||||||
|
right = ( right + MixSmooth * ( accumulatorR - right ) ) * Gain; |
||||||
|
outputs[0][i] = left; |
||||||
|
outputs[1][i] = right; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
void reset(){ |
||||||
|
ControlRateCounter = 0; |
||||||
|
bandwidthFilter[0].SetSampleRate (SampleRate ); |
||||||
|
bandwidthFilter[1].SetSampleRate (SampleRate ); |
||||||
|
bandwidthFilter[0].Reset(); |
||||||
|
bandwidthFilter[1].Reset(); |
||||||
|
damping[0].SetSampleRate (SampleRate ); |
||||||
|
damping[1].SetSampleRate (SampleRate ); |
||||||
|
damping[0].Reset(); |
||||||
|
damping[1].Reset(); |
||||||
|
predelay.Clear(); |
||||||
|
predelay.SetLength(PreDelayTime); |
||||||
|
allpass[0].Clear(); |
||||||
|
allpass[1].Clear(); |
||||||
|
allpass[2].Clear(); |
||||||
|
allpass[3].Clear(); |
||||||
|
allpass[0].SetLength (0.0048 * SampleRate); |
||||||
|
allpass[1].SetLength (0.0036 * SampleRate); |
||||||
|
allpass[2].SetLength (0.0127 * SampleRate); |
||||||
|
allpass[3].SetLength (0.0093 * SampleRate); |
||||||
|
allpass[0].SetFeedback (0.75); |
||||||
|
allpass[1].SetFeedback (0.75); |
||||||
|
allpass[2].SetFeedback (0.625); |
||||||
|
allpass[3].SetFeedback (0.625); |
||||||
|
allpassFourTap[0].Clear(); |
||||||
|
allpassFourTap[1].Clear(); |
||||||
|
allpassFourTap[2].Clear(); |
||||||
|
allpassFourTap[3].Clear(); |
||||||
|
allpassFourTap[0].SetLength(0.020 * SampleRate * Size); |
||||||
|
allpassFourTap[1].SetLength(0.060 * SampleRate * Size); |
||||||
|
allpassFourTap[2].SetLength(0.030 * SampleRate * Size); |
||||||
|
allpassFourTap[3].SetLength(0.089 * SampleRate * Size); |
||||||
|
allpassFourTap[0].SetFeedback(Density1); |
||||||
|
allpassFourTap[1].SetFeedback(Density2); |
||||||
|
allpassFourTap[2].SetFeedback(Density1); |
||||||
|
allpassFourTap[3].SetFeedback(Density2); |
||||||
|
allpassFourTap[0].SetIndex(0,0,0,0); |
||||||
|
allpassFourTap[1].SetIndex(0,0.006 * SampleRate * Size, 0.041 * SampleRate * Size, 0); |
||||||
|
allpassFourTap[2].SetIndex(0,0,0,0); |
||||||
|
allpassFourTap[3].SetIndex(0,0.031 * SampleRate * Size, 0.011 * SampleRate * Size, 0); |
||||||
|
staticDelayLine[0].Clear(); |
||||||
|
staticDelayLine[1].Clear(); |
||||||
|
staticDelayLine[2].Clear(); |
||||||
|
staticDelayLine[3].Clear(); |
||||||
|
staticDelayLine[0].SetLength(0.15 * SampleRate * Size); |
||||||
|
staticDelayLine[1].SetLength(0.12 * SampleRate * Size); |
||||||
|
staticDelayLine[2].SetLength(0.14 * SampleRate * Size); |
||||||
|
staticDelayLine[3].SetLength(0.11 * SampleRate * Size); |
||||||
|
staticDelayLine[0].SetIndex(0, 0.067 * SampleRate * Size, 0.011 * SampleRate * Size , 0.121 * SampleRate * Size); |
||||||
|
staticDelayLine[1].SetIndex(0, 0.036 * SampleRate * Size, 0.089 * SampleRate * Size , 0); |
||||||
|
staticDelayLine[2].SetIndex(0, 0.0089 * SampleRate * Size, 0.099 * SampleRate * Size , 0); |
||||||
|
staticDelayLine[3].SetIndex(0, 0.067 * SampleRate * Size, 0.0041 * SampleRate * Size , 0); |
||||||
|
earlyReflectionsDelayLine[0].Clear(); |
||||||
|
earlyReflectionsDelayLine[1].Clear(); |
||||||
|
earlyReflectionsDelayLine[0].SetLength(0.089 * SampleRate); |
||||||
|
earlyReflectionsDelayLine[0].SetIndex (0, 0.0199*SampleRate, 0.0219*SampleRate, 0.0354*SampleRate,0.0389*SampleRate, 0.0414*SampleRate, 0.0692*SampleRate, 0); |
||||||
|
earlyReflectionsDelayLine[1].SetLength(0.069 * SampleRate); |
||||||
|
earlyReflectionsDelayLine[1].SetIndex (0, 0.0099*SampleRate, 0.011*SampleRate, 0.0182*SampleRate,0.0189*SampleRate, 0.0213*SampleRate, 0.0431*SampleRate, 0); |
||||||
|
} |
||||||
|
|
||||||
|
void setParameter(int index, T value){ |
||||||
|
switch(index){ |
||||||
|
case DAMPINGFREQ: |
||||||
|
DampingFreq = /* 1. - */ value; // FIXME?
|
||||||
|
break; |
||||||
|
case DENSITY: |
||||||
|
Density1 = value; |
||||||
|
break; |
||||||
|
case BANDWIDTHFREQ: |
||||||
|
BandwidthFreq = value; |
||||||
|
break; |
||||||
|
case PREDELAY: |
||||||
|
PreDelayTime = value; |
||||||
|
break; |
||||||
|
case SIZE: |
||||||
|
Size = value; |
||||||
|
allpassFourTap[0].Clear(); |
||||||
|
allpassFourTap[1].Clear(); |
||||||
|
allpassFourTap[2].Clear(); |
||||||
|
allpassFourTap[3].Clear(); |
||||||
|
allpassFourTap[0].SetLength(0.020 * SampleRate * Size); |
||||||
|
allpassFourTap[1].SetLength(0.060 * SampleRate * Size); |
||||||
|
allpassFourTap[2].SetLength(0.030 * SampleRate * Size); |
||||||
|
allpassFourTap[3].SetLength(0.089 * SampleRate * Size); |
||||||
|
allpassFourTap[1].SetIndex(0,0.006 * SampleRate * Size, 0.041 * SampleRate * Size, 0); |
||||||
|
allpassFourTap[3].SetIndex(0,0.031 * SampleRate * Size, 0.011 * SampleRate * Size, 0); |
||||||
|
staticDelayLine[0].Clear(); |
||||||
|
staticDelayLine[1].Clear(); |
||||||
|
staticDelayLine[2].Clear(); |
||||||
|
staticDelayLine[3].Clear(); |
||||||
|
staticDelayLine[0].SetLength(0.15 * SampleRate * Size); |
||||||
|
staticDelayLine[1].SetLength(0.12 * SampleRate * Size); |
||||||
|
staticDelayLine[2].SetLength(0.14 * SampleRate * Size); |
||||||
|
staticDelayLine[3].SetLength(0.11 * SampleRate * Size); |
||||||
|
staticDelayLine[0].SetIndex(0, 0.067 * SampleRate * Size, 0.011 * SampleRate * Size , 0.121 * SampleRate * Size); |
||||||
|
staticDelayLine[1].SetIndex(0, 0.036 * SampleRate * Size, 0.089 * SampleRate * Size , 0); |
||||||
|
staticDelayLine[2].SetIndex(0, 0.0089 * SampleRate * Size, 0.099 * SampleRate * Size , 0); |
||||||
|
staticDelayLine[3].SetIndex(0, 0.067 * SampleRate * Size, 0.0041 * SampleRate * Size , 0); |
||||||
|
break; |
||||||
|
case DECAY: |
||||||
|
Decay = value; |
||||||
|
break; |
||||||
|
case GAIN: |
||||||
|
Gain = value; |
||||||
|
break; |
||||||
|
case MIX: |
||||||
|
Mix = value; |
||||||
|
break; |
||||||
|
case EARLYMIX: |
||||||
|
EarlyMix = value; |
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
float getParameter(int index) const{ |
||||||
|
switch(index){ |
||||||
|
case DAMPINGFREQ: |
||||||
|
return DampingFreq; |
||||||
|
break; |
||||||
|
case DENSITY: |
||||||
|
return Density1; |
||||||
|
break; |
||||||
|
case BANDWIDTHFREQ: |
||||||
|
return BandwidthFreq; |
||||||
|
break; |
||||||
|
case PREDELAY: |
||||||
|
return PreDelayTime; |
||||||
|
break; |
||||||
|
case SIZE: |
||||||
|
return Size; |
||||||
|
break; |
||||||
|
case DECAY: |
||||||
|
return Decay; |
||||||
|
break; |
||||||
|
case GAIN: |
||||||
|
return Gain; |
||||||
|
break; |
||||||
|
case MIX: |
||||||
|
return Mix; |
||||||
|
break; |
||||||
|
case EARLYMIX: |
||||||
|
return EarlyMix; |
||||||
|
break; |
||||||
|
default: return 0.f; |
||||||
|
break; |
||||||
|
|
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
void setSampleRate(T sr){ |
||||||
|
SampleRate = sr; |
||||||
|
ControlRate = SampleRate / 1000; |
||||||
|
reset(); |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
template<typename T, int maxLength> |
||||||
|
class Allpass |
||||||
|
{ |
||||||
|
private: |
||||||
|
T buffer[maxLength]; |
||||||
|
int index; |
||||||
|
int Length; |
||||||
|
T Feedback; |
||||||
|
|
||||||
|
public: |
||||||
|
Allpass() |
||||||
|
{ |
||||||
|
SetLength ( maxLength - 1 ); |
||||||
|
Clear(); |
||||||
|
Feedback = 0.5; |
||||||
|
} |
||||||
|
|
||||||
|
T operator()(T input) |
||||||
|
{ |
||||||
|
T output; |
||||||
|
T bufout; |
||||||
|
bufout = buffer[index]; |
||||||
|
T temp = input * -Feedback; |
||||||
|
output = bufout + temp; |
||||||
|
buffer[index] = input + ((bufout+temp)*Feedback); |
||||||
|
if(++index>=Length) index = 0; |
||||||
|
return output; |
||||||
|
} |
||||||
|
|
||||||
|
void SetLength (int Length) |
||||||
|
{ |
||||||
|
if( Length >= maxLength ) |
||||||
|
Length = maxLength; |
||||||
|
if( Length < 0 ) |
||||||
|
Length = 0; |
||||||
|
|
||||||
|
this->Length = Length; |
||||||
|
} |
||||||
|
|
||||||
|
void SetFeedback(T feedback) |
||||||
|
{ |
||||||
|
Feedback = feedback; |
||||||
|
} |
||||||
|
|
||||||
|
void Clear() |
||||||
|
{ |
||||||
|
std::memset(buffer, 0, sizeof(buffer)); |
||||||
|
index = 0; |
||||||
|
} |
||||||
|
|
||||||
|
int GetLength() const |
||||||
|
{ |
||||||
|
return Length; |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
template<typename T, int maxLength> |
||||||
|
class StaticAllpassFourTap |
||||||
|
{ |
||||||
|
private: |
||||||
|
T buffer[maxLength]; |
||||||
|
int index1, index2, index3, index4; |
||||||
|
int Length; |
||||||
|
T Feedback; |
||||||
|
|
||||||
|
public: |
||||||
|
StaticAllpassFourTap() |
||||||
|
{ |
||||||
|
SetLength ( maxLength - 1 ); |
||||||
|
Clear(); |
||||||
|
Feedback = 0.5; |
||||||
|
} |
||||||
|
|
||||||
|
T operator()(T input) |
||||||
|
{ |
||||||
|
T output; |
||||||
|
T bufout; |
||||||
|
|
||||||
|
bufout = buffer[index1]; |
||||||
|
T temp = input * -Feedback; |
||||||
|
output = bufout + temp; |
||||||
|
buffer[index1] = input + ((bufout+temp)*Feedback); |
||||||
|
|
||||||
|
if(++index1>=Length) |
||||||
|
index1 = 0; |
||||||
|
if(++index2 >= Length) |
||||||
|
index2 = 0; |
||||||
|
if(++index3 >= Length) |
||||||
|
index3 = 0; |
||||||
|
if(++index4 >= Length) |
||||||
|
index4 = 0; |
||||||
|
|
||||||
|
return output; |
||||||
|
} |
||||||
|
|
||||||
|
void SetIndex (int Index1, int Index2, int Index3, int Index4) |
||||||
|
{ |
||||||
|
index1 = Index1; |
||||||
|
index2 = Index2; |
||||||
|
index3 = Index3; |
||||||
|
index4 = Index4; |
||||||
|
} |
||||||
|
|
||||||
|
T GetIndex (int Index) |
||||||
|
{ |
||||||
|
switch (Index) |
||||||
|
{ |
||||||
|
case 0: |
||||||
|
return buffer[index1]; |
||||||
|
break; |
||||||
|
case 1: |
||||||
|
return buffer[index2]; |
||||||
|
break; |
||||||
|
case 2: |
||||||
|
return buffer[index3]; |
||||||
|
break; |
||||||
|
case 3: |
||||||
|
return buffer[index4]; |
||||||
|
break; |
||||||
|
default: |
||||||
|
return buffer[index1]; |
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
void SetLength (int Length) |
||||||
|
{ |
||||||
|
if( Length >= maxLength ) |
||||||
|
Length = maxLength; |
||||||
|
if( Length < 0 ) |
||||||
|
Length = 0; |
||||||
|
|
||||||
|
this->Length = Length; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
void Clear() |
||||||
|
{ |
||||||
|
std::memset(buffer, 0, sizeof(buffer)); |
||||||
|
index1 = index2 = index3 = index4 = 0; |
||||||
|
} |
||||||
|
|
||||||
|
void SetFeedback(T feedback) |
||||||
|
{ |
||||||
|
Feedback = feedback; |
||||||
|
} |
||||||
|
|
||||||
|
int GetLength() const |
||||||
|
{ |
||||||
|
return Length; |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
template<typename T, int maxLength> |
||||||
|
class StaticDelayLine |
||||||
|
{ |
||||||
|
private: |
||||||
|
T buffer[maxLength]; |
||||||
|
int index; |
||||||
|
int Length; |
||||||
|
T Feedback; |
||||||
|
|
||||||
|
public: |
||||||
|
StaticDelayLine() |
||||||
|
{ |
||||||
|
SetLength ( maxLength - 1 ); |
||||||
|
Clear(); |
||||||
|
} |
||||||
|
|
||||||
|
T operator()(T input) |
||||||
|
{ |
||||||
|
T output = buffer[index]; |
||||||
|
buffer[index++] = input; |
||||||
|
if(index >= Length) |
||||||
|
index = 0; |
||||||
|
return output; |
||||||
|
} |
||||||
|
|
||||||
|
void SetLength (int Length) |
||||||
|
{ |
||||||
|
if( Length >= maxLength ) |
||||||
|
Length = maxLength; |
||||||
|
if( Length < 0 ) |
||||||
|
Length = 0; |
||||||
|
|
||||||
|
this->Length = Length; |
||||||
|
} |
||||||
|
|
||||||
|
void Clear() |
||||||
|
{ |
||||||
|
std::memset(buffer, 0, sizeof(buffer)); |
||||||
|
index = 0; |
||||||
|
} |
||||||
|
|
||||||
|
int GetLength() const |
||||||
|
{ |
||||||
|
return Length; |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
template<typename T, int maxLength> |
||||||
|
class StaticDelayLineFourTap |
||||||
|
{ |
||||||
|
private: |
||||||
|
T buffer[maxLength]; |
||||||
|
int index1, index2, index3, index4; |
||||||
|
int Length; |
||||||
|
T Feedback; |
||||||
|
|
||||||
|
public: |
||||||
|
StaticDelayLineFourTap() |
||||||
|
{ |
||||||
|
SetLength ( maxLength - 1 ); |
||||||
|
Clear(); |
||||||
|
} |
||||||
|
|
||||||
|
//get ouput and iterate
|
||||||
|
T operator()(T input) |
||||||
|
{ |
||||||
|
T output = buffer[index1]; |
||||||
|
buffer[index1++] = input; |
||||||
|
if(index1 >= Length) |
||||||
|
index1 = 0; |
||||||
|
if(++index2 >= Length) |
||||||
|
index2 = 0; |
||||||
|
if(++index3 >= Length) |
||||||
|
index3 = 0; |
||||||
|
if(++index4 >= Length) |
||||||
|
index4 = 0; |
||||||
|
return output; |
||||||
|
} |
||||||
|
|
||||||
|
void SetIndex (int Index1, int Index2, int Index3, int Index4) |
||||||
|
{ |
||||||
|
index1 = Index1; |
||||||
|
index2 = Index2; |
||||||
|
index3 = Index3; |
||||||
|
index4 = Index4; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
T GetIndex (int Index) |
||||||
|
{ |
||||||
|
switch (Index) |
||||||
|
{ |
||||||
|
case 0: |
||||||
|
return buffer[index1]; |
||||||
|
break; |
||||||
|
case 1: |
||||||
|
return buffer[index2]; |
||||||
|
break; |
||||||
|
case 2: |
||||||
|
return buffer[index3]; |
||||||
|
break; |
||||||
|
case 3: |
||||||
|
return buffer[index4]; |
||||||
|
break; |
||||||
|
default: |
||||||
|
return buffer[index1]; |
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
void SetLength (int Length) |
||||||
|
{ |
||||||
|
if( Length >= maxLength ) |
||||||
|
Length = maxLength; |
||||||
|
if( Length < 0 ) |
||||||
|
Length = 0; |
||||||
|
|
||||||
|
this->Length = Length; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
void Clear() |
||||||
|
{ |
||||||
|
std::memset(buffer, 0, sizeof(buffer)); |
||||||
|
index1 = index2 = index3 = index4 = 0; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
int GetLength() const |
||||||
|
{ |
||||||
|
return Length; |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
template<typename T, int maxLength> |
||||||
|
class StaticDelayLineEightTap |
||||||
|
{ |
||||||
|
private: |
||||||
|
T buffer[maxLength]; |
||||||
|
int index1, index2, index3, index4, index5, index6, index7, index8; |
||||||
|
int Length; |
||||||
|
T Feedback; |
||||||
|
|
||||||
|
public: |
||||||
|
StaticDelayLineEightTap() |
||||||
|
{ |
||||||
|
SetLength ( maxLength - 1 ); |
||||||
|
Clear(); |
||||||
|
} |
||||||
|
|
||||||
|
//get ouput and iterate
|
||||||
|
T operator()(T input) |
||||||
|
{ |
||||||
|
T output = buffer[index1]; |
||||||
|
buffer[index1++] = input; |
||||||
|
if(index1 >= Length) |
||||||
|
index1 = 0; |
||||||
|
if(++index2 >= Length) |
||||||
|
index2 = 0; |
||||||
|
if(++index3 >= Length) |
||||||
|
index3 = 0; |
||||||
|
if(++index4 >= Length) |
||||||
|
index4 = 0; |
||||||
|
if(++index5 >= Length) |
||||||
|
index5 = 0; |
||||||
|
if(++index6 >= Length) |
||||||
|
index6 = 0; |
||||||
|
if(++index7 >= Length) |
||||||
|
index7 = 0; |
||||||
|
if(++index8 >= Length) |
||||||
|
index8 = 0; |
||||||
|
return output; |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
void SetIndex (int Index1, int Index2, int Index3, int Index4, int Index5, int Index6, int Index7, int Index8) |
||||||
|
{ |
||||||
|
index1 = Index1; |
||||||
|
index2 = Index2; |
||||||
|
index3 = Index3; |
||||||
|
index4 = Index4; |
||||||
|
index5 = Index5; |
||||||
|
index6 = Index6; |
||||||
|
index7 = Index7; |
||||||
|
index8 = Index8; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
T GetIndex (int Index) |
||||||
|
{ |
||||||
|
switch (Index) |
||||||
|
{ |
||||||
|
case 0: |
||||||
|
return buffer[index1]; |
||||||
|
break; |
||||||
|
case 1: |
||||||
|
return buffer[index2]; |
||||||
|
break; |
||||||
|
case 2: |
||||||
|
return buffer[index3]; |
||||||
|
break; |
||||||
|
case 3: |
||||||
|
return buffer[index4]; |
||||||
|
break; |
||||||
|
case 4: |
||||||
|
return buffer[index5]; |
||||||
|
break; |
||||||
|
case 5: |
||||||
|
return buffer[index6]; |
||||||
|
break; |
||||||
|
case 6: |
||||||
|
return buffer[index7]; |
||||||
|
break; |
||||||
|
case 7: |
||||||
|
return buffer[index8]; |
||||||
|
break; |
||||||
|
default: |
||||||
|
return buffer[index1]; |
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
void SetLength (int Length) |
||||||
|
{ |
||||||
|
if( Length >= maxLength ) |
||||||
|
Length = maxLength; |
||||||
|
if( Length < 0 ) |
||||||
|
Length = 0; |
||||||
|
|
||||||
|
this->Length = Length; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
void Clear() |
||||||
|
{ |
||||||
|
std::memset(buffer, 0, sizeof(buffer)); |
||||||
|
index1 = index2 = index3 = index4 = index5 = index6 = index7 = index8 = 0; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
int GetLength() const |
||||||
|
{ |
||||||
|
return Length; |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
template<typename T, int OverSampleCount> |
||||||
|
class StateVariable |
||||||
|
{ |
||||||
|
public: |
||||||
|
|
||||||
|
enum FilterType |
||||||
|
{ |
||||||
|
LOWPASS, |
||||||
|
HIGHPASS, |
||||||
|
BANDPASS, |
||||||
|
NOTCH, |
||||||
|
FilterTypeCount |
||||||
|
}; |
||||||
|
|
||||||
|
private: |
||||||
|
|
||||||
|
T sampleRate; |
||||||
|
T frequency; |
||||||
|
T q; |
||||||
|
T f; |
||||||
|
|
||||||
|
T low; |
||||||
|
T high; |
||||||
|
T band; |
||||||
|
T notch; |
||||||
|
|
||||||
|
T *out; |
||||||
|
|
||||||
|
public: |
||||||
|
StateVariable() |
||||||
|
{ |
||||||
|
SetSampleRate(44100.); |
||||||
|
Frequency(1000.); |
||||||
|
Resonance(0); |
||||||
|
Type(LOWPASS); |
||||||
|
Reset(); |
||||||
|
} |
||||||
|
|
||||||
|
T operator()(T input) |
||||||
|
{ |
||||||
|
for(unsigned int i = 0; i < OverSampleCount; i++) |
||||||
|
{ |
||||||
|
low += f * band + 1e-25; |
||||||
|
high = input - low - q * band; |
||||||
|
band += f * high; |
||||||
|
notch = low + high; |
||||||
|
} |
||||||
|
return *out; |
||||||
|
} |
||||||
|
|
||||||
|
void Reset() |
||||||
|
{ |
||||||
|
low = high = band = notch = 0; |
||||||
|
} |
||||||
|
|
||||||
|
void SetSampleRate(T sampleRate) |
||||||
|
{ |
||||||
|
this->sampleRate = sampleRate * OverSampleCount; |
||||||
|
UpdateCoefficient(); |
||||||
|
} |
||||||
|
|
||||||
|
void Frequency(T frequency) |
||||||
|
{ |
||||||
|
this->frequency = frequency; |
||||||
|
UpdateCoefficient(); |
||||||
|
} |
||||||
|
|
||||||
|
void Resonance(T resonance) |
||||||
|
{ |
||||||
|
this->q = 2 - 2 * resonance; |
||||||
|
} |
||||||
|
|
||||||
|
void Type(int type) |
||||||
|
{ |
||||||
|
switch(type) |
||||||
|
{ |
||||||
|
case LOWPASS: |
||||||
|
out = &low; |
||||||
|
break; |
||||||
|
|
||||||
|
case HIGHPASS: |
||||||
|
out = &high; |
||||||
|
break; |
||||||
|
|
||||||
|
case BANDPASS: |
||||||
|
out = &band; |
||||||
|
break; |
||||||
|
|
||||||
|
case NOTCH: |
||||||
|
out = ¬ch; |
||||||
|
break; |
||||||
|
|
||||||
|
default: |
||||||
|
out = &low; |
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private: |
||||||
|
void UpdateCoefficient() |
||||||
|
{ |
||||||
|
f = 2. * std::sin(PI * frequency / sampleRate); |
||||||
|
} |
||||||
|
}; |
||||||
|
#endif |
Loading…
Reference in new issue