Merge pull request #1 from jnonis/arp

Merge Arp branch to FX branch
pull/764/head
jnonis 6 months ago committed by GitHub
commit 37c152d2c8
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 9
      src/Makefile
  2. 176
      src/effect_3bandeq.h
  3. 2
      src/effect_base.h
  4. 116
      src/effect_mverb.cpp
  5. 54
      src/effect_mverb.h
  6. 1
      src/effect_talreverb3.cpp
  7. 1
      src/effect_talreverb3.h
  8. 8
      src/effects.h
  9. 169
      src/midi_arp.cpp
  10. 116
      src/midi_arp.h
  11. 112
      src/midi_effect_base.h
  12. 107
      src/midi_effects.h
  13. 105
      src/minidexed.cpp
  14. 12
      src/minidexed.h
  15. 648
      src/modarpeggiator/arpeggiator.cpp
  16. 122
      src/modarpeggiator/arpeggiator.hpp
  17. 241
      src/modarpeggiator/common/clock.cpp
  18. 80
      src/modarpeggiator/common/clock.hpp
  19. 33
      src/modarpeggiator/common/commons.h
  20. 48
      src/modarpeggiator/common/midiHandler.cpp
  21. 60
      src/modarpeggiator/common/midiHandler.hpp
  22. 242
      src/modarpeggiator/common/pattern.cpp
  23. 93
      src/modarpeggiator/common/pattern.hpp
  24. 339
      src/modarpeggiator/plugin.cpp
  25. 104
      src/modarpeggiator/plugin.hpp
  26. 43
      src/modarpeggiator/utils.cpp
  27. 15
      src/modarpeggiator/utils.hpp
  28. 842
      src/mverb/MVerb.h
  29. 128
      src/performanceconfig.cpp
  30. 12
      src/performanceconfig.h
  31. 452
      src/uimenu.cpp
  32. 16
      src/uimenu.h

@ -9,15 +9,18 @@ CMSIS_DIR = ../CMSIS_5/CMSIS
OBJS = main.o kernel.o minidexed.o config.o userinterface.o uimenu.o \
mididevice.o midikeyboard.o serialmididevice.o pckeyboard.o \
sysexfileloader.o performanceconfig.o perftimer.o \
effect_base.o effect_chorus.o effect_delay.o \
effect_base.o effect_chorus.o effect_delay.o effect_mverb.o \
effect_talreverb3.o effect_ds1.o effect_bigmuff.o \
moddistortion/Distortion_DS1.o moddistortion/Distortion_BigMuff.o \
moddistortion/HyperbolicTables.o moddistortion/OverSample.o \
effect_compressor.o effect_platervbstereo.o uibuttons.o midipin.o
effect_compressor.o effect_platervbstereo.o uibuttons.o midipin.o \
midi_arp.o modarpeggiator/common/clock.o \
modarpeggiator/common/midiHandler.o modarpeggiator/common/pattern.o \
modarpeggiator/utils.o modarpeggiator/arpeggiator.o
OPTIMIZE = -O3
include ./Synth_Dexed.mk
include ./Rules.mk
EXTRACLEAN += moddistortion/*.[od]
EXTRACLEAN += moddistortion/*.[od] modarpeggiator/*.[od] modarpeggiator/common/*.[od]

@ -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

@ -16,6 +16,8 @@
#define EFFECT_BIGMUFF 5
#define EFFECT_TALREVERB3 6
#define EFFECT_REVERB 7
#define EFFECT_MVERB 8
#define EFFECT_3BANDEQ 9
class AudioEffect
{

@ -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

@ -1,6 +1,7 @@
/*
* Tal Reverb 3 Port
* Ported from https://github.com/DISTRHO/DISTRHO-Ports/tree/master/ports-juce5/tal-reverb-3
* Original https://tal-software.com/
*
* Javier Nonis (https://github.com/jnonis) - 2024
*/

@ -1,6 +1,7 @@
/*
* Tal Reverb 3 Port
* Ported from https://github.com/DISTRHO/DISTRHO-Ports/tree/master/ports-juce5/tal-reverb-3
* Original https://tal-software.com/
*
* Javier Nonis (https://github.com/jnonis) - 2024
*/

@ -14,6 +14,8 @@
#include "effect_bigmuff.h"
#include "effect_talreverb3.h"
#include "effect_platervbstereo.h"
#include "effect_mverb.h"
#include "effect_3bandeq.h"
inline AudioEffect* newAudioEffect(unsigned type, float32_t samplerate)
{
@ -33,6 +35,10 @@ inline AudioEffect* newAudioEffect(unsigned type, float32_t samplerate)
return new AudioEffectTalReverb3(samplerate);
case EFFECT_REVERB:
return new AudioEffectPlateReverb(samplerate);
case EFFECT_MVERB:
return new AudioEffectMVerb(samplerate);
case EFFECT_3BANDEQ:
return new AudioEffect3BandEQ(samplerate);
case EFFECT_NONE:
default:
return new AudioEffectNone(samplerate);
@ -50,6 +56,8 @@ inline std::string getFXTypeName(int nValue)
case EFFECT_BIGMUFF: return "Big Muff";
case EFFECT_TALREVERB3: return "TalRvrb3";
case EFFECT_REVERB: return "Reverb";
case EFFECT_MVERB: return "MVerb";
case EFFECT_3BANDEQ: return "3Band EQ";
case EFFECT_NONE:
default: return "None";
}

@ -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

