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