diff --git a/src/ansible_arc.c b/src/ansible_arc.c index a5db96f..e538f13 100644 --- a/src/ansible_arc.c +++ b/src/ansible_arc.c @@ -79,7 +79,7 @@ void set_mode_arc(void) { clock = &clock_null; // clock = &clock_levels; // clock_set(f.levels_state.clock_period); - init_i2c_slave(II_LV_ADDR); + if (!leader_mode) init_i2c_slave(II_LV_ADDR); process_ii = &ii_levels; resume_levels(); update_leds(1); @@ -94,7 +94,7 @@ void set_mode_arc(void) { clock = &clock_cycles; // 24 clock_set(DAC_RATE_CV << 3); - init_i2c_slave(II_CY_ADDR); + if (!leader_mode) init_i2c_slave(II_CY_ADDR); process_ii = &ii_cycles; resume_cycles(); update_leds(2); diff --git a/src/ansible_grid.c b/src/ansible_grid.c index 7f9ec7b..4157570 100644 --- a/src/ansible_grid.c +++ b/src/ansible_grid.c @@ -30,6 +30,12 @@ bool preset_mode; uint8_t preset; +static void preset_mode_exit(void); + +bool follower_select; +bool mod_follower; +uint8_t follower; +bool kriaAltModeBlink; // flag gets flipped for the blinking u8 grid_varibrightness = 16; u8 key_count = 0; @@ -109,6 +115,7 @@ softTimer_t repeatTimer[4] = { static void grid_keytimer_kria(uint8_t held_key); static void kria_set_note(uint8_t trackNum); +static void preset_mode_handle_key(uint8_t x, uint8_t y, uint8_t z, uint8_t* glyph); static kria_modes_t ii_kr_mode_for_cmd(uint8_t cmd); static uint8_t ii_kr_cmd_for_mode(kria_modes_t mode); @@ -138,7 +145,7 @@ void set_mode_grid() { app_event_handlers[kEventMonomeRefresh] = &handler_KriaRefresh; clock = &clock_kria; clock_set(clock_period); - init_i2c_slave(II_KR_ADDR); + if (!leader_mode) init_i2c_slave(II_KR_ADDR); process_ii = &ii_kria; resume_kria(); update_leds(1); @@ -152,7 +159,7 @@ void set_mode_grid() { app_event_handlers[kEventMonomeRefresh] = &handler_MPRefresh; clock = &clock_mp; clock_set(clock_period); - init_i2c_slave(II_MP_ADDR); + if (!leader_mode) init_i2c_slave(II_MP_ADDR); process_ii = &ii_mp; resume_mp(); update_leds(2); @@ -166,7 +173,7 @@ void set_mode_grid() { app_event_handlers[kEventMonomeRefresh] = &handler_ESRefresh; clock = &clock_null; clock_set(clock_period); - init_i2c_slave(ES); + if (!leader_mode) init_i2c_slave(ES); process_ii = &ii_es; resume_es(); update_leds(3); @@ -184,11 +191,15 @@ void set_mode_grid() { flashc_memset32((void*)&(f.state.grid_mode), ansible_mode, 4, true); } +static void preset_mode_exit(void) { + // print_dbg("\r\n> PRESET EXIT"); + preset_mode = false; + flashc_memcpy((void*)f.state.followers, followers, sizeof(followers), true); +} void handler_GridFrontShort(s32 data) { if(preset_mode) { - // print_dbg("\r\n> PRESET EXIT"); - preset_mode = false; + preset_mode_exit(); switch (ansible_mode) { case mGridKria: @@ -241,31 +252,56 @@ void refresh_preset(void) { memset(monomeLedBuffer,0,128); - for(i1=0;i1<128;i1++) - monomeLedBuffer[i1] = 0; + if (!follower_select) { + monomeLedBuffer[preset * 16] = 11; + } - monomeLedBuffer[preset * 16] = 11; + if (follower_select) { + for (uint8_t i = 0; i < 4; i++) { + monomeLedBuffer[R7 + i] = (followers[follower].track_en & (1 << i)) ? L1 : L0; + } + } + for (uint8_t i = 0; i < I2C_FOLLOWER_COUNT; i++) { + if (follower_select) { + monomeLedBuffer[5 + (2 + i)*16] = i == follower ? L1 : L0; + } + else { + monomeLedBuffer[5 + (2 + i)*16] = followers[i].active ? L1 : L0; + } + } + monomeLedBuffer[5 + R7] = mod_follower ? L1 : L0; - switch(ansible_mode) { - case mGridMP: - for(i1=0;i1<8;i1++) - for(i2=0;i2<8;i2++) - if(m.glyph[i1] & (1< 1) { + memset(monomeLedBuffer + 13, L0, followers[follower].mode_ct); + monomeLedBuffer[13 + followers[follower].active_mode] = L1; + } + } + else { + switch(ansible_mode) { + case mGridMP: + for(i1=0;i1<8;i1++) + for(i2=0;i2<8;i2++) + if(m.glyph[i1] & (1<tmul[mTr]) / (f32)384.0; f32 unscaled = (track->dur[pos[trackNum][mDur]]+1) * (track->dur_mul<<2); dur[trackNum] = (u16)(unscaled * clock_scale); + aux_param[0][trackNum] = (int)unscaled; } if(kria_next_step(trackNum, mOct)) { oct[trackNum] = sum_clip(track->octshift, track->oct[pos[trackNum][mOct]], 5); @@ -907,13 +936,11 @@ void clock_kria_note(kria_track* track, uint8_t trackNum) { static void kria_set_note(uint8_t trackNum) { u8 noteInScale = (note[trackNum] + alt_note[trackNum]) % 7; // combine both note params u8 octaveBump = (note[trackNum] + alt_note[trackNum]) / 7; // if it wrapped around the octave, bump it - dac_set_value( + set_cv_note( trackNum, - tuning_table[trackNum][ - (int)cur_scale[noteInScale] + - scale_adj[noteInScale] + - (int)((oct[trackNum]+octaveBump) * 12) - ]); + (int)cur_scale[noteInScale] + + scale_adj[noteInScale] + + (int)((oct[trackNum]+octaveBump) * 12)); } void clock_kria_track( uint8_t trackNum ) { @@ -940,7 +967,7 @@ void clock_kria_track( uint8_t trackNum ) { if(trNextStep && isTrigger) { if( !kria_mutes[trackNum] ) { - dac_set_slew( trackNum, glide[trackNum] * 20 ); + set_cv_slew( trackNum, glide[trackNum] * 20 ); activeRpt[trackNum] = rpt[trackNum]; @@ -1569,6 +1596,64 @@ static void kria_set_tmul(uint8_t track, kria_modes_t mode, uint8_t new_tmul) { } } + +static void preset_mode_handle_key(u8 x, u8 y, u8 z, u8* glyph) { + if (z) { + if (x == 5 && y == 7) { + if (follower_select) { + follower_select = false; + } else { + mod_follower = true; + } + } + if (follower_select) { + if (y == 0) { + if (x <= 6) { + followers[follower].oct = x - 3; + follower_change_octave(&followers[follower], followers[follower].oct); + } + if (followers[follower].mode_ct > 1 + && x >= 13 + && x <= (13 + followers[follower].mode_ct)) { + follower_change_mode(&followers[follower], x - 13); + } + } + if (y >= 2 && y <= 5) { + if (x == 5) { + follower = y - 2; + } + } + if (y == 7) { + if (x <= 3) { + followers[follower].track_en ^= 1 << x; + } + } + } + else { + if (x > 7) { + glyph[y] ^= 1<<(x-8); + } + if (x == 5 && y >= 2 && y <= 5) { + if (mod_follower) { + follower = y - 2; + follower_select = true; + } + else { + toggle_follower(y - 2); + } + } + } + + monomeFrameDirty++; + } + else { + if (x == 5 && y == 7) { + mod_follower = false; + monomeFrameDirty++; + } + } +} + void handler_KriaGridKey(s32 data) { u8 x, y, z, index, i1, found; @@ -1600,7 +1685,7 @@ void handler_KriaGridKey(s32 data) { if(key_times[index] > 0) { // PRESET MODE FAST PRESS DETECT if(preset_mode == 1) { - if(x == 0) { + if(x == 0 && !follower_select) { if(y != preset) { preset = y; @@ -1616,7 +1701,7 @@ void handler_KriaGridKey(s32 data) { init_kria(); resume_kria(); - preset_mode = false; + preset_mode_exit(); grid_refresh = &refresh_kria; // print_dbg("\r\npreset RECALL:"); @@ -1658,12 +1743,7 @@ void handler_KriaGridKey(s32 data) { // PRESET SCREEN if(preset_mode) { - // glyph magic - if(z && x > 7) { - k.glyph[y] ^= 1<<(x-8); - - monomeFrameDirty++; - } + preset_mode_handle_key(x, y, z, k.glyph); } else if(view_clock) { if(z) { @@ -3345,15 +3425,7 @@ void resume_mp() { else clock = &clock_mp; - dac_set_slew(0,0); - dac_set_slew(1,0); - dac_set_slew(2,0); - dac_set_slew(3,0); - - clr_tr(TR1); - clr_tr(TR2); - clr_tr(TR3); - clr_tr(TR4); + reset_outputs(); monomeFrameDirty++; } @@ -3564,7 +3636,7 @@ void mp_note_on(uint8_t n) { if(mp_clock_count < 1) { mp_clock_count++; note_now[0] = n; - dac_set_value(0, tuning_table[0][(int)cur_scale[7-n] + scale_adj[7-n]]); + set_cv_note(0, (int)cur_scale[7-n] + scale_adj[7-n]); set_tr(TR1); } break; @@ -3573,7 +3645,7 @@ void mp_note_on(uint8_t n) { mp_clock_count++; w = get_note_slot(2); note_now[w] = n; - dac_set_value(w, tuning_table[w][(int)cur_scale[7-n] + scale_adj[7-n]]); + set_cv_note(w, (int)cur_scale[7-n] + scale_adj[7-n]); set_tr(TR1 + w); } break; @@ -3582,7 +3654,7 @@ void mp_note_on(uint8_t n) { mp_clock_count++; w = get_note_slot(4); note_now[w] = n; - dac_set_value(w, tuning_table[w][(int)cur_scale[7-n] + scale_adj[7-n]]); + set_cv_note(w, (int)cur_scale[7-n] + scale_adj[7-n]); set_tr(TR1 + w); } break; @@ -3740,7 +3812,7 @@ void handler_MPGridKey(s32 data) { // FAST PRESS if(key_times[index] > 0) { if(preset_mode) { - if(x == 0) { + if(x == 0 && !follower_select) { if(y != preset) { preset = y; @@ -3755,7 +3827,7 @@ void handler_MPGridKey(s32 data) { flashc_memset8((void*)&(f.mp_state.preset), preset, 1, true); init_mp(); - preset_mode = false; + preset_mode_exit(); grid_refresh = &refresh_mp; // print_dbg("\r\npreset RECALL:"); @@ -3774,11 +3846,7 @@ void handler_MPGridKey(s32 data) { // PRESET SCREEN if(preset_mode) { - // draw glyph - if(z && x>7) - m.glyph[y] ^= 1<<(x-8); - - monomeFrameDirty++; + preset_mode_handle_key(x, y, z, m.glyph); } else if(view_clock) { if(z) { @@ -4402,7 +4470,7 @@ static void es_note_on(s8 x, s8 y, u8 from_pattern, u16 timer, u8 voices) { note_index = 0; else if (note_index > 119) note_index = 119; - dac_set_value_noslew(note, tuning_table[note][note_index]); + set_cv_note(note, note_index); dac_update_now(); set_tr(TR1 + note); @@ -4783,9 +4851,8 @@ void resume_es(void) { clock_external = !gpio_get_pin_value(B10); clock = &clock_null; + reset_outputs(); for (u8 i = 0; i < 4; i++) { - dac_set_slew(i, 0); - clr_tr(TR1 + i); es_notes[i].active = 0; } @@ -4901,18 +4968,19 @@ void handler_ESGridKey(s32 data) { // preset screen if (preset_mode) { - if (!z && x == 0) { - if (y != preset) { - preset = y; - for (u8 i = 0; i < GRID_PRESETS; i++) - e.glyph[i] = f.es_state.e[preset].glyph[i]; - } else { - // flash read - es_load_preset(); - } - } else if (z && x > 7) { - e.glyph[y] ^= 1 << (x - 8); - } + preset_mode_handle_key(x, y, z, e.glyph); + if (z == 0) { + if (x == 0 && !follower_select) { + if (y != preset) { + preset = y; + for (u8 i = 0; i < GRID_PRESETS; i++) + e.glyph[i] = f.es_state.e[preset].glyph[i]; + } else { + // flash read + es_load_preset(); + } + } + } monomeFrameDirty++; return; diff --git a/src/ansible_ii_leader.c b/src/ansible_ii_leader.c new file mode 100644 index 0000000..7375462 --- /dev/null +++ b/src/ansible_ii_leader.c @@ -0,0 +1,398 @@ +#include "dac.h" +#include "i2c.h" +#include "ii.h" +#include "ansible_ii_leader.h" +#include "main.h" +#include "music.h" + +static void ii_init_jf(i2c_follower_t* follower, uint8_t track, uint8_t state) { + uint8_t d[3] = { 0 }; + + if (!state) + { + // clear all triggers to avoid hanging notes in SUSTAIN + d[0] = JF_TR; + d[1] = 0; + d[2] = 0; + i2c_master_tx(follower->addr, d, 3); + } + + d[0] = JF_MODE; + d[1] = state; + i2c_master_tx(follower->addr, d, 2); +} + +static void ii_tr_jf(i2c_follower_t* follower, uint8_t track, uint8_t state) { + uint8_t d[6] = { 0 }; + uint8_t l = 0; + uint16_t dac_value = dac_get_value(track); + if (state) { + // map from 1-320 range of duration param to V 2 - V 5 for velocity control + uint16_t vel = aux_param[0][track] * 41 + 3264; + switch (follower->active_mode) { + case 0: { // polyphonically allocated + d[0] = JF_NOTE; + d[1] = dac_value >> 8; + d[2] = dac_value & 0xFF; + d[3] = vel >> 8; + d[4] = vel & 0xFF; + l = 5; + break; + } + case 1: { // tracks to first 4 voices + d[0] = JF_VOX; + d[1] = track + 1; + d[2] = dac_value >> 8; + d[3] = dac_value & 0xFF; + d[4] = vel >> 8; + d[5] = vel & 0xFF; + l = 6; + break; + } + case 2: { // envelopes + d[0] = JF_VTR; + d[1] = track + 1; + d[2] = vel >> 8; + d[3] = vel & 0xFF; + l = 4; + break; + } + default: { + return; + } + } + } + else { + if (follower->active_mode == 0) { + d[0] = JF_NOTE; + d[1] = dac_value >> 8; + d[2] = dac_value & 0xFF; + d[3] = 0; + d[4] = 0; + l = 5; + } + else + { + d[0] = JF_TR; + d[1] = track + 1; + d[2] = 0; + l = 3; + } + } + if (l > 0) { + i2c_master_tx(follower->addr, d, l); + } +} + +static void ii_mute_jf(i2c_follower_t* follower, uint8_t track, uint8_t mode) { + uint8_t d[3] = { 0 }; + + // clear all triggers to avoid hanging notes in SUSTAIN + d[0] = JF_TR; + d[1] = 0; + d[2] = 0; + i2c_master_tx(follower->addr, d, 3); +} + +static void ii_mode_jf(i2c_follower_t* follower, uint8_t track, uint8_t mode) { + uint8_t d[4] = { 0 }; + + if (mode > follower->mode_ct) return; + follower->active_mode = mode; + if (mode == 2) { + d[0] = JF_MODE; + d[1] = 0; + i2c_master_tx(follower->addr, d, 2); + + // clear all triggers to avoid hanging notes in SUSTAIN + d[0] = JF_TR; + d[1] = 0; + d[2] = 0; + d[3] = 0; + i2c_master_tx(follower->addr, d, 3); + } + else + { + d[0] = JF_MODE; + d[1] = 1; + i2c_master_tx(follower->addr, d, 2); + } +} + +static void ii_octave_jf(i2c_follower_t* follower, uint8_t track, int8_t octave) { + int16_t shift; + if (octave > 0) { + shift = ET[12*octave] << 2; + } + else if (octave < 0) { + shift = -(ET[12*(-octave)] << 2); + } + else { + shift = 0; + } + + uint8_t d[] = { JF_SHIFT, shift >> 8, shift & 0xFF }; + i2c_master_tx(follower->addr, d, 3); +} + +static void ii_init_txo(i2c_follower_t* follower, uint8_t track, uint8_t state) { + uint8_t d[4] = { 0 }; + + if (state == 0) { + d[0] = 0x60; // TO_ENV_ACT + d[1] = track; + d[2] = 0; + d[3] = 0; + i2c_master_tx(follower->addr, d, 4); + + d[0] = 0x40; // TO_OSC + d[1] = track; + d[2] = 0; + d[3] = 0; + i2c_master_tx(follower->addr, d, 4); + + d[0] = 0x10; // TO_CV + d[1] = track; + d[2] = 0; + d[3] = 0; + i2c_master_tx(follower->addr, d, 4); + } +} + +static void ii_mode_txo(i2c_follower_t* follower, uint8_t track, uint8_t mode) { + uint8_t d[4] = { 0 }; + + if (mode > follower->mode_ct) return; + follower->active_mode = mode; + + switch (mode) { + case 0: { // enveloped oscillators + d[0] = 0x60; // TO_ENV_ACT + d[1] = track; + d[2] = 0; + d[3] = 1; + i2c_master_tx(follower->addr, d, 4); + + d[0] = 0x15; // TO_CV_OFF + d[1] = track; + d[2] = 0; + d[3] = 0; + i2c_master_tx(follower->addr, d, 4); + + d[0] = 0x10; // TO_CV + d[1] = track; + d[2] = 8192 >> 8; + d[3] = 8192 & 0xFF; + i2c_master_tx(follower->addr, d, 4); + break; + } + case 1: { // gate/cv + d[0] = 0x60; // TO_ENV_ACT + d[1] = track; + d[2] = 0; + d[3] = 0; + i2c_master_tx(follower->addr, d, 4); + + d[0] = 0x40; // TO_OSC + d[1] = track; + d[2] = 0; + d[3] = 0; + i2c_master_tx(follower->addr, d, 4); + + d[0] = 0x10; // TO_CV + d[1] = track; + d[2] = 0; + d[3] = 0; + i2c_master_tx(follower->addr, d, 4); + break; + } + default: return; + } +} + +static void ii_octave_txo(i2c_follower_t* follower, uint8_t track, int8_t octave) { + int16_t shift; + switch (follower->active_mode) { + case 0: { // enveloped oscillator, pitch is calculated from oct + break; + } + case 1: { // gate / cv + if (octave > 0) { + shift = ET[12*octave] << 2; + } + else if (octave < 0) { + shift = -(ET[12*(-octave)] << 2); + } + else { + shift = 0; + } + uint8_t d[] = { + 0x15, // TO_CV_OFF + 0, + shift >> 8, + shift & 0xFF, + }; + for (uint8_t i = 0; i < 4; i++) { + d[1] = i; + i2c_master_tx(follower->addr, d, 4); + } + break; + } + default: return; + } +} + +static void ii_tr_txo(i2c_follower_t* follower, uint8_t track, uint8_t state) { + uint8_t d[4] = { 0 }; + + switch (follower->active_mode) { + case 0: { // enveloped oscillator + d[0] = 0x6D; // TO_ENV + d[1] = track; + d[2] = 0; + d[3] = state; + i2c_master_tx(follower->addr, d, 4); + break; + } + case 1: { // gate/cv + d[0] = 0x00; // TO_TR + d[1] = track; + d[2] = 0; + d[3] = state; + i2c_master_tx(follower->addr, d, 4); + break; + } + default: return; + } +} + +static void ii_mute_txo(i2c_follower_t* follower, uint8_t track, uint8_t mode) { + for (uint8_t i = 0; i < 4; i++) { + ii_tr_txo(follower, i, 0); + } +} + +static void ii_cv_txo(i2c_follower_t* follower, uint8_t track, uint16_t dac_value) { + uint8_t d[4] = { 0 }; + + switch (follower->active_mode) { + case 0: { // enveloped oscillator + dac_value = (int)dac_value + (ET[12*(4+follower->oct)] << 2); + d[0] = 0x40; // TO_OSC + d[1] = track; + d[2] = dac_value >> 8; + d[3] = dac_value & 0xFF; + i2c_master_tx(follower->addr, d, 4); + break; + } + case 1: { // gate/cv + d[0] = 0x10; // TO_CV + d[1] = track; + d[2] = dac_value >> 8; + d[3] = dac_value & 0xFF; + i2c_master_tx(follower->addr, d, 4); + break; + } + default: return; + } +} + +static void ii_slew_txo(i2c_follower_t* follower, uint8_t track, uint16_t slew) { + uint8_t d[4] = { 0 }; + + switch (follower->active_mode) { + case 0: { // oscillator + d[0] = 0x4F; // TO_OSC_SLEW + d[1] = track; + d[2] = slew >> 8; + d[3] = slew & 0xFF; + i2c_master_tx(follower->addr, d, 4); + break; + } + case 1: { // gate/cv + d[0] = 0x12; // TO_CV_SLEW + d[1] = track; + d[2] = slew >> 8; + d[3] = slew & 0xFF; + i2c_master_tx(follower->addr, d, 4); + break; + } + default: return; + } +} + +static void ii_u8_nop(i2c_follower_t* follower, uint8_t track, uint8_t state) { +} + +static void ii_u16_nop(i2c_follower_t* follower, uint8_t track, uint16_t dac_value) { +} + +i2c_follower_t followers[I2C_FOLLOWER_COUNT] = { + { + .addr = JF_ADDR, + .active = false, + .track_en = 0xF, + .oct = 0, + + .init = ii_init_jf, + .mode = ii_mode_jf, + .tr = ii_tr_jf, + .mute = ii_mute_jf, + .cv = ii_u16_nop, + .octave = ii_octave_jf, + .slew = ii_u16_nop, + + .mode_ct = 3, + .active_mode = 0, + }, + { + .addr = TELEXO_0, + .active = false, + .track_en = 0xF, + .oct = 0, + + .init = ii_init_txo, + .mode = ii_mode_txo, + .tr = ii_tr_txo, + .mute = ii_mute_txo, + .cv = ii_cv_txo, + .octave = ii_octave_txo, + .slew = ii_slew_txo, + + .mode_ct = 2, + .active_mode = 0, + }, + { + .addr = ER301_1, + .active = false, + .track_en = 0xF, + .oct = 0, + + .init = ii_u8_nop, + .mode = ii_u8_nop, + .tr = ii_tr_txo, + .mute = ii_mute_txo, + .cv = ii_cv_txo, + .octave = ii_octave_txo, + .slew = ii_slew_txo, + + .mode_ct = 1, + .active_mode = 1, // always gate/cv + }, +}; + +void follower_change_mode(i2c_follower_t* follower, uint8_t param) { + for (int i = 0; i < 4; i++) { + if (follower->track_en & (1 << i)) { + follower->mode(follower, i, param); + } + } +} + +void follower_change_octave(i2c_follower_t* follower, int8_t param) { + for (int i = 0; i < 4; i++) { + if (follower->track_en & (1 << i)) { + follower->octave(follower, i, param); + } + } +} diff --git a/src/ansible_ii_leader.h b/src/ansible_ii_leader.h new file mode 100644 index 0000000..52cfef9 --- /dev/null +++ b/src/ansible_ii_leader.h @@ -0,0 +1,33 @@ +#pragma once + +#define I2C_FOLLOWER_COUNT 3 + +struct i2c_follower_t; + +typedef void(*ii_u8_cb)(struct i2c_follower_t* follower, uint8_t track, uint8_t param); +typedef void(*ii_s8_cb)(struct i2c_follower_t* follower, uint8_t track, int8_t param); +typedef void(*ii_u16_cb)(struct i2c_follower_t* follower, uint8_t track, uint16_t param); + +typedef struct i2c_follower_t { + uint8_t addr; + bool active; + uint8_t track_en; + int8_t oct; + + ii_u8_cb init; + ii_u8_cb mode; + ii_u8_cb tr; + ii_u8_cb mute; + + ii_u16_cb cv; + ii_u16_cb slew; + ii_s8_cb octave; + + uint8_t mode_ct; + uint8_t active_mode; +} i2c_follower_t; + +extern i2c_follower_t followers[I2C_FOLLOWER_COUNT]; + +void follower_change_mode(i2c_follower_t* follower, uint8_t param); +void follower_change_octave(i2c_follower_t* follower, int8_t param); diff --git a/src/ansible_midi.c b/src/ansible_midi.c index f9eec93..38488f3 100644 --- a/src/ansible_midi.c +++ b/src/ansible_midi.c @@ -241,7 +241,7 @@ void set_mode_midi(void) { app_event_handlers[kEventTrNormal] = &handler_StandardTrNormal; app_event_handlers[kEventMidiPacket] = &handler_StandardMidiPacket; restore_midi_standard(); - init_i2c_slave(II_MID_ADDR); + if (!leader_mode) init_i2c_slave(II_MID_ADDR); process_ii = &ii_midi_standard; update_leds(1); break; @@ -255,7 +255,7 @@ void set_mode_midi(void) { restore_midi_arp(); clock = &clock_midi_arp; clock_set(arp_state.clock_period); - init_i2c_slave(II_ARP_ADDR); + if (!leader_mode) init_i2c_slave(II_ARP_ADDR); process_ii = &ii_midi_arp; update_leds(2); break; diff --git a/src/ansible_preset_docdef.c b/src/ansible_preset_docdef.c index 6f85f88..4643d2a 100644 --- a/src/ansible_preset_docdef.c +++ b/src/ansible_preset_docdef.c @@ -79,6 +79,62 @@ json_docdef_t ansible_meta_docdefs[] = { .dst_size = sizeof_field(nvram_data_t, state.i2c_addr), }), }, + { + .name = "followers", + .read = json_read_array, + .write = json_write_array, + .state = &ansible_json_read_array_state[0], + .params = &((json_read_array_params_t) { + .array_len = sizeof_field(nvram_data_t, state.followers) / sizeof_field(nvram_data_t, state.followers[0]), + .item_size = sizeof_field(nvram_data_t, state.followers[0]), + .item_docdef = &((json_docdef_t) { + .read = json_read_object, + .write = json_write_object, + .state = &ansible_app_object_state[0], + .params = &((json_read_object_params_t) { + .docdef_ct = 7, + .docdefs = ((json_docdef_t[]) { + { + .name = "active", + .read = json_read_scalar, + .write = json_write_bool, + .params = &((json_read_scalar_params_t) { + .dst_size = sizeof_field(nvram_data_t, state.followers[0].active), + .dst_offset = offsetof(nvram_data_t, state.followers[0].active), + }), + }, + { + .name = "track_en", + .read = json_read_scalar, + .write = json_write_number, + .params = &((json_read_scalar_params_t) { + .dst_size = sizeof_field(nvram_data_t, state.followers[0].track_en), + .dst_offset = offsetof(nvram_data_t, state.followers[0].track_en), + }), + }, + { + .name = "oct", + .read = json_read_scalar, + .write = json_write_number, + .params = &((json_read_scalar_params_t) { + .dst_size = sizeof_field(nvram_data_t, state.followers[0].oct), + .dst_offset = offsetof(nvram_data_t, state.followers[0].oct), + }), + }, + { + .name = "active_mode", + .read = json_read_scalar, + .write = json_write_number, + .params = &((json_read_scalar_params_t) { + .dst_size = sizeof_field(nvram_data_t, state.followers[0].active_mode), + .dst_offset = offsetof(nvram_data_t, state.followers[0].active_mode), + }), + }, + }), + }), + }), + }), + }, { .name = "connected", .read = json_read_enum, diff --git a/src/ansible_tt.c b/src/ansible_tt.c index 4e2e2b0..a2c91d8 100644 --- a/src/ansible_tt.c +++ b/src/ansible_tt.c @@ -22,7 +22,7 @@ void set_mode_tt(void) { app_event_handlers[kEventTrNormal] = &handler_TTTrNormal; clock = &clock_tt; clock_set(f.tt_state.clock_period); - init_i2c_slave(f.state.i2c_addr); + if (!leader_mode) init_i2c_slave(f.state.i2c_addr); process_ii = &ii_tt; update_leds(0); diff --git a/src/config.mk b/src/config.mk index 3e6ec2b..0ab16e7 100644 --- a/src/config.mk +++ b/src/config.mk @@ -71,6 +71,7 @@ CSRCS = \ ../src/ansible_tt.c \ ../src/ansible_usb_disk.c \ ../src/ansible_preset_docdef.c \ + ../src/ansible_ii_leader.c \ ../src/gitversion.c \ ../libavr32/src/adc.c \ ../libavr32/src/arp.c \ diff --git a/src/main.c b/src/main.c index 126f291..7195bdf 100644 --- a/src/main.c +++ b/src/main.c @@ -65,6 +65,7 @@ usb flash #include "ansible_midi.h" #include "ansible_tt.h" #include "ansible_usb_disk.h" +#include "ansible_ii_leader.h" #define FIRSTRUN_KEY 0x22 @@ -78,6 +79,9 @@ nvram_data_t f; ansible_mode_t ansible_mode; +bool leader_mode = false; +uint16_t aux_param[2][4] = { { 0 } }; + //////////////////////////////////////////////////////////////////////////////// // prototypes @@ -324,7 +328,7 @@ static void handler_FrontLong(s32 data) { print_dbg("\r\n+ i2c address: "); print_dbg_hex(f.state.i2c_addr); // TEST - init_i2c_slave(f.state.i2c_addr); + if (!leader_mode) init_i2c_slave(f.state.i2c_addr); } static void handler_SaveFlash(s32 data) { @@ -519,16 +523,137 @@ void update_leds(uint8_t m) { void set_tr(uint8_t n) { gpio_set_gpio_pin(n); + uint8_t tr = n - TR1; + for (uint8_t i = 0; i < I2C_FOLLOWER_COUNT; i++) { + bool play_follower = followers[i].active + && followers[i].track_en & (1 << tr); + if (play_follower) { + followers[i].tr(&followers[i], tr, 1); + } + } } void clr_tr(uint8_t n) { gpio_clr_gpio_pin(n); + uint8_t tr = n - TR1; + for (uint8_t i = 0; i < I2C_FOLLOWER_COUNT; i++) { + bool play_follower = followers[i].active + && followers[i].track_en & (1 << tr); + if (play_follower) { + followers[i].tr(&followers[i], tr, 0); + } + } } uint8_t get_tr(uint8_t n) { return gpio_get_pin_value(n); } +void set_cv_note(uint8_t n, uint16_t note) { + dac_set_value(n, tuning_table[n][note]); + for (uint8_t i = 0; i < I2C_FOLLOWER_COUNT; i++) { + bool play_follower = followers[i].active + && followers[i].track_en & (1 << n); + if (play_follower) { + uint16_t cv_transposed = ET[note] << 2; + followers[i].cv(&followers[i], n, cv_transposed); + } + } +} + +void set_cv_slew(uint8_t n, uint16_t s) { + dac_set_slew(n, s); + for (uint8_t i = 0; i < I2C_FOLLOWER_COUNT; i++) { + bool play_follower = followers[i].active + && followers[i].track_en & (1 << n); + if (play_follower) { + followers[i].slew(&followers[i], n, s); + } + } +} + +void reset_outputs(void) { + for (uint8_t n = 0; n < 4; n++) { + dac_set_slew(n, 0); + gpio_clr_gpio_pin(n + TR1); + for (uint8_t i = 0; i < I2C_FOLLOWER_COUNT; i++) { + bool play_follower = followers[i].active + && followers[i].track_en & (1 << n); + if (play_follower) { + followers[i].mute(&followers[n], 0, 0); + } + } + } +} + +static void follower_on(uint8_t n) { + for (uint8_t i = 0; i < 4; i++) { + followers[n].init(&followers[n], i, 1); + followers[n].mode(&followers[n], i, followers[n].active_mode); + followers[n].octave(&followers[n], 0, followers[n].oct); + } +} + +static void follower_off(uint8_t n) { + for (uint8_t i = 0; i < 4; i++) { + followers[n].init(&followers[n], i, 0); + } +} + +void toggle_follower(uint8_t n) { + followers[n].active = !followers[n].active; + if (followers[n].active) { + for (uint8_t i = 0; i < I2C_FOLLOWER_COUNT; i++) { + if (i != n && followers[i].active) { + follower_on(n); + return; + } + } + print_dbg("\r\n> enter i2c leader mode"); + leader_mode = true; + init_i2c_master(); + follower_on(n); + } + else { + follower_off(n); + for (uint8_t i = 0; i < I2C_FOLLOWER_COUNT; i++) { + if (i != n && followers[i].active) { + return; + } + } + print_dbg("\r\n> exit i2c leader mode"); + leader_mode = false; + switch (ansible_mode) { + case mArcLevels: + init_i2c_slave(II_LV_ADDR); + break; + case mArcCycles: + init_i2c_slave(II_CY_ADDR); + break; + case mGridKria: + init_i2c_slave(II_KR_ADDR); + break; + case mGridMP: + init_i2c_slave(II_MP_ADDR); + break; + case mGridES: + init_i2c_slave(ES); + break; + case mMidiStandard: + init_i2c_slave(II_MID_ADDR); + break; + case mMidiArp: + init_i2c_slave(II_ARP_ADDR); + break; + case mTT: + init_i2c_slave(f.state.i2c_addr); + break; + default: + break; + } + } +} + void clock_set(uint32_t n) { timer_set(&clockTimer, n); } @@ -552,7 +677,21 @@ void load_flash_state(void) { print_dbg("\r\ni2c addr: "); print_dbg_hex(f.state.i2c_addr); - init_i2c_slave(f.state.i2c_addr); + + leader_mode = false; + memcpy((void*)followers, f.state.followers, sizeof(followers)); + for (uint8_t i = 0; i < I2C_FOLLOWER_COUNT; i++) { + if (followers[i].active) { + if (!leader_mode) { + leader_mode = true; + init_i2c_master(); + } + follower_on(i); + } + } + if (!leader_mode) { + init_i2c_slave(f.state.i2c_addr); + } } void ii_ansible(uint8_t* d, uint8_t len) { @@ -641,6 +780,7 @@ int main(void) flashc_memset32((void*)&(f.state.midi_mode), mMidiStandard, 4, true); flashc_memset8((void*)&(f.state.i2c_addr), 0xA0, 1, true); flashc_memset8((void*)&(f.state.grid_varibrightness), 16, 1, true); + flashc_memcpy((void*)f.state.followers, followers, sizeof(followers), true); default_tuning(); default_kria(); default_mp(); diff --git a/src/main.h b/src/main.h index d2ddc7e..e62dbe2 100644 --- a/src/main.h +++ b/src/main.h @@ -6,6 +6,7 @@ #include "ansible_arc.h" #include "ansible_midi.h" #include "ansible_tt.h" +#include "ansible_ii_leader.h" #define TR1 B02 #define TR2 B03 @@ -40,6 +41,11 @@ typedef enum { connected_t connected; + + +extern bool leader_mode; +extern uint16_t aux_param[2][4]; + typedef struct { connected_t connected; ansible_mode_t arc_mode; @@ -48,6 +54,7 @@ typedef struct { ansible_mode_t none_mode; uint8_t i2c_addr; uint8_t grid_varibrightness; + i2c_follower_t followers[I2C_FOLLOWER_COUNT]; } ansible_state_t; @@ -69,6 +76,7 @@ typedef const struct { extern nvram_data_t f; extern ansible_mode_t ansible_mode; +extern i2c_follower_t followers[I2C_FOLLOWER_COUNT]; extern softTimer_t auxTimer[4]; extern uint16_t tuning_table[4][120]; @@ -87,6 +95,10 @@ void set_mode(ansible_mode_t m); void update_leds(uint8_t m); void set_tr(uint8_t n); void clr_tr(uint8_t n); +void set_cv_note(uint8_t n, uint16_t cv); +void set_cv_slew(uint8_t n, uint16_t s); +void reset_outputs(void); +void toggle_follower(uint8_t n); uint8_t get_tr(uint8_t n); void clock_set(uint32_t n); void clock_set_tr(uint32_t n, uint8_t phase);