@ -108,6 +108,8 @@ CMiniDexed::CMiniDexed (CConfig *pConfig, CInterruptSystem *pInterrupt,
m_pTG[i] = new CDexedAdapter (CConfig::MaxNotes, pConfig->GetSampleRate ());
assert (m_pTG[i]);
m_MidiArpSpinLock[i] = new CSpinLock();
m_MidiArp[i] = new MidiEffect(pConfig->GetSampleRate(), m_pTG[i]);
m_pTG[i]->setEngineType(pConfig->GetEngineType ());
m_pTG[i]->activate ();
@ -416,7 +418,13 @@ void CMiniDexed::Run (unsigned nCore)
for (unsigned i = 0; i < CConfig::TGsCore23; i++, nTG++)
{
assert (m_pTG[nTG]);
m_MidiArpSpinLock[nTG]->Acquire();
m_MidiArp[nTG]->process(m_nFramesToProcess);
m_MidiArpSpinLock[nTG]->Release();
m_pTG[nTG]->getSamples (m_OutputLevel[nTG][0],m_nFramesToProcess);
m_InsertFXSpinLock[nTG]->Acquire();
m_InsertFX[nTG]->process(m_OutputLevel[nTG][0], m_OutputLevel[nTG][0], m_OutputLevel[nTG][0], m_OutputLevel[nTG][1], m_nFramesToProcess);
m_InsertFXSpinLock[nTG]->Release();
@ -618,6 +626,24 @@ void CMiniDexed::setInsertFXType (unsigned nType, unsigned nTG)
m_UI.ParameterChanged ();
}
void CMiniDexed::setMidiFXType (unsigned nType, unsigned nTG)
{
assert (nTG < CConfig::ToneGenerators);
// If the effect type is already set just return
if (m_MidiArp[nTG]->getId() == nType) {
return;
}
m_MidiArpSpinLock[nTG]->Acquire();
delete m_MidiArp[nTG];
m_MidiArp[nTG] = newMidiEffect(nType, m_pConfig->GetSampleRate(), m_pTG[nTG]);
m_MidiArp[nTG]->setTempo(m_nTempo);
m_MidiArpSpinLock[nTG]->Release();
m_UI.ParameterChanged ();
}
void CMiniDexed::setSendFXType (unsigned nType)
{
m_SendFXSpinLock.Acquire();
@ -744,6 +770,7 @@ void CMiniDexed::setTempo(unsigned nValue)
for (unsigned nTG = 0; nTG < CConfig::ToneGenerators; nTG++)
{
m_InsertFX[nTG]->setTempo(m_nTempo);
m_MidiArp[nTG]->setTempo(m_nTempo);
}
// Update UI
@ -751,6 +778,16 @@ void CMiniDexed::setTempo(unsigned nValue)
}
bool CMiniDexed::isPlaying(void)
{
return m_bPlaying;
}
void CMiniDexed::setPlaying(bool bValue)
{
m_bPlaying = bValue;
}
void CMiniDexed::handleClock (void)
{
if (m_nClockCounter == 0)
@ -783,7 +820,16 @@ void CMiniDexed::keyup (int16_t pitch, unsigned nTG)
pitch = ApplyNoteLimits (pitch, nTG);
if (pitch >= 0)
{
m_pTG[nTG]->keyup (pitch);
if (m_MidiArp[nTG]->getBypass())
{
m_pTG[nTG]->keyup(pitch);
}
else
{
m_MidiArpSpinLock[nTG]->Acquire();
m_MidiArp[nTG]->keyup(pitch);
m_MidiArpSpinLock[nTG]->Release();
}
}
}
@ -795,7 +841,16 @@ void CMiniDexed::keydown (int16_t pitch, uint8_t velocity, unsigned nTG)
pitch = ApplyNoteLimits (pitch, nTG);
if (pitch >= 0)
{
m_pTG[nTG]->keydown (pitch, velocity);
if (m_MidiArp[nTG]->getBypass())
{
m_pTG[nTG]->keydown (pitch, velocity);
}
else
{
m_MidiArpSpinLock[nTG]->Acquire();
m_MidiArp[nTG]->keydown(pitch, velocity);
m_MidiArpSpinLock[nTG]->Release();
}
}
}
@ -971,6 +1026,7 @@ void CMiniDexed::SetParameter (TParameter Parameter, int nValue)
break;
case ParameterTempo:
nValue=constrain((int)nValue,30,250);
this->setTempo(nValue);
break;
@ -1046,6 +1102,7 @@ void CMiniDexed::SetTGParameter (TTGParameter Parameter, int nValue, unsigned nT
case TGParameterReverbSend: SetReverbSend (nValue, nTG); break;
case TGParameterInsertFXType: setInsertFXType(nValue, nTG); break;
case TGParameterMidiFXType: setMidiFXType(nValue, nTG); break;
default:
assert (0);
@ -1071,6 +1128,7 @@ int CMiniDexed::GetTGParameter (TTGParameter Parameter, unsigned nTG)
case TGParameterMIDIChannel: return m_nMIDIChannel[nTG];
case TGParameterReverbSend: return m_nReverbSend[nTG];
case TGParameterInsertFXType: return m_InsertFX[nTG]->getId();
case TGParameterMidiFXType: return m_MidiArp[nTG]->getId();
case TGParameterPitchBendRange: return m_nPitchBendRange[nTG];
case TGParameterPitchBendStep: return m_nPitchBendStep[nTG];
case TGParameterPortamentoMode: return m_nPortamentoMode[nTG];
@ -1104,6 +1162,20 @@ int CMiniDexed::GetTGParameter (TTGParameter Parameter, unsigned nTG)
}
}
void CMiniDexed::SetMidiFXParameter (unsigned Parameter, int nValue, unsigned nTG, unsigned nFXType) {
assert (nTG < CConfig::ToneGenerators);
assert (m_MidiArp[nTG]->getId() == nFXType);
m_MidiArp[nTG]->setParameter(Parameter, nValue);
}
int CMiniDexed::GetMidiFXParameter (unsigned Parameter, unsigned nTG, unsigned nFXType) {
assert (nTG < CConfig::ToneGenerators);
assert (m_MidiArp[nTG]->getId() == nFXType);
return m_MidiArp[nTG]->getParameter(Parameter);
}
void CMiniDexed::SetTGFXParameter (unsigned Parameter, int nValue, unsigned nTG, unsigned nFXType) {
assert (nTG < CConfig::ToneGenerators);
assert (m_InsertFX[nTG]->getId() == nFXType);
@ -1214,12 +1286,17 @@ void CMiniDexed::ProcessSound (void)
}
float32_t SampleBuffer[2][nFrames];
m_MidiArpSpinLock[0]->Acquire();
m_MidiArp[0]->process(nFrames);
m_MidiArpSpinLock[0]->Release();
m_pTG[0]->getSamples (SampleBuffer[0], nFrames);
m_InsertFXSpinLock[0]->Acquire();
m_InsertFX[0]->process(SampleBuffer[0], SampleBuffer[0], SampleBuffer[0], SampleBuffer[1], nFrames);
m_InsertFXSpinLock[0]->Release();
// Convert dual float array (left, right) to single int16 array (left/right)
float32_t tmp_float[nFrames*2];
for(uint16_t i=0; i<nFrames;i++)
@ -1271,7 +1348,13 @@ void CMiniDexed::ProcessSound (void)
for (unsigned i = 0; i < CConfig::TGsCore1; i++)
{
assert (m_pTG[i]);
m_MidiArpSpinLock[i]->Acquire();
m_MidiArp[i]->process(nFrames);
m_MidiArpSpinLock[i]->Release();
m_pTG[i]->getSamples (m_OutputLevel[i][0], nFrames);
m_InsertFXSpinLock[i]->Acquire();
m_InsertFX[i]->process(m_OutputLevel[i][0], m_OutputLevel[i][0], m_OutputLevel[i][0], m_OutputLevel[i][1], nFrames);
m_InsertFXSpinLock[i]->Release();
@ -1439,6 +1522,12 @@ bool CMiniDexed::DoSavePerformance (void)
pParams.clear();
pParams.shrink_to_fit();
m_PerformanceConfig.SetMidiFX (m_MidiArp[nTG]->getId(), nTG);
std::vector<unsigned> pMidiFXParams = m_MidiArp[nTG]->getParameters();
m_PerformanceConfig.SetMidiFXParams (pMidiFXParams, nTG);
pMidiFXParams.clear();
pMidiFXParams.shrink_to_fit();
m_PerformanceConfig.SetDetune (m_nMasterTune[nTG], nTG);
m_PerformanceConfig.SetCutoff (m_nCutoff[nTG], nTG);
m_PerformanceConfig.SetResonance (m_nResonance[nTG], nTG);
@ -1484,6 +1573,8 @@ bool CMiniDexed::DoSavePerformance (void)
m_PerformanceConfig.SetReverbDiffusion (m_nParameter[ParameterReverbDiffusion]);
m_PerformanceConfig.SetReverbLevel (m_nParameter[ParameterReverbLevel]);
m_PerformanceConfig.SetTempo (m_nTempo);
if(m_bSaveAsDeault)
{
m_PerformanceConfig.SetNewPerformance(0);
@ -1896,6 +1987,12 @@ void CMiniDexed::LoadPerformanceParameters(void)
pParams.clear();
pParams.shrink_to_fit();
setMidiFXType(m_PerformanceConfig.GetMidiFX (nTG), nTG);
std::vector<unsigned> pMidiFXParams = m_PerformanceConfig.GetMidiFXParams(nTG);
m_MidiArp[nTG]->setParameters(pMidiFXParams);
pMidiFXParams.clear();
pMidiFXParams.shrink_to_fit();
m_nNoteLimitLow[nTG] = m_PerformanceConfig.GetNoteLimitLow (nTG);
m_nNoteLimitHigh[nTG] = m_PerformanceConfig.GetNoteLimitHigh (nTG);
m_nNoteShift[nTG] = m_PerformanceConfig.GetNoteShift (nTG);
@ -1937,6 +2034,8 @@ void CMiniDexed::LoadPerformanceParameters(void)
SetParameter (ParameterReverbLowPass, m_PerformanceConfig.GetReverbLowPass ());
SetParameter (ParameterReverbDiffusion, m_PerformanceConfig.GetReverbDiffusion ());
SetParameter (ParameterReverbLevel, m_PerformanceConfig.GetReverbLevel ());
SetParameter (ParameterTempo, m_PerformanceConfig.GetTempo ());
}
std::string CMiniDexed::GetNewPerformanceDefaultName(void)

@ -45,6 +45,7 @@
#include "effect_platervbstereo.h"
#include "effect_compressor.h"
#include "effects.h"
#include "midi_effects.h"
class CMiniDexed
#ifdef ARM_ALLOW_MULTI_CORE
@ -85,6 +86,9 @@ public:
void setTempo(unsigned nValue);
void handleClock(void);
bool isPlaying(void);
void setPlaying(bool bValue);
void keyup (int16_t pitch, unsigned nTG);
void keydown (int16_t pitch, uint8_t velocity, unsigned nTG);
@ -100,6 +104,7 @@ public:
void setAftertouch (uint8_t value, unsigned nTG);
void setInsertFXType (unsigned nType, unsigned nTG);
void setMidiFXType (unsigned nType, unsigned nTG);
void setSendFXType (unsigned nType);
void setSendFXLevel (unsigned nValue);
@ -191,6 +196,7 @@ public:
TGParameterVolume,
TGParameterPan,
TGParameterInsertFXType,
TGParameterMidiFXType,
TGParameterMasterTune,
TGParameterCutoff,
TGParameterResonance,
@ -229,6 +235,9 @@ public:
void SetTGParameter (TTGParameter Parameter, int nValue, unsigned nTG);
int GetTGParameter (TTGParameter Parameter, unsigned nTG);
void SetMidiFXParameter (unsigned Parameter, int nValue, unsigned nTG, unsigned nFXType);
int GetMidiFXParameter (unsigned Parameter, unsigned nTG, unsigned nFXType);
void SetTGFXParameter (unsigned Parameter, int nValue, unsigned nTG, unsigned nFXType);
int GetTGFXParameter (unsigned Parameter, unsigned nTG, unsigned nFXType);
@ -302,6 +311,7 @@ private:
unsigned m_nNoteLimitHigh[CConfig::ToneGenerators];
int m_nNoteShift[CConfig::ToneGenerators];
MidiEffect* m_MidiArp[CConfig::ToneGenerators];
AudioEffect* m_InsertFX[CConfig::ToneGenerators];
unsigned m_nReverbSend[CConfig::ToneGenerators];
@ -339,6 +349,7 @@ private:
AudioEffect* m_SendFX = NULL;
float32_t m_SendFXLevel = 1.0f;
CSpinLock* m_MidiArpSpinLock[CConfig::ToneGenerators];
CSpinLock* m_InsertFXSpinLock[CConfig::ToneGenerators];
CSpinLock m_SendFXSpinLock;
CSpinLock m_ReverbSpinLock;
@ -359,6 +370,7 @@ private:
unsigned m_nClockCounter;
unsigned long m_mClockTime;
unsigned m_nTempo; // Tempo in BPM
bool m_bPlaying = false;
};
#endif

