Not working Arp

pull/764/head
jnonis 7 months ago
parent 9c55aa6a46
commit 0cb0553f25
  1. 7
      src/Makefile
  2. 101
      src/midi_arp.cpp
  3. 40
      src/midi_arp.h
  4. 12
      src/minidexed.cpp
  5. 2
      src/minidexed.h
  6. 648
      src/modarpeggiator/arpeggiator.cpp
  7. 122
      src/modarpeggiator/arpeggiator.hpp
  8. 241
      src/modarpeggiator/common/clock.cpp
  9. 80
      src/modarpeggiator/common/clock.hpp
  10. 33
      src/modarpeggiator/common/commons.h
  11. 65
      src/modarpeggiator/common/midiHandler.cpp
  12. 60
      src/modarpeggiator/common/midiHandler.hpp
  13. 242
      src/modarpeggiator/common/pattern.cpp
  14. 93
      src/modarpeggiator/common/pattern.hpp
  15. 339
      src/modarpeggiator/plugin.cpp
  16. 104
      src/modarpeggiator/plugin.hpp
  17. 43
      src/modarpeggiator/utils.cpp
  18. 15
      src/modarpeggiator/utils.hpp

@ -13,11 +13,14 @@ OBJS = main.o kernel.o minidexed.o config.o userinterface.o uimenu.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,101 @@
#include "midi_arp.h"
#include <stdio.h>
MidiArp::MidiArp(float32_t samplerate, CDexedAdapter* synth)
{
this->samplerate = samplerate;
this->syncMode = 1;
this->synth = synth;
arpeggiator.transmitHostInfo(0, 4, 1, 1, 120.0);
arpeggiator.setSampleRate(samplerate);
arpeggiator.setDivision(7);
arpeggiator.getMidiBuffer();
}
MidiArp::~MidiArp()
{
}
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::process(uint16_t len)
{
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.data(), events.size(), len);
events.clear();
events.shrink_to_fit();
/*
printf("Before Send Midi\n");
fflush(NULL);
struct MidiBuffer buffer = arpeggiator.getMidiBuffer();
for (unsigned x = 0; x < buffer.numBufferedEvents + buffer.numBufferedThroughEvents; x++) {
printf("Loop x: %d\n", x);
fflush(NULL);
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;
}
}
printf("After Send Midi\n");
fflush(NULL);
*/
}

@ -0,0 +1,40 @@
/*
* Base AudioEffect interface
* Javier Nonis (https://github.com/jnonis) - 2024
*/
#ifndef _MIDI_ARP_H
#define _MIDI_ARP_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"
#include "dexedadapter.h"
class MidiArp
{
public:
MidiArp(float32_t samplerate, CDexedAdapter* synth);
~MidiArp();
void keydown(int16_t pitch, uint8_t velocity);
void keyup(int16_t pitch);
void process(uint16_t len);
protected:
bool bypass = false;
float32_t samplerate;
private:
static const unsigned MIDI_NOTE_OFF = 0b1000;
static const unsigned MIDI_NOTE_ON = 0b1001;
CDexedAdapter* synth;
Arpeggiator arpeggiator;
int syncMode;
std::vector<MidiEvent> events;
};
#endif // _MIDI_ARP_H

