Skip to content
Permalink
e29558b103
Go to file
 
 
Cannot retrieve contributors at this time
1809 lines (1562 sloc) 44.5 KB
// libavr32
#include "print_funcs.h"
#include "flashc.h"
#include "delay.h"
#include "midi_common.h"
#include "arp.h"
#include "notes.h"
#include "timers.h"
#include "util.h"
#include "monome.h"
#include "ii.h"
#include "i2c.h"
#include "gpio.h"
#include "dac.h"
#include "init_common.h"
#include "conf_tc_irq.h"
#include "interrupts.h"
// this
#include "main.h"
#include "ansible_midi.h"
#define MONO_PITCH_CV 0
#define MONO_VELOCITY_CV 1
#define MONO_PRESSURE_CV 2
#define MONO_MOD_CV 3
#define MONO_BEND_CV 3
// NB: default to a -2 octave shift for pitch to help people with
// small keyboards keep the osc pitch knob closer to mid range.
// -3277 == N -24 on tt
#define DEFAULT_PITCH_SHIFT -3277
//------------------------------
//------ types
typedef struct {
u8 learning : 1;
u8 note_idx : 3;
u8 cc_idx : 3;
} fixed_learning_state_t;
typedef struct {
u8 key1 : 1;
u8 key1_consumed : 1;
u8 key2 : 1;
u8 key2_consumed : 1;
u8 front : 1;
u8 normaled : 1; // isn't this tracked elsewhere?
} key_state_t;
typedef enum {
eClockInternal,
eClockExternal,
eClockMidi
} clock_source;
//------------------------------
//------ prototypes
static void write_midi_standard(void);
static void write_midi_arp(void);
static uint16_t pitch_cv(u8 num, s16 offset);
static uint16_t velocity_cv(u8 vel);
static uint16_t cc_cv(u8 value);
static void restore_midi_standard(void);
static void set_voice_allocation(voicing_mode v);
static void set_voice_slew(voicing_mode v, s16 slew);
static void set_voice_tune(voicing_mode v, s16 shift);
static void reset(void);
static void poly_note_on(u8 ch, u8 num, u8 vel);
static void poly_note_off(u8 ch, u8 num, u8 vel);
static void poly_pitch_bend(u8 ch, u16 bend);
static void poly_sustain(u8 ch, u8 val);
static void poly_control_change(u8 ch, u8 num, u8 val);
static void poly_panic(void);
static void mono_note_on(u8 ch, u8 num, u8 vel);
static void mono_note_off(u8 ch, u8 num, u8 vel);
static void mono_pitch_bend(u8 ch, u16 bend);
static void mono_sustain(u8 ch, u8 val);
static void mono_channel_pressure(u8 ch, u8 val);
static void mono_control_change(u8 ch, u8 num, u8 val);
static void mono_panic(void);
static void mono_rt_tick(void);
static void mono_rt_start(void);
static void mono_rt_stop(void);
static void mono_rt_continue(void);
static void multi_tr_set(u8 ch);
static void multi_tr_clr(u8 ch);
static void multi_note_on(u8 ch, u8 num, u8 vel);
static void multi_note_off(u8 ch, u8 num, u8 vel);
static void multi_pitch_bend(u8 ch, u16 bend);
static void multi_sustain(u8 ch, u8 val);
static void multi_control_change(u8 ch, u8 num, u8 val);
static void fixed_write_mapping(const fixed_mapping_t *dest, const fixed_mapping_t *src);
static void fixed_start_learning(void);
static bool fixed_finalize_learning(bool cancel);
static void fixed_note_on(u8 ch, u8 num, u8 vel);
static void fixed_note_off(u8 ch, u8 num, u8 vel);
static void fixed_control_change(u8 ch, u8 num, u8 val);
static void restore_midi_arp(void);
static void arp_state_set_hold(bool hold);
static void arp_rebuild(chord_t *c);
static void arp_reset(void);
static void arp_next_style(void);
static void arp_note_on(u8 ch, u8 num, u8 vel);
static void arp_note_off(u8 ch, u8 num, u8 vel);
static void arp_pitch_bend(u8 ch, u16 bend);
static void arp_control_change(u8 ch, u8 num, u8 val);
static void arp_rt_tick(void);
static void arp_rt_start(void);
static void arp_rt_stop(void);
static void arp_rt_continue(void);
static void player_note_on(u8 ch, u8 num, u8 vel);
static void player_note_off(u8 ch, u8 num, u8 vel);
//-----------------------------
//----- globals
// step = 16384.0 / (10 octave * 12.0 semitones per octave)
// [int(n * step) for n in xrange(0,128)]
const u16 SEMI14[128] = {
0, 136, 273, 409, 546, 682, 819, 955, 1092, 1228, 1365, 1501, 1638,
1774, 1911, 2048, 2184, 2321, 2457, 2594, 2730, 2867, 3003, 3140,
3276, 3413, 3549, 3686, 3822, 3959, 4096, 4232, 4369, 4505, 4642,
4778, 4915, 5051, 5188, 5324, 5461, 5597, 5734, 5870, 6007, 6144,
6280, 6417, 6553, 6690, 6826, 6963, 7099, 7236, 7372, 7509, 7645,
7782, 7918, 8055, 8192, 8328, 8465, 8601, 8738, 8874, 9011, 9147,
9284, 9420, 9557, 9693, 9830, 9966, 10103, 10240, 10376, 10513, 10649,
10786, 10922, 11059, 11195, 11332, 11468, 11605, 11741, 11878, 12014,
12151, 12288, 12424, 12561, 12697, 12834, 12970, 13107, 13243, 13380,
13516, 13653, 13789, 13926, 14062, 14199, 14336, 14472, 14609, 14745,
14882, 15018, 15155, 15291, 15428, 15564, 15701, 15837, 15974, 16110,
16247, 16384, 16520, 16657, 16793, 16930, 17066, 17203, 17339
};
// step = 16384.0 / (10 octave * 12.0 semitones per octave)
// semi_per_octave = step * 12
// bend_step = semi_per_octave / 512.0
// [int(n * bend_step) for n in xrange(0,512)]
const u16 BEND1_14[512] = {
0, 3, 6, 9, 12, 16, 19, 22, 25, 28, 32, 35, 38, 41, 44, 48, 51, 54,
57, 60, 64, 67, 70, 73, 76, 80, 83, 86, 89, 92, 96, 99, 102, 105,
108, 112, 115, 118, 121, 124, 128, 131, 134, 137, 140, 144, 147,
150, 153, 156, 160, 163, 166, 169, 172, 176, 179, 182, 185, 188,
192, 195, 198, 201, 204, 208, 211, 214, 217, 220, 224, 227, 230,
233, 236, 240, 243, 246, 249, 252, 256, 259, 262, 265, 268, 272,
275, 278, 281, 284, 288, 291, 294, 297, 300, 304, 307, 310, 313,
316, 320, 323, 326, 329, 332, 336, 339, 342, 345, 348, 352, 355,
358, 361, 364, 368, 371, 374, 377, 380, 384, 387, 390, 393, 396,
400, 403, 406, 409, 412, 416, 419, 422, 425, 428, 432, 435, 438,
441, 444, 448, 451, 454, 457, 460, 464, 467, 470, 473, 476, 480,
483, 486, 489, 492, 496, 499, 502, 505, 508, 512, 515, 518, 521,
524, 528, 531, 534, 537, 540, 544, 547, 550, 553, 556, 560, 563,
566, 569, 572, 576, 579, 582, 585, 588, 592, 595, 598, 601, 604,
608, 611, 614, 617, 620, 624, 627, 630, 633, 636, 640, 643, 646,
649, 652, 656, 659, 662, 665, 668, 672, 675, 678, 681, 684, 688,
691, 694, 697, 700, 704, 707, 710, 713, 716, 720, 723, 726, 729,
732, 736, 739, 742, 745, 748, 752, 755, 758, 761, 764, 768, 771,
774, 777, 780, 784, 787, 790, 793, 796, 800, 803, 806, 809, 812,
816, 819, 822, 825, 828, 832, 835, 838, 841, 844, 848, 851, 854,
857, 860, 864, 867, 870, 873, 876, 880, 883, 886, 889, 892, 896,
899, 902, 905, 908, 912, 915, 918, 921, 924, 928, 931, 934, 937,
940, 944, 947, 950, 953, 956, 960, 963, 966, 969, 972, 976, 979,
982, 985, 988, 992, 995, 998, 1001, 1004, 1008, 1011, 1014, 1017,
1020, 1024, 1027, 1030, 1033, 1036, 1040, 1043, 1046, 1049, 1052,
1056, 1059, 1062, 1065, 1068, 1072, 1075, 1078, 1081, 1084, 1088,
1091, 1094, 1097, 1100, 1104, 1107, 1110, 1113, 1116, 1120, 1123,
1126, 1129, 1132, 1136, 1139, 1142, 1145, 1148, 1152, 1155, 1158,
1161, 1164, 1168, 1171, 1174, 1177, 1180, 1184, 1187, 1190, 1193,
1196, 1200, 1203, 1206, 1209, 1212, 1216, 1219, 1222, 1225, 1228,
1232, 1235, 1238, 1241, 1244, 1248, 1251, 1254, 1257, 1260, 1264,
1267, 1270, 1273, 1276, 1280, 1283, 1286, 1289, 1292, 1296, 1299,
1302, 1305, 1308, 1312, 1315, 1318, 1321, 1324, 1328, 1331, 1334,
1337, 1340, 1344, 1347, 1350, 1353, 1356, 1360, 1363, 1366, 1369,
1372, 1376, 1379, 1382, 1385, 1388, 1392, 1395, 1398, 1401, 1404,
1408, 1411, 1414, 1417, 1420, 1424, 1427, 1430, 1433, 1436, 1440,
1443, 1446, 1449, 1452, 1456, 1459, 1462, 1465, 1468, 1472, 1475,
1478, 1481, 1484, 1488, 1491, 1494, 1497, 1500, 1504, 1507, 1510,
1513, 1516, 1520, 1523, 1526, 1529, 1532, 1536, 1539, 1542, 1545,
1548, 1552, 1555, 1558, 1561, 1564, 1568, 1571, 1574, 1577, 1580,
1584, 1587, 1590, 1593, 1596, 1600, 1603, 1606, 1609, 1612, 1616,
1619, 1622, 1625, 1628, 1632, 1635
};
// copy of nvram state for editing
static midi_standard_state_t standard_state;
static midi_arp_state_t arp_state;
static midi_behavior_t active_behavior = {0};
static midi_behavior_t player_behavior = {0};
// standard mode working state
static fixed_learning_state_t fixed_learn;
static note_pool_t notes[4];
static voice_flags_t flags[4];
static voice_state_t voice_state;
// arp mode working state
static chord_t chord;
static u8 chord_held_notes;
static bool chord_should_reset;
static arp_seq_t sequences[2];
static arp_seq_t *active_seq;
static arp_seq_t *next_seq;
static arp_player_t player[4];
// shared state
static s16 pitch_offset[4];
static midi_clock_t midi_clock;
static key_state_t key_state;
static clock_source sync_source;
void set_mode_midi(void) {
switch(ansible_mode) {
case mMidiStandard:
print_dbg("\r\n> mode midi standard");
standard_state = f.midi_standard_state;
app_event_handlers[kEventKey] = &handler_StandardKey;
app_event_handlers[kEventTr] = &handler_StandardTr;
app_event_handlers[kEventTrNormal] = &handler_StandardTrNormal;
app_event_handlers[kEventMidiPacket] = &handler_StandardMidiPacket;
restore_midi_standard();
if (!leader_mode) init_i2c_slave(II_MID_ADDR);
process_ii = &ii_midi_standard;
update_leds(1);
break;
case mMidiArp:
print_dbg("\r\n> mode midi arp");
arp_state = f.midi_arp_state;
app_event_handlers[kEventKey] = &handler_ArpKey;
app_event_handlers[kEventTr] = &handler_ArpTr;
app_event_handlers[kEventTrNormal] = &handler_ArpTrNormal;
app_event_handlers[kEventMidiPacket] = &handler_ArpMidiPacket;
restore_midi_arp();
clock = &clock_midi_arp;
clock_set(arp_state.clock_period);
if (!leader_mode) init_i2c_slave(II_ARP_ADDR);
process_ii = &ii_midi_arp;
update_leds(2);
break;
default:
break;
}
if(connected == conMIDI) {
app_event_handlers[kEventFrontShort] = &handler_MidiFrontShort;
app_event_handlers[kEventFrontLong] = &handler_MidiFrontLong;
}
sync_source = eClockInternal;
midi_clock_init(&midi_clock);
midi_clock_set_div(&midi_clock, 4); // 16th notes (24 ppq / 4 == 6 pp 16th)
key_state.key1 = key_state.key2 = key_state.front = 0;
key_state.key1_consumed = key_state.key2_consumed = 0;
key_state.normaled = !gpio_get_pin_value(B10);
for (u8 i = 0; i < 4; i++) {
pitch_offset[i] = 0;
dac_set_value_noslew(i, 0);
}
dac_update_now();
}
void handler_MidiFrontShort(s32 data) {
if (key_state.key2) {
if (ansible_mode == mMidiStandard) {
// save voice mode configuration to flash
update_leds(0);
write_midi_standard();
print_dbg("\r\n standard: wrote midi config");
update_leds(1);
}
else {
// mMidiArp
update_leds(0);
write_midi_arp();
print_dbg("\r\n arp: wrote config");
update_leds(2);
}
key_state.key2_consumed = 1; // hide the release
}
else if (key_state.key1 && ansible_mode == mMidiStandard &&
standard_state.voicing == eVoiceFixed) {
key_state.key1_consumed = 1;
fixed_start_learning();
}
}
void handler_MidiFrontLong(s32 data) {
if (key_state.key2) {
// panic sequence to reset standard/arp mode to defaults
if (ansible_mode == mMidiStandard) {
default_midi_standard();
update_leds(0);
delay_ms(50);
update_leds(1);
delay_ms(50);
update_leds(0);
delay_ms(50);
set_mode(mMidiStandard);
print_dbg("\r\n standard: wrote default config");
}
else {
default_midi_arp();
update_leds(0);
delay_ms(50);
update_leds(2);
delay_ms(50);
update_leds(0);
delay_ms(50);
set_mode(mMidiArp);
print_dbg("\r\n arp: wrote default config");
}
key_state.key2_consumed = 1;
}
else {
// normal mode switch
if (ansible_mode == mMidiStandard)
set_mode(mMidiArp);
else
set_mode(mMidiStandard);
}
}
////////////////////////////////////////////////////////////////////////////////
///// common cv utilities
inline static uint16_t pitch_cv(u8 num, s16 offset) {
return SEMI14[num] + offset;
}
inline static uint16_t velocity_cv(u8 vel) {
// 128 << 7 == 16384; 14-bit val, shift to 12-bit on dac update
return vel << 7;
}
inline static uint16_t cc_cv(u8 value) {
return value << 7;
}
static void reset(void) {
// start from a clean slate
for (int i = 0; i < 4; i++) {
voice_flags_init(&(flags[i]));
notes_init(&(notes[i]));
pitch_offset[i] = 0;
dac_set_value_noslew(i, 0);
}
dac_update_now();
clr_tr(TR1);
clr_tr(TR2);
clr_tr(TR3);
clr_tr(TR4);
}
static void set_voice_allocation(voicing_mode v) {
reset();
// config behavior, clock, flags, etc.
switch (v) {
case eVoicePoly:
active_behavior.note_on = &poly_note_on;
active_behavior.note_off = &poly_note_off;
active_behavior.channel_pressure = NULL;
active_behavior.pitch_bend = &poly_pitch_bend;
active_behavior.control_change = &poly_control_change;
active_behavior.clock_tick = NULL;
active_behavior.seq_start = NULL;
active_behavior.seq_stop = NULL;
active_behavior.seq_continue = NULL;
active_behavior.panic = &poly_panic;
voice_slot_init(&voice_state, kVoiceAllocRotate, 4); // TODO: count configurable?
clock = &clock_null;
print_dbg("\r\n standard: voice poly");
break;
case eVoiceMono:
active_behavior.note_on = &mono_note_on;
active_behavior.note_off = &mono_note_off;
active_behavior.channel_pressure = &mono_channel_pressure;
active_behavior.pitch_bend = &mono_pitch_bend;
active_behavior.control_change = &mono_control_change;
active_behavior.clock_tick = &mono_rt_tick;
active_behavior.seq_start = &mono_rt_start;
active_behavior.seq_stop = &mono_rt_stop;
active_behavior.seq_continue = &mono_rt_continue;
active_behavior.panic = &mono_panic;
clock = &clock_null;
flags[0].legato = 1;
mono_pitch_bend(0, MIDI_BEND_ZERO);
print_dbg("\r\n standard: voice mono");
break;
case eVoiceMulti:
active_behavior.note_on = &multi_note_on;
active_behavior.note_off = &multi_note_off;
active_behavior.channel_pressure = NULL;
active_behavior.pitch_bend = &multi_pitch_bend;
active_behavior.control_change = &multi_control_change;
active_behavior.clock_tick = NULL;
active_behavior.seq_start = NULL;
active_behavior.seq_stop = NULL;
active_behavior.seq_continue = NULL;
active_behavior.panic = &reset;
clock = &clock_null;
flags[0].legato = flags[1].legato = flags[2].legato = flags[3].legato = 1;
print_dbg("\r\n standard: voice multi");
break;
case eVoiceFixed:
active_behavior.note_on = &fixed_note_on;
active_behavior.note_off = &fixed_note_off;
active_behavior.channel_pressure = NULL;
active_behavior.pitch_bend = NULL;
active_behavior.control_change = &fixed_control_change;
active_behavior.clock_tick = NULL;
active_behavior.seq_start = NULL;
active_behavior.seq_stop = NULL;
active_behavior.seq_continue = NULL;
active_behavior.panic = &reset;
clock = &clock_null;
print_dbg("\r\n standard: voice fixed");
break;
default:
print_dbg("\r\n standard: bad voice mode");
}
}
static void set_voice_slew(voicing_mode v, s16 slew) {
u8 i;
switch (v) {
case eVoicePoly:
case eVoiceMulti:
for (i = 0; i < 4; i++) {
dac_set_slew(i, slew);
}
break;
case eVoiceMono:
dac_set_slew(0, slew); // pitch
dac_set_slew(1, 0); // velocity
dac_set_slew(2, 5); // channel pressure
dac_set_slew(3, 5); // mod
break;
default:
for (i = 0; i < 4; i++) {
dac_set_slew(i, 0);
}
break;
}
}
static void set_voice_tune(voicing_mode v, s16 shift) {
u8 i;
switch (v) {
case eVoicePoly:
case eVoiceMulti:
for (i = 0; i < 4; i++) {
dac_set_off(i, shift);
}
break;
case eVoiceMono:
dac_set_off(0, shift); // pitch
dac_set_off(1, 0); // velocity
dac_set_off(2, 0); // channel pressure
dac_set_off(3, 0); // mod
break;
default:
for (i = 0; i < 4; i++) {
dac_set_off(i, 0);
}
break;
}
}
////////////////////////////////////////////////////////////////////////////////
///// handlers (standard)
static void restore_midi_standard(void) {
set_voice_allocation(standard_state.voicing);
set_voice_slew(standard_state.voicing, standard_state.slew);
set_voice_tune(standard_state.voicing, standard_state.shift);
clock_set(standard_state.clock_period);
}
void default_midi_standard(void) {
fixed_mapping_t m;
flashc_memset32((void*)&(f.midi_standard_state.clock_period), 100, 4, true);
flashc_memset8((void*)&(f.midi_standard_state.voicing), eVoicePoly, 1, true);
m.notes[0] = 60; // C4
m.notes[1] = 62; // D4
m.notes[2] = 64; // E4
m.notes[3] = 65; // F4
m.cc[0] = 16;
m.cc[1] = 17;
m.cc[2] = 18;
m.cc[3] = 19;
fixed_write_mapping(&(f.midi_standard_state.fixed), &m);
flashc_memset16((void*)&(f.midi_standard_state.shift), DEFAULT_PITCH_SHIFT, 2, true);
flashc_memset16((void*)&(f.midi_standard_state.slew), 0, 2, true);
}
void write_midi_standard(void) {
flashc_memset32((void*)&(f.midi_standard_state.clock_period),
standard_state.clock_period, 4, true);
flashc_memset8((void*)&(f.midi_standard_state.voicing),
standard_state.voicing, 1, true);
fixed_write_mapping(&(f.midi_standard_state.fixed), &standard_state.fixed);
flashc_memset16((void*)&(f.midi_standard_state.shift),
standard_state.shift, 2, true);
flashc_memset16((void*)&(f.midi_standard_state.slew),
standard_state.slew, 2, true);
}
void clock_midi_standard(uint8_t phase) {
if (phase)
set_tr(TR4);
else
clr_tr(TR4);
}
void ii_midi_standard(uint8_t *d, uint8_t l) {
s16 s;
if (l) {
switch (d[0]) {
case II_MID_SLEW:
s = sclip((int16_t)((d[1] << 8) + d[2]), 0, 20000);
// print_dbg("\r\nmid ii slew: ");
// print_dbg_ulong(s);
standard_state.slew = s;
set_voice_slew(standard_state.voicing, s);
break;
case II_MID_SHIFT:
s = (int16_t)((d[1] << 8) + d[2]);
// print_dbg("\r\nmid ii shift: ");
// print_dbg_hex(s);
standard_state.shift = s;
set_voice_tune(standard_state.voicing, s);
break;
default:
ii_ansible(d, l);
break;
}
}
}
void handler_StandardKey(s32 data) {
switch (data) {
case 0: // key 1 release
key_state.key1 = 0;
if (key_state.key1_consumed) {
key_state.key1_consumed = 0;
}
else {
if (fixed_learn.learning == 1) {
// cancel learning
fixed_finalize_learning(true);
}
else if (active_behavior.panic) {
// panic, all notes off
(*active_behavior.panic)();
}
}
break;
case 1:
key_state.key1 = 1;
break;
case 2: // key 2 release
key_state.key2 = 0;
if (key_state.key2_consumed) {
key_state.key2_consumed = 0;
}
else {
if (fixed_learn.learning == 1) {
// in learning mode; do nothing
}
else {
// switch voicing mode
standard_state.voicing++;
if (standard_state.voicing >= eVoiceMAX) {
standard_state.voicing = eVoicePoly;
}
set_voice_allocation(standard_state.voicing);
set_voice_slew(standard_state.voicing, standard_state.slew);
set_voice_tune(standard_state.voicing, standard_state.shift);
}
}
break;
case 3:
key_state.key2 = 1;
break;
}
}
void handler_StandardTr(s32 data) {
}
void handler_StandardTrNormal(s32 data) {
key_state.normaled = data;
}
void handler_StandardMidiPacket(s32 data) {
midi_packet_parse(&active_behavior, (u32)data);
}
////////////////////////////////////////////////////////////////////////////////
///// poly behavior (standard)
static void poly_note_on(u8 ch, u8 num, u8 vel) {
u8 slot = voice_slot_next(&voice_state);
if (voice_slot_active(&voice_state, slot)) {
// steal slot
multi_tr_clr(slot);
// FIXME: here we clear tr but then immediately reset it for note
// on so the envelop wont retrigger. need some way to queue or
// pause long enough to have tr retrigger.
voice_slot_release(&voice_state, slot);
}
voice_slot_activate(&voice_state, slot, num);
multi_note_on(slot, num, vel);
}
static void poly_note_off(u8 ch, u8 num, u8 vel) {
s8 slot = voice_slot_find(&voice_state, num);
if (slot != -1) {
if (!flags[slot].sustain) {
multi_tr_clr(slot);
voice_slot_release(&voice_state, slot);
}
}
}
static void poly_pitch_bend(u8 ch, u16 bend) {
// MAINT: assuming bend is always 14 bit...
if (bend == MIDI_BEND_ZERO) {
pitch_offset[0] = 0;
}
else if (bend > MIDI_BEND_ZERO) {
bend -= MIDI_BEND_ZERO;
pitch_offset[0] = BEND1_14[bend >> 4];
}
else {
bend = MIDI_BEND_ZERO - bend - 1;
pitch_offset[0] = -BEND1_14[bend >> 4];
}
for (u8 i = 0; i < voice_state.count; i++) {
if (voice_slot_active(&voice_state, i)) {
dac_set_value(i, pitch_cv(voice_slot_num(&voice_state, i),
pitch_offset[0]));
}
}
dac_update_now();
}
static void poly_sustain(u8 ch, u8 val) {
for (u8 slot = 0; slot < MAX_VOICE_COUNT; slot++) {
if (val < 64) {
// release active voices
if (voice_slot_active(&voice_state, slot)) {
multi_tr_clr(slot);
voice_slot_release(&voice_state, slot);
}
flags[slot].sustain = 0;
}
else {
flags[slot].sustain = 1;
}
}
}
static void poly_control_change(u8 ch, u8 num, u8 val) {
switch (num) {
case 64: // sustain pedal
poly_sustain(ch, val);
break;
}
}
static void poly_panic(void) {
voice_slot_init(&voice_state, voice_state.mode, voice_state.count);
reset();
}
////////////////////////////////////////////////////////////////////////////////
///// mono behavior (standard)
static void mono_note_on(u8 ch, u8 num, u8 vel) {
if (num > MIDI_NOTE_MAX)
return;
// keep track of held notes for legato and pitch bend
notes_hold(&notes[0], num, vel);
dac_set_value(MONO_PITCH_CV, pitch_cv(num, pitch_offset[0]));
dac_set_value_noslew(MONO_VELOCITY_CV, velocity_cv(vel));
dac_update_now();
set_tr(TR1);
}
static void mono_note_off(u8 ch, u8 num, u8 vel) {
const held_note_t *prior;
if (num > MIDI_NOTE_MAX)
return;
if (flags[0].sustain == 0) {
notes_release(&notes[0], num);
prior = notes_get(&notes[0], kNotePriorityLast);
if (prior) {
dac_set_value(MONO_PITCH_CV, pitch_cv(prior->num, pitch_offset[0]));
dac_set_value(MONO_VELOCITY_CV, velocity_cv(prior->vel));
dac_update_now();
}
else {
clr_tr(TR1);
}
}
}
static void mono_pitch_bend(u8 ch, u16 bend) {
// MAINT: assuming bend is always 14 bit...
if (bend == MIDI_BEND_ZERO) {
pitch_offset[0] = 0;
}
else if (bend > MIDI_BEND_ZERO) {
bend -= MIDI_BEND_ZERO;
pitch_offset[0] = BEND1_14[bend >> 4];
}
else {
bend = MIDI_BEND_ZERO - bend - 1;
pitch_offset[0] = -BEND1_14[bend >> 4];
}
// re-set pitch to pick up changed offset
const held_note_t *active = notes_get(&(notes[0]), kNotePriorityLast);
if (active) {
dac_set_value(MONO_PITCH_CV, pitch_cv(active->num, pitch_offset[0]));
dac_update_now();
}
}
static void mono_sustain(u8 ch, u8 val) {
if (val < 64) {
clr_tr(TR2);
flags[0].sustain = 0;
// release any held notes
notes_init(&notes[0]);
clr_tr(TR1);
}
else {
set_tr(TR2);
flags[0].sustain = 1;
}
}
static void mono_channel_pressure(u8 ch, u8 val) {
dac_set_value_noslew(MONO_PRESSURE_CV, cc_cv(val));
dac_update_now();
}
static void mono_generic(u8 ch, u8 val) {
(val < 64) ? clr_tr(TR3) : set_tr(TR3);
}
static void mono_control_change(u8 ch, u8 num, u8 val) {
switch (num) {
case 1: // mod wheel
dac_set_value_noslew(MONO_MOD_CV, cc_cv(val));
dac_update_now();
break;
case 64: // sustain pedal
mono_sustain(ch, val);
break;
case 80: // generic
mono_generic(ch, val);
default:
break;
}
}
static void mono_rt_tick(void) {
static u32 previous = 0;
u32 now, average;
if (key_state.normaled) {
// external sync on;
return;
}
now = time_now();
if (previous == 0) {
previous = now;
}
// smooth it? given all the polling tempo changes jump around
average = (now + previous) >> 1;
previous = now;
midi_clock_pulse(&midi_clock, average);
time_clear();
//print_dbg("\r\n mono_rt_tick: ");
//print_dbg_ulong(midi_clock.trigger);
//print_dbg(" p: ");
//print_dbg_ulong(midi_clock.pulse_period);
if (midi_clock.trigger) {
set_tr(TR4);
}
else {
clr_tr(TR4);
}
}
static void mono_rt_start(void) {
print_dbg("\r\n mono_rt_start()");
midi_clock_start(&midi_clock);
}
static void mono_rt_stop(void) {
print_dbg("\r\n mono_rt_stop()");
midi_clock_stop(&midi_clock);
clr_tr(TR4);
}
static void mono_rt_continue(void) {
print_dbg("\r\n mono_rt_continue()");
midi_clock_continue(&midi_clock);
}
static void mono_panic(void) {
midi_clock_stop(&midi_clock);
reset();
}
////////////////////////////////////////////////////////////////////////////////
///// multi behavior (standard)
inline static void multi_tr_set(u8 ch) {
switch (ch) {
case 0:
set_tr(TR1);
break;
case 1:
set_tr(TR2);
break;
case 2:
set_tr(TR3);
break;
case 3:
set_tr(TR4);
break;
}
}
inline static void multi_tr_clr(u8 ch) {
switch (ch) {
case 0:
clr_tr(TR1);
break;
case 1:
clr_tr(TR2);
break;
case 2:
clr_tr(TR3);
break;
case 3:
clr_tr(TR4);
break;
}
}
static void multi_note_on(u8 ch, u8 num, u8 vel) {
if (ch > 3 || num > MIDI_NOTE_MAX)
return;
notes_hold(&notes[ch], num, vel);
dac_set_value(ch, pitch_cv(num, pitch_offset[ch]));
dac_update_now();
multi_tr_set(ch);
}
static void multi_note_off(u8 ch, u8 num, u8 vel) {
const held_note_t *prior;
if (ch > 3 || num > MIDI_NOTE_MAX)
return;
if (flags[ch].sustain == 0) {
notes_release(&notes[ch], num);
if (flags[ch].legato) {
prior = notes_get(&notes[ch], kNotePriorityLast);
if (prior) {
dac_set_value(ch, pitch_cv(prior->num, pitch_offset[ch]));
dac_update_now();
}
else {
multi_tr_clr(ch);
}
}
else {
// legato is off
multi_tr_clr(ch);
}
}
}
static void multi_pitch_bend(u8 ch, u16 bend) {
if (ch > 3)
return;
// MAINT: assuming bend is always 14 bit...
if (bend == MIDI_BEND_ZERO) {
pitch_offset[ch] = 0;
}
else if (bend > MIDI_BEND_ZERO) {
bend -= MIDI_BEND_ZERO;
pitch_offset[ch] = BEND1_14[bend >> 4];
}
else {
bend = MIDI_BEND_ZERO - bend - 1;
pitch_offset[ch] = -BEND1_14[bend >> 4];
}
// re-set pitch to pick up changed offset
const held_note_t *active = notes_get(&(notes[ch]), kNotePriorityLast);
if (active) {
dac_set_value(ch, pitch_cv(active->num, pitch_offset[ch]));
dac_update_now();
}
}
static void multi_sustain(u8 ch, u8 val) {
if (ch > 3)
return;
if (val < 64) {
multi_tr_clr(ch);
flags[ch].sustain = 0;
if (flags[ch].legato) {
notes_init(&(notes[ch]));
}
}
else {
flags[ch].sustain = 1;
}
}
static void multi_control_change(u8 ch, u8 num, u8 val) {
if (ch > 3)
return;
switch (num) {
case 64: // sustain pedal
multi_sustain(ch, val);
break;
default:
break;
}
}
////////////////////////////////////////////////////////////////////////////////
///// fixed behavior (standard)
static void fixed_write_mapping(const fixed_mapping_t *dest, const fixed_mapping_t *src) {
for (u8 i = 0; i < 4; i++) {
flashc_memset8((void*)&(dest->notes[i]), src->notes[i], 1, true);
flashc_memset8((void*)&(dest->cc[i]), src->cc[i], 1, true);
}
}
static void fixed_start_learning(void) {
print_dbg("\r\n standard: start fixed mode learn");
fixed_learn.learning = 1;
fixed_learn.note_idx = 0;
fixed_learn.cc_idx = 0;
for (u8 i = 0; i < 4; i++) {
multi_tr_clr(i);
dac_set_value_noslew(i, 0);
}
dac_update_now();
}
static bool fixed_finalize_learning(bool cancel) {
if (cancel || (fixed_learn.note_idx == 4 && fixed_learn.cc_idx == 4)) {
print_dbg("\r\n standard: fixed learning complete");
fixed_learn.learning = 0;
// clear all tr and cv
for (u8 i = 0; i < 4; i++) {
multi_tr_clr(i);
dac_set_value_noslew(i, 0);
print_dbg("\r\n n: ");
print_dbg_ulong(standard_state.fixed.notes[i]);
print_dbg(" cc: ");
print_dbg_ulong(standard_state.fixed.cc[i]);
}
dac_update_now();
if (!cancel) {
fixed_write_mapping(&(f.midi_standard_state.fixed), &(standard_state.fixed));
}
return true;
}
// learning not finalized
return false;
}
static void fixed_note_on(u8 ch, u8 num, u8 vel) {
static bool initial_set = false;
if (fixed_learn.learning) {
if (fixed_learn.note_idx < 4) {
if (fixed_learn.note_idx == 0 && initial_set == false) {
initial_set = true;
standard_state.fixed.notes[fixed_learn.note_idx] = num;
multi_tr_set(fixed_learn.note_idx); // indicate mapping
}
// different note; advance to next mapping
if (num != standard_state.fixed.notes[fixed_learn.note_idx]) {
fixed_learn.note_idx++;
if (fixed_learn.note_idx < 4) {
standard_state.fixed.notes[fixed_learn.note_idx] = num;
multi_tr_set(fixed_learn.note_idx); // indicate mapping
if (fixed_learn.note_idx == 3) {
fixed_learn.note_idx++;
}
}
}
}
if (fixed_finalize_learning(false)) {
// prep for next learning pass
initial_set = false;
}
}
else {
for (u8 i = 0; i < 4; i++) {
if (standard_state.fixed.notes[i] == num) {
multi_tr_set(i);
// TODO: should these be gates or triggers?
break;
}
}
}
}
static void fixed_note_off(u8 ch, u8 num, u8 vel) {
if (!fixed_learn.learning) {
for (u8 i = 0; i < 4; i++) {
if (standard_state.fixed.notes[i] == num) {
multi_tr_clr(i);
break;
}
}
}
}
static void fixed_control_change(u8 ch, u8 num, u8 val) {
static bool initial_set = false;
if (fixed_learn.learning) {
if (fixed_learn.cc_idx < 4) {
// initial state; first num always sets first channel
if (fixed_learn.cc_idx == 0 && initial_set == false) {
initial_set = true;
standard_state.fixed.cc[fixed_learn.cc_idx] = num;
}
// different cc; advance to next mapping
if (num != standard_state.fixed.cc[fixed_learn.cc_idx]) {
fixed_learn.cc_idx++;
if (fixed_learn.cc_idx < 4) {
standard_state.fixed.cc[fixed_learn.cc_idx] = num;
if (fixed_learn.cc_idx == 3) {
// immediately end learning instead of waiting around for
// a different cc to be sent..
fixed_learn.cc_idx++;
}
}
}
// update outputs to provide feedback
if (fixed_learn.cc_idx < 4) {
dac_set_value_noslew(fixed_learn.cc_idx, cc_cv(val));
dac_update_now();
}
}
if (fixed_finalize_learning(false)) {
// prepare for next learning pass
initial_set = false;
}
}
else {
for (u8 i = 0; i < 4; i++) {
if (standard_state.fixed.cc[i] == num) {
dac_set_value_noslew(i, cc_cv(val));
dac_update_now();
break;
}
}
}
}
////////////////////////////////////////////////////////////////////////////////
void default_midi_arp() {
flashc_memset32((void*)&(f.midi_arp_state.clock_period), 100, 4, true);
flashc_memset8((void*)&(f.midi_arp_state.style), eStylePlayed, 1, true);
flashc_memset8((void*)&(f.midi_arp_state.hold), 0, 1, true);
for (u8 i = 0; i < 4; i++) {
flashc_memset8((void*)&(f.midi_arp_state.p[i].fill), 1, 1, true);
flashc_memset8((void*)&(f.midi_arp_state.p[i].division), i + 1, 1, true);
flashc_memset8((void*)&(f.midi_arp_state.p[i].rotation), 0, 1, true);
flashc_memset8((void*)&(f.midi_arp_state.p[i].gate), 0, 1, true);
flashc_memset8((void*)&(f.midi_arp_state.p[i].steps), 0, 1, true);
flashc_memset8((void*)&(f.midi_arp_state.p[i].offset), 12, 1, true);
flashc_memset16((void*)&(f.midi_arp_state.p[i].slew), 0, 2, true);
flashc_memset16((void*)&(f.midi_arp_state.p[i].shift), DEFAULT_PITCH_SHIFT, 2, true);
}
}
void write_midi_arp(void) {
arp_player_t *p;
flashc_memset32((void*)&(f.midi_arp_state.clock_period),
arp_state.clock_period, 4, true);
flashc_memset8((void*)&(f.midi_arp_state.style),
arp_state.style, 1, true);
flashc_memset8((void*)&(f.midi_arp_state.hold),
arp_state.hold, 1, true);
for (u8 i = 0; i < 4; i++) {
p = &(player[i]);
flashc_memset8((void*)&(f.midi_arp_state.p[i].fill),
arp_player_get_fill(p), 1, true);
flashc_memset8((void*)&(f.midi_arp_state.p[i].division),
arp_player_get_division(p), 1, true);
flashc_memset8((void*)&(f.midi_arp_state.p[i].rotation),
arp_player_get_rotation(p), 1, true);
flashc_memset8((void*)&(f.midi_arp_state.p[i].gate),
arp_player_get_gate_width(p), 1, true);
flashc_memset8((void*)&(f.midi_arp_state.p[i].steps),
arp_player_get_steps(p), 1, true);
flashc_memset8((void*)&(f.midi_arp_state.p[i].offset),
arp_player_get_offset(p), 1, true);
flashc_memset16((void*)&(f.midi_arp_state.p[i].slew),
dac_get_slew(i), 2, true);
flashc_memset16((void*)&(f.midi_arp_state.p[i].shift),
dac_get_off(i), 2, true);
}
}
static void arp_next_style(void) {
arp_state.style++;
if (arp_state.style > eStyleRandom) {
arp_state.style = eStylePlayed;
}
print_dbg("\r\n arp style: ");
print_dbg_ulong(arp_state.style);
arp_rebuild(&chord);
}
static bool arp_seq_switch_active(void) {
// TODO: better abstract this and move to arp.c
arp_seq_t *last_seq;
bool switched = false;
// disable interrupts
u8 irq_flags = irqs_pause();
if (next_seq->state == eSeqWaiting) {
next_seq->state = eSeqPlaying;
active_seq->state = eSeqFree;
last_seq = active_seq;
active_seq = next_seq;
next_seq = last_seq;
switched = true;
}
// enable interrupts
irqs_resume(irq_flags);
return switched;
}
static void arp_clock_pulse(uint8_t phase) {
if (phase) {
arp_seq_switch_active();
}
for (u8 i = 0; i < 4; i++) {
arp_player_pulse(&(player[i]), active_seq, &player_behavior, phase);
}
// NB: forcing a dac update so that when there is no slewing cv
// changes ~30us after the tr goes high. without this cv change is
// delayed ~1ms or more based on the update timer freq. that said
// doing this does result in cv arriving at the target before
// slew_ms since the first slew step is executed here...
//
// calling the timer update function is safe since this function is
// running at interrupt level as well.
dac_timer_update();
}
void clock_midi_arp(uint8_t phase) {
// internal clock timer callback, pulse clock if we are not being
// driven externally
if (sync_source == eClockInternal) {
arp_clock_pulse(phase);
}
}
void ii_midi_arp(uint8_t *d, uint8_t l) {
arp_player_t *p;
u8 i, v, p1, p2;
s16 s;
if (l) {
switch (d[0]) {
case II_ARP_STYLE:
p1 = uclip(d[1], eStylePlayed, eStyleRandom);
// print_dbg("\r\narp ii style: ");
// print_dbg_ulong(p1);
arp_state.style = p1;
arp_rebuild(&chord);
break;
case II_ARP_HOLD:
// print_dbg("\r\narp ii hold: ");
// print_dbg_ulong(d[1]);
arp_state_set_hold(d[1] > 0);
break;
case II_ARP_RPT:
v = uclip(d[1], 0, 4);
p1 = uclip(d[2], 0, 8);
s = sclip((int16_t)((d[3] << 8) + d[4]), -24, 24);
// print_dbg("\r\narp ii rpt: ");
// print_dbg_ulong(v);
// print_dbg(" ");
// print_dbg_ulong(p1);
// print_dbg(" ");
// print_dbg_hex(s);
if (v == 0) {
for (i = 0; i < 4; i++)
arp_player_set_steps(&(player[i]), p1);
arp_player_set_offset(&(player[i]), s);
}
else {
arp_player_set_steps(&(player[v-1]), p1);
arp_player_set_offset(&(player[v-1]), s);
}
break;
case II_ARP_GATE:
v = uclip(d[1], 0, 4);
p1 = uclip(d[2], 0, 127);
// print_dbg("\r\narp ii gate: ");
// print_dbg_ulong(v);
// print_dbg(" ");
// print_dbg_ulong(p1);
// FIXME: the gate width input range is 0-127, should tt range
// be non-midi like say 0-100?
if (v == 0) {
for (i = 0; i < 4; i++)
arp_player_set_gate_width(&(player[i]), p1);
}
else {
arp_player_set_gate_width(&(player[v-1]), p1);
}
break;
case II_ARP_DIV:
v = uclip(d[1], 0, 4);
p1 = uclip(d[2], 1, 32); // NB: 32 is maximum for euclidean tables
// print_dbg("\r\narp ii div: ");
// print_dbg_ulong(v);
// print_dbg(" ");
// print_dbg_ulong(p1);
if (v == 0) {
for (i = 0; i < 4; i++)
arp_player_set_division(&(player[i]), p1, &player_behavior);
}
else {
arp_player_set_division(&(player[v-1]), p1, &player_behavior);
}
break;
case II_ARP_FILL:
v = uclip(d[1], 0, 4);
p1 = uclip(d[2], 0, 32); // NB: 32 is maximum for euclidean tables
// print_dbg("\r\narp ii fill: ");
// print_dbg_ulong(v);
// print_dbg(" ");
// print_dbg_ulong(p1);
if (v == 0) {
for (i = 0; i < 4; i++)
arp_player_set_fill(&(player[i]), p1);
}
else {
arp_player_set_fill(&(player[v-1]), p1);
}
break;
case II_ARP_ROT:
v = uclip(d[1], 0, 4);
s = sclip((int16_t)((d[2] << 8) + d[3]), -32, 32);
// print_dbg("\r\narp ii rot: ");
// print_dbg_ulong(v);
// print_dbg(" ");
// print_dbg_hex(s);
if (v == 0) {
for (i = 0; i < 4; i++)
arp_player_set_rotation(&(player[i]), s);
}
else {
arp_player_set_rotation(&(player[v-1]), s);
}
break;
case II_ARP_ER:
v = uclip(d[1], 0, 4);
p1 = uclip(d[2], 0, 32); // NB: 32 is maximum for euclidean tables
p2 = uclip(d[3], 1, 32);
s = sclip((int16_t)((d[4] << 8) + d[5]), -32, 32);
// print_dbg("\r\narp ii er: ");
// print_dbg_ulong(v);
// print_dbg(" ");
// print_dbg_ulong(p1);
// print_dbg(" ");
// print_dbg_ulong(p2);
// print_dbg(" ");
// print_dbg_hex(s);
if (v == 0) {
for (i = 0; i < 4; i++) {
p = &(player[i]);
arp_player_set_division(p, p2, &player_behavior);
arp_player_set_fill(p, p1);
arp_player_set_rotation(p, s);
}
}
else {
p = &(player[v-1]);
arp_player_set_division(p, p2, &player_behavior);
arp_player_set_fill(p, p1);
arp_player_set_rotation(p, s);
}
break;
case II_ARP_SLEW:
v = uclip(d[1], 0, 4);
s = sclip((int16_t)((d[2] << 8) + d[3]), 0, 20000);
// print_dbg("\r\narp ii slew: ");
// print_dbg_ulong(v);
// print_dbg(" ");
// print_dbg_ulong(s);
if (v == 0) {
for (i = 0; i < 4; i++)
dac_set_slew(i, s);
}
else {
dac_set_slew(v-1, s);
}
break;
case II_ARP_RESET:
v = uclip(d[1], 0, 4);
// print_dbg("\r\narp ii reset: ");
// print_dbg_ulong(v);
if (v == 0) {
for (i = 0; i < 4; i++)
arp_player_reset(&(player[i]), &player_behavior);
}
else {
arp_player_reset(&(player[v-1]), &player_behavior);
}
break;
case II_ARP_SHIFT:
v = uclip(d[1], 0, 4);
s = (int16_t)((d[2] << 8) + d[3]);
// print_dbg("\r\narp ii shift: ");
// print_dbg_ulong(v);
// print_dbg(" ");
// print_dbg_hex(s);
if (v == 0) {
for (i = 0; i < 4; i++)
dac_set_off(i, s);
}
else {
dac_set_off(v-1, s);
}
break;
default:
ii_ansible(d, l);
break;
}
}
}
void handler_ArpKey(s32 data) {
static bool tapped = false;
u32 now;
switch (data) {
case 0:
// key 1 release
key_state.key1 = 0;
break;
case 1:
// key 1 press: tap tempo / force internal clock or toggle hold
key_state.key1 = 1;
if (key_state.key2 == 1) {
arp_state_set_hold(!arp_state.hold);
key_state.key2 = 0; // goofy; use this to signal to case 2 that style should change
}
else {
if (tapped) {
if (sync_source == eClockMidi) {
sync_source = eClockInternal;
}
tapped = false;
now = time_now();
now = uclip(now >> 1, 23, 1000); // range in ms
print_dbg("\r\n arp tap: ");
print_dbg_ulong(now);
arp_state.clock_period = now;
clock_set_tr(now, 0);
}
else {
tapped = true;
time_clear();
}
}
break;
case 2:
// key 2 release
if (key_state.key2_consumed) {
key_state.key2_consumed = 0;
}
else {
if (key_state.key1 == 0 && key_state.key2 == 1) {
// arp mode swetch on release if not toggling hold mode
arp_next_style();
}
}
key_state.key2 = 0;
break;
case 3:
// key 2 press
key_state.key2 = 1;
break;
}
}
void handler_ArpTr(s32 data) {
u32 now;
switch (data) {
case 0:
// tr 1 lo
arp_clock_pulse(0);
break;
case 1:
// tr 1 hi; sync internal clock timer
now = time_now();
time_clear();
now = now >> 1; // high/low phase
// TODO: clip now to low/high bounds?
clock_set_tr(now, 0);
arp_clock_pulse(1);
break;
case 2:
// tr 2 lo; nothing
break;
case 3:
// tr 2 hi; reset
for (u8 i = 0; i < 4; i++) {
arp_player_reset(&player[i], &player_behavior);
}
break;
}
}
void handler_ArpTrNormal(s32 data) {
print_dbg("\r\n> arp tr normal ");
print_dbg_ulong(data);
switch (data) {
case 0:
key_state.normaled = 0;
sync_source = eClockInternal;
break;
case 1:
key_state.normaled = 1;
sync_source = eClockExternal;
break;
}
}
void handler_ArpMidiPacket(s32 data) {
midi_packet_parse(&active_behavior, (u32)data);
}
void restore_midi_arp(void) {
arp_player_t *p;
chord_init(&chord);
chord_held_notes = 0;
chord_should_reset = false;
// ping pong
active_seq = &sequences[0];
next_seq = &sequences[1];
arp_seq_init(active_seq);
arp_seq_init(next_seq);
// ensure style matches stored config
arp_seq_build(active_seq, arp_state.style, &chord, &(notes[0]));
arp_seq_build(next_seq, arp_state.style, &chord, &(notes[0]));
for (u8 i = 0; i < 4; i++) {
p = &(player[i]);
arp_player_init(p, i, arp_state.p[i].division);
arp_player_set_gate_width(p, arp_state.p[i].gate);
arp_player_set_steps(p, arp_state.p[i].steps);
arp_player_set_offset(p, arp_state.p[i].offset);
arp_player_set_fill(p, arp_state.p[i].fill);
dac_set_off(i, arp_state.p[i].shift);
dac_set_slew(i, arp_state.p[i].slew);
}
active_behavior.note_on = &arp_note_on;
active_behavior.note_off = &arp_note_off;
active_behavior.channel_pressure = NULL;
active_behavior.pitch_bend = &arp_pitch_bend;
active_behavior.control_change = &arp_control_change;
active_behavior.clock_tick = &arp_rt_tick;
active_behavior.seq_start = &arp_rt_start;
active_behavior.seq_stop = &arp_rt_stop;
active_behavior.seq_continue = &arp_rt_continue;
active_behavior.panic = NULL;
player_behavior.note_on = &player_note_on;
player_behavior.note_off = &player_note_off;
player_behavior.channel_pressure = NULL;
player_behavior.pitch_bend = NULL;
player_behavior.control_change = NULL;
player_behavior.clock_tick = NULL;
player_behavior.seq_start = NULL;
player_behavior.seq_stop = NULL;
player_behavior.seq_continue = NULL;
player_behavior.panic = NULL;
}
static void arp_state_set_hold(bool hold) {
if (hold != arp_state.hold) {
arp_state.hold = hold;
print_dbg("\r\n arp hold: ");
print_dbg_ulong(arp_state.hold);
if (arp_state.hold) {
// entering hold mode, preserve chord
chord_held_notes = chord.note_count;
}
else {
// existing hold mode, reset arp
arp_reset();
}
}
}
static void arp_rebuild(chord_t *c) {
arp_seq_state current_state = arp_seq_get_state(next_seq);
if (current_state == eSeqFree || current_state == eSeqWaiting) {
arp_seq_set_state(next_seq, eSeqBuilding); // TODO: check return
arp_seq_build(next_seq, arp_state.style, &chord, &(notes[0]));
arp_seq_set_state(next_seq, eSeqWaiting);
}
else {
print_dbg("\r\n > arp: next seq busy, skipping build");
}
}
static void arp_reset(void) {
// used when exiting held mode
chord_should_reset = false;
chord_init(&chord);
notes_init(&(notes[0]));
arp_rebuild(&chord);
}
static void arp_note_on(u8 ch, u8 num, u8 vel) {
if (arp_state.hold) {
if (chord_should_reset) {
//print_dbg("\r\n > arp: hold chord resetting");
chord_should_reset = false;
chord_init(&chord);
notes_init(&(notes[0]));
}
chord_held_notes++;
//print_dbg("\r\n > arp: chord held: ");
//print_dbg_ulong(chord_held_notes);
}
chord_note_add(&chord, num, vel);
notes_hold(&(notes[0]), num, vel);
arp_rebuild(&chord);
}
static void arp_note_off(u8 ch, u8 num, u8 vel) {
if (arp_state.hold && chord_contains(&chord, num)) {
chord_held_notes--;
if (chord_held_notes == 0) {
//print_dbg("\r\n > arp chord should reset");
chord_should_reset = true;
}
}
else {
chord_note_release(&chord, num);
notes_release(&(notes[0]), num);
arp_rebuild(&chord);
}
}
static void arp_pitch_bend(u8 ch, u16 bend) {
}
static void arp_control_change(u8 ch, u8 num, u8 val) {
u16 period, t;
u8 i;
switch (num) {
case 16:
// clock mod
if (sync_source == eClockInternal) {
// clock speed; 1000ms - 23ms (same range as ww)
period = 25000 / ((val << 3) + 25);
//print_dbg("\r\n arp clock ms: ");
//print_dbg_ulong(period);
clock_set(period);
}
break;
case 17:
// gate mod
//print_dbg("\r\n arp gates: ");
for (i = 0; i < 4; i++) {
t = arp_player_set_gate_width(&player[i], val);
//print_dbg_ulong(t);
//print_dbg(" ");
}
break;
case 18:
// division mod
//print_dbg("\r\n arp divs: 1 ");
for (i = 1; i < 4; i++) {
t = (i + 1) + ((val >> 5) * (i + 1));
//print_dbg_ulong(t);
//print_dbg(" ");
arp_player_set_division(&player[i], t, &player_behavior);
}
}
}
static void arp_rt_tick(void) {
u32 now;
if (sync_source != eClockMidi)
return;
now = time_now();
time_clear();
midi_clock_pulse(&midi_clock, now);
if (midi_clock.pulse_count == 0) {
arp_clock_pulse(1);
}
else if (midi_clock.pulse_count == (midi_clock.pulse_div_count >> 1)) {
arp_clock_pulse(0);
}
}
static void arp_rt_start(void) {
if (sync_source != eClockExternal) {
sync_source = eClockMidi;
midi_clock_start(&midi_clock);
}
}
static void arp_rt_stop(void) {
if (sync_source == eClockExternal) {
midi_clock_stop(&midi_clock);
for (u8 i = 0; i < 4; i++) {
arp_player_reset(&player[i], &player_behavior);
}
}
}
static void arp_rt_continue(void) {
// some devices appear to only send continue and stop messages, not
// start so continue messages will switch the sync to midi as well
if (sync_source != eClockExternal) {
sync_source = eClockMidi;
midi_clock_continue(&midi_clock);
}
}
static void player_note_on(u8 ch, u8 num, u8 vel) {
if (ch > 3 || num > MIDI_NOTE_MAX)
return;
/*
if (ch == 0) {
print_dbg("\r\n >> p note on: ");
print_dbg_ulong(num);
}
*/
dac_set_value(ch, SEMI14[num]);
multi_tr_set(ch);
}
static void player_note_off(u8 ch, u8 num, u8 vel) {
if (ch > 3 || num > MIDI_NOTE_MAX)
return;
multi_tr_clr(ch);
}