@ -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 = &notch;
break;
default:
out = &low;
break;
}
}
private:
void UpdateCoefficient()
{
f = 2. * std::sin(PI * frequency / sampleRate);
}
};
#endif

@ -139,6 +139,12 @@ bool CPerformanceConfig::Load (void)
PropertyName.Format ("InsertFXParams%u", nTG+1);
m_sInsertFXParams[nTG] = m_Properties.GetString (PropertyName, "");
PropertyName.Format ("MidiFX%u", nTG+1);
m_nMidiFX[nTG] = m_Properties.GetNumber (PropertyName, 0);
PropertyName.Format ("MidiFXParams%u", nTG+1);
m_sMidiFXParams[nTG] = m_Properties.GetString (PropertyName, "");
PropertyName.Format ("Detune%u", nTG+1);
m_nDetune[nTG] = m_Properties.GetSignedNumber (PropertyName, 0);
@ -217,6 +223,8 @@ bool CPerformanceConfig::Load (void)
m_nReverbDiffusion = m_Properties.GetNumber ("ReverbDiffusion", 65);
m_nReverbLevel = m_Properties.GetNumber ("ReverbLevel", 100);
m_nTempo = m_Properties.GetNumber ("Tempo", 120);
// Set EFFECT_REVERB as Default for backward compatibility
// EFFECT_REVERB 7
m_nSendFX = m_Properties.GetNumber ("SendFX", 7);
@ -274,6 +282,12 @@ bool CPerformanceConfig::Save (void)
PropertyName.Format ("InsertFXParams%u", nTG+1);
m_Properties.SetString (PropertyName, m_sInsertFXParams[nTG].c_str());
PropertyName.Format ("MidiFX%u", nTG+1);
m_Properties.SetNumber (PropertyName, m_nMidiFX[nTG]);
PropertyName.Format ("MidiFXParams%u", nTG+1);
m_Properties.SetString (PropertyName, m_sMidiFXParams[nTG].c_str());
PropertyName.Format ("Detune%u", nTG+1);
m_Properties.SetSignedNumber (PropertyName, m_nDetune[nTG]);
@ -395,6 +409,8 @@ bool CPerformanceConfig::Save (void)
tokens.shrink_to_fit();
}
m_Properties.SetNumber ("Tempo", m_nTempo);
return m_Properties.Save ();
}
@ -437,21 +453,19 @@ unsigned CPerformanceConfig::GetInsertFX (unsigned nTG) const
std::vector<unsigned> CPerformanceConfig::GetInsertFXParams (unsigned nTG) const
{
assert (nTG < CConfig::ToneGenerators);
return StringToVector(m_sInsertFXParams[nTG]);
}
std::vector<unsigned> tokens;
std::string params = m_sInsertFXParams[nTG];
if (params.empty()) {
return tokens;
}
unsigned CPerformanceConfig::GetMidiFX (unsigned nTG) const
{
assert (nTG < CConfig::ToneGenerators);
return m_nMidiFX[nTG];
}
char delimiter = ',';
std::stringstream ss(params);
std::string temp;
while (getline(ss, temp, delimiter))
{
tokens.push_back(stoi(temp));
}
return tokens;
std::vector<unsigned> CPerformanceConfig::GetMidiFXParams (unsigned nTG) const
{
assert (nTG < CConfig::ToneGenerators);
return StringToVector(m_sMidiFXParams[nTG]);
}
int CPerformanceConfig::GetDetune (unsigned nTG) const
@ -535,16 +549,19 @@ void CPerformanceConfig::SetInsertFX (unsigned nValue, unsigned nTG)
void CPerformanceConfig::SetInsertFXParams (std::vector<unsigned> pParams, unsigned nTG)
{
assert (nTG < CConfig::ToneGenerators);
m_sInsertFXParams[nTG] = VectorToString(pParams);
}
std::string params = "";
for (size_t i = 0; i < pParams.size(); i++)
{
if (i != 0) {
params += ",";
}
params += std::to_string(pParams[i]);
}
m_sInsertFXParams[nTG] = params;
void CPerformanceConfig::SetMidiFX (unsigned nValue, unsigned nTG)
{
assert (nTG < CConfig::ToneGenerators);
m_nMidiFX[nTG] = nValue;
}
void CPerformanceConfig::SetMidiFXParams (std::vector<unsigned> pParams, unsigned nTG)
{
assert (nTG < CConfig::ToneGenerators);
m_sMidiFXParams[nTG] = VectorToString(pParams);
}
void CPerformanceConfig::SetDetune (int nValue, unsigned nTG)
@ -601,20 +618,7 @@ unsigned CPerformanceConfig::GetSendFX (void) const
std::vector<unsigned> CPerformanceConfig::GetSendFXParams (void) const
{
std::vector<unsigned> tokens;
std::string params = m_sSendFXParams;
if (params.empty()) {
return tokens;
}
char delimiter = ',';
std::stringstream ss(params);
std::string temp;
while (getline(ss, temp, delimiter))
{
tokens.push_back(stoi(temp));
}
return tokens;
return StringToVector(m_sSendFXParams);
}
unsigned CPerformanceConfig::GetSendFXLevel (void) const
@ -658,6 +662,11 @@ unsigned CPerformanceConfig::GetReverbLevel (void) const
return m_nReverbLevel;
}
unsigned CPerformanceConfig::GetTempo (void) const
{
return m_nTempo;
}
void CPerformanceConfig::SetCompressorEnable (bool bValue)
{
m_bCompressorEnable = bValue;
@ -670,15 +679,7 @@ void CPerformanceConfig::SetSendFX (unsigned nValue)
void CPerformanceConfig::SetSendFXParams (std::vector<unsigned> pParams)
{
std::string params = "";
for (size_t i = 0; i < pParams.size(); i++)
{
if (i != 0) {
params += ",";
}
params += std::to_string(pParams[i]);
}
m_sSendFXParams = params;
m_sSendFXParams = VectorToString(pParams);
}
void CPerformanceConfig::SetSendFXLevel (unsigned nValue)
@ -720,6 +721,12 @@ void CPerformanceConfig::SetReverbLevel (unsigned nValue)
{
m_nReverbLevel = nValue;
}
void CPerformanceConfig::SetTempo (unsigned nValue)
{
m_nTempo = nValue;
}
// Pitch bender and portamento:
void CPerformanceConfig::SetPitchBendRange (unsigned nValue, unsigned nTG)
{
@ -1490,3 +1497,34 @@ bool CPerformanceConfig::IsValidPerformanceBank(unsigned nBankID)
}
return true;
}
std::string CPerformanceConfig::VectorToString (std::vector<unsigned> pParams)
{
std::string params = "";
for (size_t i = 0; i < pParams.size(); i++)
{
if (i != 0) {
params += ",";
}
params += std::to_string(pParams[i]);
}
return params;
}
std::vector<unsigned> CPerformanceConfig::StringToVector (std::string sParams) const
{
std::vector<unsigned> tokens;
if (sParams.empty()) {
return tokens;
}
char delimiter = ',';
std::stringstream ss(sParams);
std::string temp;
while (getline(ss, temp, delimiter))
{
tokens.push_back(stoi(temp));
}
return tokens;
}

