mirror of https://github.com/probonopd/MiniDexed
parent
9c55aa6a46
commit
0cb0553f25
@ -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
|
@ -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…
Reference in new issue