bool32.rising clock
bool32 hold arpeggio
bool32 double stroke each arpeggiated note
bool32 don't receive midi notes!
int32.positive arp direction (0-4)
int32.positive number of addition octaves (0-3)
int32.bipolar transpose semitones
frac32.bipolar modulate note length
bool32 gate
int32.positive number of notes in arpeggio
frac32.bipolar pitch
frac32.positive velocity
bool32.tgl retrigger gate if notelength is longer than clock input
frac32.u.map 0=as played
int32.hradio ↑, ↓, ↕, rand, order
int32.hradio octaves
frac32.s.map.klineartime.exp notelength
spinner arpeggio input midi channel (0=all channels)
bool32 holding
int32.label notes
int8_t notes[128];
int8_t notes_ord[128];
int8_t released_on_hold[128];
int8_t latched_notes[256]; // holdes last latched and ordered notes
int notecount;
int ntrigclock;
int ngate;
int highest_note;
int lowest_note;
int order_pos;
int octave;
int moveup;
int holding;
int sustainpedal;
int dehold_lock;
int ignored;
int32_t lastnote;
int32_t gateval;
int32_t outpitch;
uint32_t outvelo;
int secondclock;
outpitch = 0;
outvelo = 0;
notecount = 0;
ntrigclock = 0;
lastnote = 0;
gateval = 0;
ngate = 0;
highest_note = 0;
lowest_note = 0;
order_pos = 0;
octave = 0;
moveup = 1;
holding = 0;
sustainpedal = 0;
dehold_lock = 0;
secondclock = 0;
ignored = 0;
for (int i = 0; i < 128; i++) {
notes[i] = 0;
notes_ord[i] = 0;
released_on_hold[i] = 0;
latched_notes[i] = latched_notes[i + 128] = 0;
}
if ((inlet_clock > 0) && !ntrigclock) {
// clock received
if (notecount > 0) {
if (inlet_double > 0 && secondclock > 0) {
ngate = 1;
} else {
int32_t playnote = 0;
int32_t nextnote;
int nextoctave = octave;
int direction = (param_direction + inlet_direction) % 5;
int number_octaves = (param_octaves + inlet_octaves) % 4;
if (direction == 0) {
// up
nextnote = lastnote;
do {
nextnote = (nextnote + 1) % 128;
if (notes[nextnote] != 0) {
playnote = nextnote;
if (playnote == highest_note) {
nextoctave = (octave + 1) % (number_octaves + 1);
}
break;
}
} while (nextnote != lastnote);
}
if (direction == 1) {
// down
nextnote = lastnote;
do {
nextnote = nextnote - 1;
if (nextnote < 0)
nextnote = 127;
if (notes[nextnote] != 0) {
playnote = nextnote;
if (playnote == lowest_note) {
nextoctave = octave - 1;
if (nextoctave < 0) {
nextoctave = number_octaves;
};
}
break;
}
} while (nextnote != lastnote);
}
if (direction == 2) {
// up/down
nextnote = lastnote;
if (moveup) {
do {
nextnote = (nextnote + 1) % 128;
if (notes[nextnote] != 0) {
playnote = nextnote;
if (playnote == highest_note) {
nextoctave = (octave + 1) % (number_octaves + 1);
if (nextoctave == 0) {
nextoctave = (number_octaves > 0 && notecount == 1)
? octave - 1
: octave;
moveup = 0;
}
}
break;
}
} while (nextnote != lastnote);
} else {
do {
nextnote = nextnote - 1;
if (nextnote < 0)
nextnote = 127;
if (notes[nextnote] != 0) {
playnote = nextnote;
if (playnote == lowest_note) {
nextoctave = octave - 1;
if (nextoctave < 0) {
nextoctave = (number_octaves > 0 && notecount == 1) ? 1 : 0;
moveup = 1;
};
}
break;
}
} while (nextnote != lastnote);
}
}
if (direction == 3) {
// rand
int rand = GenerateRandomNumber();
int randpos = ((rand < 0 ? -rand + 1 : rand) % (notecount));
playnote = notes_ord[randpos];
rand = GenerateRandomNumber();
nextoctave = ((rand < 0 ? -rand + 1 : rand) % (number_octaves + 1));
}
if (direction == 4) {
// order
playnote = notes_ord[order_pos];
if (order_pos == notecount - 1) {
nextoctave = (octave + 1) % (number_octaves + 1);
}
order_pos = (order_pos + 1) % notecount;
}
if (playnote > 0) {
outpitch = (playnote + inlet_transpose + 12 * octave - 64) << 21;
// LogTextMessage("Triggering #%i %i", playnote, outpitch);
outvelo = param_velocity > 0 ? param_velocity : (notes[playnote] << 20);
ngate = 1;
lastnote = playnote;
octave = nextoctave;
}
}
secondclock = secondclock == 0 ? 1 : 0;
}
ntrigclock = 1;
}
if (!(inlet_clock > 0) && ntrigclock) {
ntrigclock = 0;
}
// create gate pulse
if (ngate > 0) {
// check if gate is closed
if (gateval <= 0 || param_retrigger == 0) {
gateval = 1 << 30;
ngate = 0;
} else {
// retrigger
gateval = 0;
}
}
if (gateval > 0) {
int32_t t;
MTOF(-(param_notelength + inlet_notelength), t);
gateval -= t >> 3;
if (gateval <= 0)
outlet_gate = 0;
else
outlet_gate = 1;
} else
outlet_gate = 0;
outlet_pitch = outpitch;
outlet_velocity = outvelo;
disp_notes = notecount;
outlet_notes = notecount;
ignored = inlet_ignore;
int new_holding = sustainpedal | inlet_hold;
if (holding == 0 && new_holding > 0 && ignored > 0 && notecount == 0) {
dehold_lock = 1;
// restore last latch if no notes are pressed and ignore is on
order_pos = 0;
for (int i = 0; i < 128; i++) {
notes[i] = latched_notes[i];
notes_ord[i] = latched_notes[i + 128];
if (notes_ord[i] > 0) {
order_pos++;
}
if (notes[i] > 0) {
notecount++;
released_on_hold[i] = 1;
}
}
octave = 0;
moveup = 1;
lastnote = notecount > 0 ? notecount - 1 : 0;
secondclock = 0;
dehold_lock = 0;
}
if (holding == 1 && new_holding == 0) {
dehold_lock = 1;
int store_hold = notecount;
// here comes nasty copy&paste code from the midi section :(
for (int r = 0; r < 128; r++) {
// store last latched
if (store_hold > 0) {
latched_notes[r] = notes[r];
latched_notes[r + 128] = notes_ord[r];
}
if (released_on_hold[r] == 1) {
released_on_hold[r] = 0;
notes[r] = 0;
notecount = notecount > 0 ? notecount - 1 : 0;
// find in play order array and remove it
int orderpos = -1;
for (int i = 0; i < 128; i++) {
if (notes_ord[i] == r) {
orderpos = i;
break;
}
}
if (orderpos > -1) {
for (int i = 0; i < notecount - orderpos; i++) {
notes_ord[i + orderpos] = notes_ord[i + orderpos + 1];
}
notes_ord[notecount + 1] = 0;
}
}
}
for (int i = 0; i < 128; i++) {
if (notes[i] > 0) {
lowest_note = i;
break;
}
}
for (int i = 127; i >= 0; i--) {
if (notes[i] > 0) {
highest_note = i;
break;
}
}
dehold_lock = 0;
}
holding = new_holding;
disp_holding = holding;
int channel = (status & 0xf) + 1;
int cmd = (status & 0x70) >> 4;
if ((channel != attr_MIDIchannel && attr_MIDIchannel > 0) || dehold_lock) {
return;
}
bool resetStartNote = false;
// some midi devices send note on with velocity 0 instead of note off
if (cmd == 1 && data2 == 0) {
cmd = 0;
}
if (cmd == 1 && ignored == 0) {
// note on
// LogTextMessage("Pressed: %i (%i)", data1, data2);
if (notecount == 0) {
resetStartNote = true;
}
if (notes[data1] == 0) {
notecount++;
notes_ord[notecount - 1] = data1;
}
notes[data1] = data2;
released_on_hold[data1] = 0;
} else if (cmd == 0 && ignored == 0) {
// note off
// LogTextMessage("Release: %i", data1);
if (holding) {
released_on_hold[data1] = 1;
} else {
if (notes[data1] != 0) {
notecount = notecount > 0 ? notecount - 1 : 0;
notes[data1] = 0;
// find in play order array and remove it
int orderpos = -1;
for (int i = 0; i < 128; i++) {
if (notes_ord[i] == data1) {
orderpos = i;
break;
}
}
if (orderpos > -1) {
for (int i = 0; i < notecount - orderpos; i++) {
notes_ord[i + orderpos] = notes_ord[i + orderpos + 1];
}
notes_ord[notecount + 1] = 0;
}
}
}
} else if (cmd == 3 && data1 == 64 && ignored == 0) {
sustainpedal = data2 > 0 ? 1 : 0;
}
if (ignored == 0 && (cmd == 0 || cmd == 1)) {
// find highest and lowest note
for (int i = 0; i < 128; i++) {
if (notes[i] > 0) {
lowest_note = i;
break;
}
}
for (int i = 127; i >= 0; i--) {
if (notes[i] > 0) {
highest_note = i;
break;
}
}
if (resetStartNote) {
order_pos = 0;
octave = 0;
moveup = 1;
lastnote = 0;
}
/*// debug out
for (int i=0;i<notecount;i++) {
LogTextMessage("%i = #%i", i+1, notes_ord[i]);
} */
}