@ -51,6 +51,8 @@ public:
unsigned GetPan (unsigned nTG) const; // 0 .. 127
unsigned GetInsertFX (unsigned nTG) const; // 0 .. X
std::vector<unsigned> GetInsertFXParams (unsigned nTG) const;
unsigned GetMidiFX (unsigned nTG) const; // 0 .. X
std::vector<unsigned> GetMidiFXParams (unsigned nTG) const;
int GetDetune (unsigned nTG) const; // -99 .. 99
unsigned GetCutoff (unsigned nTG) const; // 0 .. 99
unsigned GetResonance (unsigned nTG) const; // 0 .. 99
@ -81,6 +83,8 @@ public:
void SetPan (unsigned nValue, unsigned nTG);
void SetInsertFX (unsigned nValue, unsigned nTG);
void SetInsertFXParams (std::vector<unsigned> pParams, unsigned nTG);
void SetMidiFX (unsigned nValue, unsigned nTG);
void SetMidiFXParams (std::vector<unsigned> pParams, unsigned nTG);
void SetDetune (int nValue, unsigned nTG);
void SetCutoff (unsigned nValue, unsigned nTG);
void SetResonance (unsigned nValue, unsigned nTG);
@ -118,6 +122,7 @@ public:
unsigned GetReverbLowPass (void) const; // 0 .. 99
unsigned GetReverbDiffusion (void) const; // 0 .. 99
unsigned GetReverbLevel (void) const; // 0 .. 99
unsigned GetTempo (void) const;
void SetCompressorEnable (bool bValue);
void SetSendFX (unsigned nValue);
@ -130,6 +135,7 @@ public:
void SetReverbLowPass (unsigned nValue);
void SetReverbDiffusion (unsigned nValue);
void SetReverbLevel (unsigned nValue);
void SetTempo (unsigned nValue);
bool VoiceDataFilled(unsigned nTG);
bool ListPerformances();
@ -170,6 +176,8 @@ private:
unsigned m_nPan[CConfig::ToneGenerators];
unsigned m_nInsertFX[CConfig::ToneGenerators];
std::string m_sInsertFXParams[CConfig::ToneGenerators];
unsigned m_nMidiFX[CConfig::ToneGenerators];
std::string m_sMidiFXParams[CConfig::ToneGenerators];
int m_nDetune[CConfig::ToneGenerators];
unsigned m_nCutoff[CConfig::ToneGenerators];
unsigned m_nResonance[CConfig::ToneGenerators];
@ -218,6 +226,10 @@ private:
unsigned m_nReverbLowPass;
unsigned m_nReverbDiffusion;
unsigned m_nReverbLevel;
unsigned m_nTempo;
std::string VectorToString (std::vector<unsigned> pParams);
std::vector<unsigned> StringToVector (std::string sParams) const;
};
#endif

