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