@ -108,7 +108,8 @@ CMiniDexed::CMiniDexed (CConfig *pConfig, CInterruptSystem *pInterrupt,
m_pTG[i] = new CDexedAdapter (CConfig::MaxNotes, pConfig->GetSampleRate ());
assert (m_pTG[i]);
m_MidiArp[i] = new MidiArp(pConfig->GetSampleRate(), m_pTG[i]);
m_pTG[i]->setEngineType(pConfig->GetEngineType ());
m_pTG[i]->activate ();
}
@ -416,6 +417,7 @@ void CMiniDexed::Run (unsigned nCore)
for (unsigned i = 0; i < CConfig::TGsCore23; i++, nTG++)
{
assert (m_pTG[nTG]);
m_MidiArp[nTG]->process(m_nFramesToProcess);
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);
@ -783,7 +785,8 @@ void CMiniDexed::keyup (int16_t pitch, unsigned nTG)
pitch = ApplyNoteLimits (pitch, nTG);
if (pitch >= 0)
{
m_pTG[nTG]->keyup (pitch);
m_MidiArp[nTG]->keyup(pitch);
//m_pTG[nTG]->keyup (pitch);
}
}
@ -795,7 +798,8 @@ void CMiniDexed::keydown (int16_t pitch, uint8_t velocity, unsigned nTG)
pitch = ApplyNoteLimits (pitch, nTG);
if (pitch >= 0)
{
m_pTG[nTG]->keydown (pitch, velocity);
m_MidiArp[nTG]->keydown(pitch, velocity);
//m_pTG[nTG]->keydown (pitch, velocity);
}
}
@ -1214,6 +1218,7 @@ void CMiniDexed::ProcessSound (void)
}
float32_t SampleBuffer[2][nFrames];
m_MidiArp[0]->process(nFrames);
m_pTG[0]->getSamples (SampleBuffer[0], nFrames);
m_InsertFXSpinLock[0]->Acquire();
m_InsertFX[0]->process(SampleBuffer[0], SampleBuffer[0], SampleBuffer[0], SampleBuffer[1], nFrames);
@ -1271,6 +1276,7 @@ void CMiniDexed::ProcessSound (void)
for (unsigned i = 0; i < CConfig::TGsCore1; i++)
{
assert (m_pTG[i]);
m_MidiArp[i]->process(nFrames);
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);

@ -41,6 +41,7 @@
#include <circle/sound/soundbasedevice.h>
#include <circle/spinlock.h>
#include "common.h"
#include "midi_arp.h"
#include "effect_mixer.hpp"
#include "effect_platervbstereo.h"
#include "effect_compressor.h"
@ -302,6 +303,7 @@ private:
unsigned m_nNoteLimitHigh[CConfig::ToneGenerators];
int m_nNoteShift[CConfig::ToneGenerators];
MidiArp* m_MidiArp[CConfig::ToneGenerators];
AudioEffect* m_InsertFX[CConfig::ToneGenerators];
unsigned m_nReverbSend[CConfig::ToneGenerators];

@ -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,65 @@
#include "midiHandler.hpp"
#include <stdio.h>
MidiHandler::MidiHandler()
{
printf("MidiHandler constructor\n");
printf("MidiEvent size: %d\n", sizeof(MidiEvent));
fflush(NULL);
/*
memset(buffer.bufferedEvents, 0, MIDI_BUFFER_SIZE * sizeof(MidiEvent));
memset(buffer.bufferedMidiThroughEvents, 0, MIDI_BUFFER_SIZE * sizeof(MidiEvent));
memset(buffer.midiOutputBuffer, 0, MIDI_BUFFER_SIZE * sizeof(MidiEvent));
*/
for (unsigned i = 0; i < MIDI_BUFFER_SIZE; i++) {
printf("i: %d\n", i);
fflush(NULL);
for (unsigned x = 0; x < buffer.bufferedEvents[i].kDataSize; i++) {
printf("x: %d\n", x);
fflush(NULL);
/*
buffer.bufferedEvents[i].data[x] = 0;
buffer.bufferedMidiThroughEvents[i].data[x] = 0;
buffer.midiOutputBuffer[i].data[x] = 0;
*/
}
}
emptyMidiBuffer();
//printf("buffer.bufferedEvents: %d\n", buffer.bufferedEvents[0].data);
//fflush(NULL);
}
MidiHandler::~MidiHandler()
{
}
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[MIDI_BUFFER_SIZE];
unsigned numBufferedEvents;
MidiEvent bufferedMidiThroughEvents[MIDI_BUFFER_SIZE];
unsigned numBufferedThroughEvents;
MidiEvent midiOutputBuffer[MIDI_BUFFER_SIZE];
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_
Loading…
Cancel
Save