@ -35,7 +35,7 @@ LOGMODULE ("uimenu");
const CUIMenu::TMenuItem CUIMenu::s_MenuRoot[] =
{
{"MiniDexed", MenuHandler, s_MainMenu},
{"MiniDexed", MainMenuHandler, s_MainMenu},
{0}
};
@ -75,6 +75,7 @@ const CUIMenu::TMenuItem CUIMenu::s_TGMenu[] =
{"Portamento", MenuHandler, s_EditPortamentoMenu},
{"Poly/Mono", EditTGParameter, 0, CMiniDexed::TGParameterMonoMode},
{"Modulation", MenuHandler, s_ModulationMenu},
{"Midi FX", MenuHandlerMidiFX},
{"Channel", EditTGParameter, 0, CMiniDexed::TGParameterMIDIChannel},
{"Edit Voice", MenuHandler, s_EditVoiceMenu},
{0}
@ -236,6 +237,61 @@ CUIMenu::TMenuItem CUIMenu::s_FXReverb[] =
{0}
};
CUIMenu::TMenuItem CUIMenu::s_FXMVerb[] =
{
{"Bypass", EditTGFXParameter, 0, AudioEffectMVerb::Param::BYPASS},
{"Damp Fq", EditTGFXParameter, 0, AudioEffectMVerb::Param::DAMPINGFREQ},
{"Density", EditTGFXParameter, 0, AudioEffectMVerb::Param::DENSITY},
{"Band Fq", EditTGFXParameter, 0, AudioEffectMVerb::Param::BANDWIDTHFREQ},
{"Decay", EditTGFXParameter, 0, AudioEffectMVerb::Param::DECAY},
{"Predelay", EditTGFXParameter, 0, AudioEffectMVerb::Param::PREDELAY},
{"Size", EditTGFXParameter, 0, AudioEffectMVerb::Param::SIZE},
{"Gain", EditTGFXParameter, 0, AudioEffectMVerb::Param::GAIN},
{"Mix", EditTGFXParameter, 0, AudioEffectMVerb::Param::MIX},
{"Early Mix", EditTGFXParameter, 0, AudioEffectMVerb::Param::EARLYMIX},
{0}
};
CUIMenu::TMenuItem CUIMenu::s_FX3BandEQ[] =
{
{"Bypass", EditTGFXParameter, 0, AudioEffect3BandEQ::Param::BYPASS},
{"Low", EditTGFXParameter, 0, AudioEffect3BandEQ::Param::EQ_LOW},
{"Mid", EditTGFXParameter, 0, AudioEffect3BandEQ::Param::EQ_MID},
{"High", EditTGFXParameter, 0, AudioEffect3BandEQ::Param::EQ_HIGH},
{"Master", EditTGFXParameter, 0, AudioEffect3BandEQ::Param::MASTER},
{"Low Mid FQ", EditTGFXParameter, 0, AudioEffect3BandEQ::Param::LOW_MID_FQ},
{"Mid High FQ", EditTGFXParameter, 0, AudioEffect3BandEQ::Param::MID_HIGH_FQ},
{0}
};
const CUIMenu::TMenuItem CUIMenu::s_MidiFX[] =
{
{"Type:", EditTGParameter2, 0, CMiniDexed::TGParameterMidiFXType},
{"Edit:", EditMidiFX},
{0}
};
CUIMenu::TMenuItem CUIMenu::s_MidiFXNone[] =
{
{"None"},
{0}
};
CUIMenu::TMenuItem CUIMenu::s_MidiFXArp[] =
{
{"Bypass", EditTGMidiFXParameter, 0, MidiArp::Param::BYPASS},
{"Latch", EditTGMidiFXParameter, 0, MidiArp::Param::LATCH},
{"Sync", EditTGMidiFXParameter, 0, MidiArp::Param::SYNC},
{"Arp Mode", EditTGMidiFXParameter, 0, MidiArp::Param::ARP_MODE},
{"Division", EditTGMidiFXParameter, 0, MidiArp::Param::DIVISION},
{"Note Length", EditTGMidiFXParameter, 0, MidiArp::Param::NOTE_LENGTH},
{"Velocity", EditTGMidiFXParameter, 0, MidiArp::Param::VELOCITY},
{"Oct Spread", EditTGMidiFXParameter, 0, MidiArp::Param::OCTAVE_SPREAD},
{"Oct Mode", EditTGMidiFXParameter, 0, MidiArp::Param::OCTAVE_MODE},
{"Panic", EditTGMidiFXParameter, 0, MidiArp::Param::PANIC},
{0}
};
// inserting menu items before "OP1" affect OPShortcutHandler()
const CUIMenu::TMenuItem CUIMenu::s_EditVoiceMenu[] =
{
@ -307,7 +363,7 @@ const CUIMenu::TMenuItem CUIMenu::s_SaveMenu[] =
const CUIMenu::TParameter CUIMenu::s_GlobalParameter[CMiniDexed::ParameterUnknown] =
{
{0, 1, 1, ToOnOff}, // ParameterCompressorEnable
{0, 7, 1, ToFXType}, // ParameterSendFXType
{0, 9, 1, ToFXType}, // ParameterSendFXType
{0, 100, 1}, // ParameterSendFXLevel
{0, 1, 1, ToOnOff}, // ParameterReverbEnable
{0, 99, 1}, // ParameterReverbSize
@ -330,7 +386,8 @@ const CUIMenu::TParameter CUIMenu::s_TGParameter[CMiniDexed::TGParameterUnknown]
{0, CSysExFileLoader::VoicesPerBank-1, 1}, // TGParameterProgram
{0, 127, 8, ToVolume}, // TGParameterVolume
{0, 127, 8, ToPan}, // TGParameterPan
{0, 7, 1, ToFXType}, // TGParameterInsertFXType
{0, 9, 1, ToFXType}, // TGParameterInsertFXType
{0, 1, 1, ToMidiFXType}, // TGParameterMidiFXType
{-99, 99, 1}, // TGParameterMasterTune
{0, 99, 1}, // TGParameterCutoff
{0, 99, 1}, // TGParameterResonance
@ -436,6 +493,48 @@ const CUIMenu::TParameter CUIMenu::s_TGFXReverbParam[AudioEffectPlateReverb::Par
{0, 99, 1}, // LEVEL
};
// must match AudioEffectMVerb::Param
const CUIMenu::TParameter CUIMenu::s_TGFXMVerbParam[AudioEffectMVerb::Param::UNKNOWN] =
{
{0, 1, 1, ToOnOff}, // BYPASS
{0, 100, 1}, // DAMPINGFREQ
{0, 100, 1}, // DENSITY
{0, 100, 1}, // BANDWIDTHFREQ
{0, 100, 1}, // DECAY
{0, 100, 1}, // PREDELAY
{0, 100, 1}, // SIZE
{0, 100, 1}, // GAIN
{0, 100, 1, ToMix}, // MIX
{0, 100, 1}, // EARLYMIX
};
// must match AudioEffect3BandEQ::Param
const CUIMenu::TParameter CUIMenu::s_TGFX3BandEQParam[AudioEffect3BandEQ::Param::UNKNOWN] =
{
{0, 1, 1, ToOnOff}, // BYPASS
{0, 100, 1}, // LOW
{0, 100, 1}, // MID
{0, 100, 1}, // HIGH
{0, 100, 1}, // MASTER
{0, 1000, 10}, // LOW_MID_FQ
{1000, 20000, 10}, // MID_HIGH_FQ
};
// must match MidiArp::Param
const CUIMenu::TParameter CUIMenu::s_TGMidiFXArpParam[MidiArp::Param::UNKNOWN] =
{
{0, 1, 1, ToOnOff}, // BYPASS
{0, 1, 1, ToOnOff}, // LATCH
{0, 2, 1}, // SYNC
{0, 5, 1, ToArpMode}, // ARP_MODE
{0, 12, 1, ToArpDivision}, // DIVISION
{0, 100, 1}, // NOTE_LENGTH
{0, 127, 1}, // VELOCITY
{1, 4, 1}, // OCTAVE_SPREAD
{0, 4, 1, ToArpOctMode}, // OCTAVE_MODE
{0, 1, 1, ToOnOff} // PANIC
};
// must match DexedVoiceParameters in Synth_Dexed
const CUIMenu::TParameter CUIMenu::s_VoiceParameter[] =
{
@ -604,6 +703,81 @@ void CUIMenu::EventHandler (TMenuEvent Event)
}
}
void CUIMenu::MainMenuHandler (CUIMenu *pUIMenu, TMenuEvent Event)
{
bool menuChange = false;
switch (Event)
{
case MenuEventUpdate:
break;
case MenuEventSelect: // push menu
assert (pUIMenu->m_nCurrentMenuDepth < MaxMenuDepth);
pUIMenu->m_MenuStackParent[pUIMenu->m_nCurrentMenuDepth] = pUIMenu->m_pParentMenu;
pUIMenu->m_MenuStackMenu[pUIMenu->m_nCurrentMenuDepth] = pUIMenu->m_pCurrentMenu;
pUIMenu->m_nMenuStackItem[pUIMenu->m_nCurrentMenuDepth]
= pUIMenu->m_nCurrentMenuItem;
pUIMenu->m_nMenuStackSelection[pUIMenu->m_nCurrentMenuDepth]
= pUIMenu->m_nCurrentSelection;
pUIMenu->m_nMenuStackParameter[pUIMenu->m_nCurrentMenuDepth]
= pUIMenu->m_nCurrentParameter;
pUIMenu->m_nCurrentMenuDepth++;
pUIMenu->m_pParentMenu = pUIMenu->m_pCurrentMenu;
pUIMenu->m_nCurrentParameter =
pUIMenu->m_pCurrentMenu[pUIMenu->m_nCurrentSelection].Parameter;
pUIMenu->m_pCurrentMenu =
pUIMenu->m_pCurrentMenu[pUIMenu->m_nCurrentSelection].MenuItem;
pUIMenu->m_nCurrentMenuItem = pUIMenu->m_nCurrentSelection;
pUIMenu->m_nCurrentSelection = 0;
menuChange = true;
break;
case MenuEventStepDown:
if (pUIMenu->m_nCurrentSelection > 0)
{
pUIMenu->m_nCurrentSelection--;
}
break;
case MenuEventStepUp:
++pUIMenu->m_nCurrentSelection;
if (!pUIMenu->m_pCurrentMenu[pUIMenu->m_nCurrentSelection].Name) // more entries?
{
pUIMenu->m_nCurrentSelection--;
}
break;
default:
return;
}
if (pUIMenu->m_pCurrentMenu) // if this is another menu?
{
string strTempo;
if (menuChange)
{
strTempo = "";
}
else
{
strTempo = (pUIMenu->m_pMiniDexed->isPlaying() ? ">" : "#") +
to_string(pUIMenu->m_pMiniDexed->getTempo());
}
pUIMenu->m_pUI->DisplayWrite (
pUIMenu->m_pParentMenu[pUIMenu->m_nCurrentMenuItem].Name,
strTempo.c_str(),
pUIMenu->m_pCurrentMenu[pUIMenu->m_nCurrentSelection].Name,
pUIMenu->m_nCurrentSelection > 0,
!!pUIMenu->m_pCurrentMenu[pUIMenu->m_nCurrentSelection+1].Name);
}
else
{
pUIMenu->EventHandler (MenuEventUpdate); // no, update parameter display
}
}
void CUIMenu::MenuHandler (CUIMenu *pUIMenu, TMenuEvent Event)
{
switch (Event)
@ -655,7 +829,7 @@ void CUIMenu::MenuHandler (CUIMenu *pUIMenu, TMenuEvent Event)
{
pUIMenu->m_pUI->DisplayWrite (
pUIMenu->m_pParentMenu[pUIMenu->m_nCurrentMenuItem].Name,
to_string(pUIMenu->m_pMiniDexed->getTempo()).c_str(),
"",
pUIMenu->m_pCurrentMenu[pUIMenu->m_nCurrentSelection].Name,
pUIMenu->m_nCurrentSelection > 0,
!!pUIMenu->m_pCurrentMenu[pUIMenu->m_nCurrentSelection+1].Name);
@ -666,6 +840,93 @@ void CUIMenu::MenuHandler (CUIMenu *pUIMenu, TMenuEvent Event)
}
}
void CUIMenu::MenuHandlerMidiFX (CUIMenu *pUIMenu, TMenuEvent Event)
{
// Setup menu when it's open
if (!pUIMenu->m_pCurrentMenu)
{
pUIMenu->m_pCurrentMenu = s_MidiFX;
}
switch (Event)
{
case MenuEventUpdate:
break;
case MenuEventSelect: // push menu
assert (pUIMenu->m_nCurrentMenuDepth < MaxMenuDepth);
pUIMenu->m_MenuStackParent[pUIMenu->m_nCurrentMenuDepth] = pUIMenu->m_pParentMenu;
pUIMenu->m_MenuStackMenu[pUIMenu->m_nCurrentMenuDepth] = pUIMenu->m_pCurrentMenu;
pUIMenu->m_nMenuStackItem[pUIMenu->m_nCurrentMenuDepth]
= pUIMenu->m_nCurrentMenuItem;
pUIMenu->m_nMenuStackSelection[pUIMenu->m_nCurrentMenuDepth]
= pUIMenu->m_nCurrentSelection;
pUIMenu->m_nMenuStackParameter[pUIMenu->m_nCurrentMenuDepth]
= pUIMenu->m_nCurrentParameter;
pUIMenu->m_nCurrentMenuDepth++;
pUIMenu->m_pParentMenu = pUIMenu->m_pCurrentMenu;
pUIMenu->m_nCurrentParameter =
pUIMenu->m_pCurrentMenu[pUIMenu->m_nCurrentSelection].Parameter;
pUIMenu->m_pCurrentMenu =
pUIMenu->m_pCurrentMenu[pUIMenu->m_nCurrentSelection].MenuItem;
pUIMenu->m_nCurrentMenuItem = pUIMenu->m_nCurrentSelection;
pUIMenu->m_nCurrentSelection = 0;
break;
case MenuEventStepDown:
if (pUIMenu->m_nCurrentSelection > 0)
{
pUIMenu->m_nCurrentSelection--;
}
break;
case MenuEventStepUp:
++pUIMenu->m_nCurrentSelection;
if (!pUIMenu->m_pCurrentMenu[pUIMenu->m_nCurrentSelection].Name) // more entries?
{
pUIMenu->m_nCurrentSelection--;
}
break;
case MenuEventPressAndStepDown:
case MenuEventPressAndStepUp:
pUIMenu->TGShortcutHandler (Event);
return;
default:
return;
}
if (pUIMenu->m_pCurrentMenu) // if this is another menu?
{
// Identify TG
unsigned nTG = pUIMenu->m_nMenuStackParameter[pUIMenu->m_nCurrentMenuDepth-1];
// Create TG label
string TG ("TG");
TG += to_string (nTG+1);
// Get current FX type
int nFxType = pUIMenu->m_pMiniDexed->GetTGParameter(CMiniDexed::TGParameterMidiFXType, nTG);
// Create Paramter with type label
std::string value;
value.append(pUIMenu->m_pCurrentMenu[pUIMenu->m_nCurrentSelection].Name);
value.append(getMidiFXTypeName(nFxType));
pUIMenu->m_pUI->DisplayWrite (
TG.c_str (),
pUIMenu->m_pParentMenu[pUIMenu->m_nCurrentMenuItem].Name,
value.c_str(),
pUIMenu->m_nCurrentSelection > 0,
!!pUIMenu->m_pCurrentMenu[pUIMenu->m_nCurrentSelection+1].Name);
}
else
{
pUIMenu->EventHandler (MenuEventUpdate); // no, update parameter display
}
}
void CUIMenu::MenuHandlerInsertFX (CUIMenu *pUIMenu, TMenuEvent Event)
{
// Setup menu when it's open
@ -1103,6 +1364,148 @@ void CUIMenu::EditTGParameter2 (CUIMenu *pUIMenu, TMenuEvent Event) // second me
nValue > rParam.Minimum, nValue < rParam.Maximum);
}
void CUIMenu::EditMidiFX (CUIMenu *pUIMenu, TMenuEvent Event)
{
unsigned nTG = pUIMenu->m_nMenuStackParameter[pUIMenu->m_nCurrentMenuDepth-2];
int nFXType = pUIMenu->m_pMiniDexed->GetTGParameter(CMiniDexed::TGParameterMidiFXType, nTG);
pUIMenu->m_pCurrentMenu = getMidiFXMenuItem(nFXType);
switch (Event)
{
case MenuEventUpdate:
break;
case MenuEventSelect: // push menu
if (nFXType == MidiEffect::ID)
{
break;
}
assert (pUIMenu->m_nCurrentMenuDepth < MaxMenuDepth);
pUIMenu->m_MenuStackParent[pUIMenu->m_nCurrentMenuDepth] = pUIMenu->m_pParentMenu;
pUIMenu->m_MenuStackMenu[pUIMenu->m_nCurrentMenuDepth] = pUIMenu->m_pCurrentMenu;
pUIMenu->m_nMenuStackItem[pUIMenu->m_nCurrentMenuDepth]
= pUIMenu->m_nCurrentMenuItem;
pUIMenu->m_nMenuStackSelection[pUIMenu->m_nCurrentMenuDepth]
= pUIMenu->m_nCurrentSelection;
pUIMenu->m_nMenuStackParameter[pUIMenu->m_nCurrentMenuDepth]
= pUIMenu->m_nCurrentParameter;
pUIMenu->m_nCurrentMenuDepth++;
pUIMenu->m_pParentMenu = pUIMenu->m_pCurrentMenu;
pUIMenu->m_nCurrentParameter =
pUIMenu->m_pCurrentMenu[pUIMenu->m_nCurrentSelection].Parameter;
pUIMenu->m_pCurrentMenu =
pUIMenu->m_pCurrentMenu[pUIMenu->m_nCurrentSelection].MenuItem;
pUIMenu->m_nCurrentMenuItem = pUIMenu->m_nCurrentSelection;
pUIMenu->m_nCurrentSelection = 0;
break;
case MenuEventStepDown:
if (pUIMenu->m_nCurrentSelection > 0)
{
pUIMenu->m_nCurrentSelection--;
}
break;
case MenuEventStepUp:
++pUIMenu->m_nCurrentSelection;
if (!pUIMenu->m_pCurrentMenu[pUIMenu->m_nCurrentSelection].Name) // more entries?
{
pUIMenu->m_nCurrentSelection--;
}
break;
default:
return;
}
if (pUIMenu->m_pCurrentMenu) // if this is another menu?
{
string TG ("TG");
TG += to_string (nTG+1);
pUIMenu->m_pUI->DisplayWrite (
TG.c_str (),
getMidiFXTypeName(nFXType).c_str(),
pUIMenu->m_pCurrentMenu[pUIMenu->m_nCurrentSelection].Name,
pUIMenu->m_nCurrentSelection > 0,
!!pUIMenu->m_pCurrentMenu[pUIMenu->m_nCurrentSelection+1].Name);
}
else
{
pUIMenu->EventHandler (MenuEventUpdate); // no, update parameter display
}
}
void CUIMenu::EditTGMidiFXParameter (CUIMenu *pUIMenu, TMenuEvent Event)
{
// Get TG
unsigned nTG = pUIMenu->m_nMenuStackParameter[pUIMenu->m_nCurrentMenuDepth-3];
// Get FX type
int nFXType = pUIMenu->m_pMiniDexed->GetTGParameter(CMiniDexed::TGParameterMidiFXType, nTG);
// Get Param
unsigned nParam = pUIMenu->m_nCurrentParameter;
TParameter pParam = getMidiFXParameter(nFXType, nParam);
const TParameter &rParam = pParam;
int nValue = pUIMenu->m_pMiniDexed->GetMidiFXParameter (nParam, nTG, nFXType);
switch (Event)
{
case MenuEventUpdate:
break;
case MenuEventPressAndStepDown:
nValue -= rParam.Increment * 9;
case MenuEventStepDown:
nValue -= rParam.Increment;
if (nValue < rParam.Minimum)
{
nValue = rParam.Minimum;
}
pUIMenu->m_pMiniDexed->SetMidiFXParameter (nParam, nValue, nTG, nFXType);
break;
case MenuEventPressAndStepUp:
nValue += rParam.Increment * 9;
case MenuEventStepUp:
nValue += rParam.Increment;
if (nValue > rParam.Maximum)
{
nValue = rParam.Maximum;
}
pUIMenu->m_pMiniDexed->SetMidiFXParameter (nParam, nValue, nTG, nFXType);
break;
default:
return;
}
string TG ("TG");
TG += to_string (nTG+1);
// Get value again after change
nValue = pUIMenu->m_pMiniDexed->GetMidiFXParameter (nParam, nTG, nFXType);
CUIMenu::TToString *pToString = rParam.ToString;
string Value;
if (pToString)
{
Value = (*pToString) (nValue);
}
else
{
Value = to_string (nValue);
}
pUIMenu->m_pUI->DisplayWrite (TG.c_str (),
pUIMenu->m_pParentMenu[pUIMenu->m_nCurrentMenuItem].Name,
Value.c_str (),
nValue > rParam.Minimum, nValue < rParam.Maximum);
}
void CUIMenu::EditInsertFX (CUIMenu *pUIMenu, TMenuEvent Event)
{
unsigned nTG = pUIMenu->m_nMenuStackParameter[pUIMenu->m_nCurrentMenuDepth-2];
@ -2556,6 +2959,12 @@ CUIMenu::TMenuItem* CUIMenu::getFXMenuItem(unsigned type)
case EFFECT_REVERB:
menu = s_FXReverb;
break;
case EFFECT_MVERB:
menu = s_FXMVerb;
break;
case EFFECT_3BANDEQ:
menu = s_FX3BandEQ;
break;
case EFFECT_NONE:
default:
menu = s_FXNone;
@ -2614,6 +3023,41 @@ CUIMenu::TParameter CUIMenu::getFXParameter(unsigned type, unsigned nParam)
case EFFECT_REVERB:
pParam = s_TGFXReverbParam[nParam];
break;
case EFFECT_MVERB:
pParam = s_TGFXMVerbParam[nParam];
break;
case EFFECT_3BANDEQ:
pParam = s_TGFX3BandEQParam[nParam];
break;
default:
break;
}
return pParam;
}
CUIMenu::TMenuItem* CUIMenu::getMidiFXMenuItem(unsigned type)
{
CUIMenu::TMenuItem* menu;
switch (type)
{
case MidiArp::ID:
menu = s_MidiFXArp;
break;
case MidiEffect::ID:
default:
menu = s_MidiFXNone;
break;
}
return menu;
}
CUIMenu::TParameter CUIMenu::getMidiFXParameter(unsigned type, unsigned nParam)
{
TParameter pParam;
switch (type)
{
case MidiArp::ID:
pParam = s_TGMidiFXArpParam[nParam];
default:
break;
}

@ -79,6 +79,7 @@ private:
};
private:
static void MainMenuHandler (CUIMenu *pUIMenu, TMenuEvent Event);
static void MenuHandler (CUIMenu *pUIMenu, TMenuEvent Event);
static void EditGlobalParameter (CUIMenu *pUIMenu, TMenuEvent Event);
static void EditVoiceBankNumber (CUIMenu *pUIMenu, TMenuEvent Event);
@ -93,6 +94,12 @@ private:
static void SavePerformanceNewFile (CUIMenu *pUIMenu, TMenuEvent Event);
static void EditPerformanceBankNumber (CUIMenu *pUIMenu, TMenuEvent Event);
static void MenuHandlerMidiFX (CUIMenu *pUIMenu, TMenuEvent Event);
static void EditMidiFX (CUIMenu *pUIMenu, TMenuEvent Event);
static void EditTGMidiFXParameter (CUIMenu *pUIMenu, TMenuEvent Event);
static CUIMenu::TMenuItem* getMidiFXMenuItem(unsigned type);
static CUIMenu::TParameter getMidiFXParameter(unsigned type, unsigned param);
static CUIMenu::TMenuItem* getFXMenuItem(unsigned type);
static CUIMenu::TParameter getFXParameter(unsigned type, unsigned param);
@ -165,6 +172,7 @@ private:
static const TMenuItem s_SendFXMenu[];
static const TMenuItem s_ReverbMenu[];
static const TMenuItem s_InsertFX[];
static const TMenuItem s_MidiFX[];
static TMenuItem s_FXNone[];
static TMenuItem s_FXChorus[];
@ -174,6 +182,11 @@ private:
static TMenuItem s_FXBigMuff[];
static TMenuItem s_FXTalReverb3[];
static TMenuItem s_FXReverb[];
static TMenuItem s_FXMVerb[];
static TMenuItem s_FX3BandEQ[];
static TMenuItem s_MidiFXNone[];
static TMenuItem s_MidiFXArp[];
static const TMenuItem s_EditVoiceMenu[];
static const TMenuItem s_OperatorMenu[];
@ -194,6 +207,9 @@ private:
static const TParameter s_TGFXBigMuffParam[];
static const TParameter s_TGFXTalReverb3Param[];
static const TParameter s_TGFXReverbParam[];
static const TParameter s_TGFXMVerbParam[];
static const TParameter s_TGFX3BandEQParam[];
static const TParameter s_TGMidiFXArpParam[];
static const TParameter s_VoiceParameter[];
static const TParameter s_OPParameter[];

Loading…
Cancel
Save