Permalink
Cannot retrieve contributors at this time
| #include <ctype.h> | |
| #include <stdio.h> | |
| #include <string.h> | |
| // asf | |
| #include "compiler.h" | |
| #include "delay.h" | |
| #include "gpio.h" | |
| #include "intc.h" | |
| #include "pm.h" | |
| #include "preprocessor.h" | |
| #include "print_funcs.h" | |
| #include "spi.h" | |
| #include "sysclk.h" | |
| #include "usb_protocol_hid.h" | |
| // system | |
| #include "adc.h" | |
| #include "events.h" | |
| #include "font.h" | |
| #include "hid.h" | |
| #include "i2c.h" | |
| #include "init_common.h" | |
| #include "init_teletype.h" | |
| #include "interrupts.h" | |
| #include "kbd.h" | |
| #include "monome.h" | |
| #include "region.h" | |
| #include "screen.h" | |
| #include "timers.h" | |
| #include "util.h" | |
| // this | |
| #include "chaos.h" | |
| #include "conf_board.h" | |
| #include "edit_mode.h" | |
| #include "flash.h" | |
| #include "globals.h" | |
| #include "grid.h" | |
| #include "help_mode.h" | |
| #include "keyboard_helper.h" | |
| #include "live_mode.h" | |
| #include "pattern_mode.h" | |
| #include "preset_r_mode.h" | |
| #include "preset_w_mode.h" | |
| #include "teletype.h" | |
| #include "teletype_io.h" | |
| #include "usb_disk_mode.h" | |
| #ifdef TELETYPE_PROFILE | |
| #include "profile.h" | |
| profile_t prof_Script[SCRIPT_COUNT], prof_Delay[DELAY_SIZE], prof_CV, prof_ADC, | |
| prof_ScreenRefresh; | |
| void tele_profile_script(size_t s) { | |
| profile_update(&prof_Script[s]); | |
| } | |
| void tele_profile_delay(uint8_t d) { | |
| profile_update(&prof_Delay[d]); | |
| } | |
| #endif | |
| //////////////////////////////////////////////////////////////////////////////// | |
| // constants | |
| #define RATE_CLOCK 10 | |
| #define RATE_CV 6 | |
| #define SS_TIMEOUT 90 /* minutes */ * 60 * 100 | |
| //////////////////////////////////////////////////////////////////////////////// | |
| // globals (defined in globals.h) | |
| scene_state_t scene_state; | |
| char scene_text[SCENE_TEXT_LINES][SCENE_TEXT_CHARS]; | |
| uint8_t preset_select; | |
| region line[8] = { { .w = 128, .h = 8, .x = 0, .y = 0 }, | |
| { .w = 128, .h = 8, .x = 0, .y = 8 }, | |
| { .w = 128, .h = 8, .x = 0, .y = 16 }, | |
| { .w = 128, .h = 8, .x = 0, .y = 24 }, | |
| { .w = 128, .h = 8, .x = 0, .y = 32 }, | |
| { .w = 128, .h = 8, .x = 0, .y = 40 }, | |
| { .w = 128, .h = 8, .x = 0, .y = 48 }, | |
| { .w = 128, .h = 8, .x = 0, .y = 56 } }; | |
| char copy_buffer[SCENE_TEXT_LINES][SCENE_TEXT_CHARS]; | |
| uint8_t copy_buffer_len = 0; | |
| //////////////////////////////////////////////////////////////////////////////// | |
| // locals | |
| static device_config_t device_config; | |
| static tele_mode_t mode = M_LIVE; | |
| static tele_mode_t last_mode = M_LIVE; | |
| static uint32_t ss_counter = 0; | |
| static u8 grid_connected = 0; | |
| static u8 grid_control_mode = 0; | |
| static uint16_t adc[4]; | |
| typedef struct { | |
| uint16_t now; | |
| uint16_t off; | |
| uint16_t target; | |
| uint16_t slew; | |
| uint16_t step; | |
| int32_t delta; | |
| uint32_t a; | |
| } aout_t; | |
| static u8 ignore_front_press = 0; | |
| static aout_t aout[4]; | |
| static bool metro_timer_enabled; | |
| static uint8_t front_timer; | |
| static uint8_t mod_key = 0, hold_key, hold_key_count = 0; | |
| static uint64_t last_adc_tick = 0; | |
| // timers | |
| static softTimer_t clockTimer = { .next = NULL, .prev = NULL }; | |
| static softTimer_t refreshTimer = { .next = NULL, .prev = NULL }; | |
| static softTimer_t keyTimer = { .next = NULL, .prev = NULL }; | |
| static softTimer_t cvTimer = { .next = NULL, .prev = NULL }; | |
| static softTimer_t adcTimer = { .next = NULL, .prev = NULL }; | |
| static softTimer_t hidTimer = { .next = NULL, .prev = NULL }; | |
| static softTimer_t metroTimer = { .next = NULL, .prev = NULL }; | |
| static softTimer_t monomePollTimer = { .next = NULL, .prev = NULL }; | |
| static softTimer_t monomeRefreshTimer = { .next = NULL, .prev = NULL }; | |
| static softTimer_t gridFaderTimer = { .next = NULL, .prev = NULL }; | |
| //////////////////////////////////////////////////////////////////////////////// | |
| // prototypes | |
| // timer callback prototypes | |
| static void cvTimer_callback(void* o); | |
| static void clockTimer_callback(void* o); | |
| static void refreshTimer_callback(void* o); | |
| static void keyTimer_callback(void* o); | |
| static void adcTimer_callback(void* o); | |
| static void hidTimer_callback(void* o); | |
| static void metroTimer_callback(void* o); | |
| static void monome_poll_timer_callback(void* obj); | |
| static void monome_refresh_timer_callback(void* obj); | |
| static void grid_fader_timer_callback(void* obj); | |
| // event handler prototypes | |
| static void handler_None(int32_t data); | |
| static void handler_Front(int32_t data); | |
| static void handler_PollADC(int32_t data); | |
| static void handler_KeyTimer(int32_t data); | |
| static void handler_HidConnect(int32_t data); | |
| static void handler_HidDisconnect(int32_t data); | |
| static void handler_HidTimer(int32_t data); | |
| static void handler_MscConnect(int32_t data); | |
| static void handler_Trigger(int32_t data); | |
| static void handler_ScreenRefresh(int32_t data); | |
| static void handler_EventTimer(int32_t data); | |
| static void handler_AppCustom(int32_t data); | |
| // event queue | |
| static void empty_event_handlers(void); | |
| static void assign_main_event_handlers(void); | |
| static void assign_msc_event_handlers(void); | |
| static void check_events(void); | |
| // key handling | |
| static void process_keypress(uint8_t key, uint8_t mod_key, bool is_held_key, | |
| bool is_release); | |
| static bool process_global_keys(uint8_t key, uint8_t mod_key, bool is_held_key); | |
| // start/stop monome polling/refresh timers | |
| void timers_set_monome(void); | |
| void timers_unset_monome(void); | |
| // other | |
| static void render_init(void); | |
| static void exit_screensaver(void); | |
| static void update_device_config(u8 refresh); | |
| //////////////////////////////////////////////////////////////////////////////// | |
| // timer callbacks | |
| void cvTimer_callback(void* o) { | |
| #ifdef TELETYPE_PROFILE | |
| profile_update(&prof_CV); | |
| #endif | |
| bool updated = false; | |
| bool slewing = false; | |
| for (size_t i = 0; i < 4; i++) { | |
| if (aout[i].step) { | |
| aout[i].step--; | |
| if (aout[i].step == 0) { aout[i].now = aout[i].target; } | |
| else { | |
| aout[i].a += aout[i].delta; | |
| aout[i].now = aout[i].a >> 16; | |
| slewing = true; | |
| } | |
| updated = true; | |
| } | |
| } | |
| set_slew_icon(slewing); | |
| if (updated) { | |
| uint16_t a0, a1, a2, a3; | |
| if (device_config.flip) { | |
| a0 = aout[3].now >> 2; | |
| a1 = aout[2].now >> 2; | |
| a2 = aout[1].now >> 2; | |
| a3 = aout[0].now >> 2; | |
| } | |
| else { | |
| a0 = aout[0].now >> 2; | |
| a1 = aout[1].now >> 2; | |
| a2 = aout[2].now >> 2; | |
| a3 = aout[3].now >> 2; | |
| } | |
| spi_selectChip(DAC_SPI, DAC_SPI_NPCS); | |
| spi_write(DAC_SPI, 0x31); | |
| spi_write(DAC_SPI, a2 >> 4); | |
| spi_write(DAC_SPI, a2 << 4); | |
| spi_write(DAC_SPI, 0x31); | |
| spi_write(DAC_SPI, a0 >> 4); | |
| spi_write(DAC_SPI, a0 << 4); | |
| spi_unselectChip(DAC_SPI, DAC_SPI_NPCS); | |
| spi_selectChip(DAC_SPI, DAC_SPI_NPCS); | |
| spi_write(DAC_SPI, 0x38); | |
| spi_write(DAC_SPI, a3 >> 4); | |
| spi_write(DAC_SPI, a3 << 4); | |
| spi_write(DAC_SPI, 0x38); | |
| spi_write(DAC_SPI, a1 >> 4); | |
| spi_write(DAC_SPI, a1 << 4); | |
| spi_unselectChip(DAC_SPI, DAC_SPI_NPCS); | |
| } | |
| #ifdef TELETYPE_PROFILE | |
| profile_update(&prof_CV); | |
| #endif | |
| } | |
| void clockTimer_callback(void* o) { | |
| event_t e = { .type = kEventTimer, .data = 0 }; | |
| event_post(&e); | |
| } | |
| void refreshTimer_callback(void* o) { | |
| event_t e = { .type = kEventScreenRefresh, .data = 0 }; | |
| event_post(&e); | |
| } | |
| void keyTimer_callback(void* o) { | |
| event_t e = { .type = kEventKeyTimer, .data = 0 }; | |
| event_post(&e); | |
| } | |
| void adcTimer_callback(void* o) { | |
| event_t e = { .type = kEventPollADC, .data = 0 }; | |
| event_post(&e); | |
| } | |
| void hidTimer_callback(void* o) { | |
| event_t e = { .type = kEventHidTimer, .data = 0 }; | |
| event_post(&e); | |
| } | |
| void metroTimer_callback(void* o) { | |
| event_t e = { .type = kEventAppCustom, .data = 0 }; | |
| event_post(&e); | |
| } | |
| // monome polling callback | |
| static void monome_poll_timer_callback(void* obj) { | |
| // asynchronous, non-blocking read | |
| // UHC callback spawns appropriate events | |
| ftdi_read(); | |
| } | |
| // monome refresh callback | |
| static void monome_refresh_timer_callback(void* obj) { | |
| if (grid_connected && scene_state.grid.grid_dirty) { | |
| static event_t e; | |
| e.type = kEventMonomeRefresh; | |
| event_post(&e); | |
| } | |
| } | |
| // monome: start polling | |
| void timers_set_monome(void) { | |
| timer_add(&monomePollTimer, 20, &monome_poll_timer_callback, NULL); | |
| timer_add(&monomeRefreshTimer, 30, &monome_refresh_timer_callback, NULL); | |
| } | |
| // monome stop polling | |
| void timers_unset_monome(void) { | |
| timer_remove(&monomePollTimer); | |
| timer_remove(&monomeRefreshTimer); | |
| } | |
| void grid_fader_timer_callback(void* o) { | |
| grid_process_fader_slew(&scene_state); | |
| } | |
| //////////////////////////////////////////////////////////////////////////////// | |
| // event handlers | |
| void handler_None(int32_t data) {} | |
| void handler_Front(int32_t data) { | |
| if (ss_counter >= SS_TIMEOUT) { | |
| exit_screensaver(); | |
| return; | |
| } | |
| ss_counter = 0; | |
| if (data == 0) { | |
| if (ignore_front_press) { | |
| ignore_front_press = 0; | |
| return; | |
| } | |
| if (grid_connected) { | |
| grid_control_mode = !grid_control_mode; | |
| if (grid_control_mode && mode == M_HELP) set_mode(M_LIVE); | |
| grid_set_control_mode(grid_control_mode, mode, &scene_state); | |
| return; | |
| } | |
| if (mode != M_PRESET_R) { | |
| front_timer = 0; | |
| set_preset_r_mode(adc[1] >> 7); | |
| set_mode(M_PRESET_R); | |
| } | |
| else | |
| front_timer = 15; | |
| } | |
| else { | |
| if (front_timer) { set_last_mode(); } | |
| front_timer = 0; | |
| } | |
| } | |
| void handler_PollADC(int32_t data) { | |
| #ifdef TELETYPE_PROFILE | |
| profile_update(&prof_ADC); | |
| #endif | |
| static int16_t last_knob = 0; | |
| adc_convert(&adc); | |
| ss_set_in(&scene_state, adc[0] << 2); | |
| if (ss_counter >= SS_TIMEOUT && (adc[1] >> 8 != last_knob >> 8)) { | |
| exit_screensaver(); | |
| return; | |
| } | |
| last_knob = adc[1]; | |
| if (mode == M_PATTERN) { | |
| process_pattern_knob(adc[1], mod_key); | |
| ss_set_param(&scene_state, adc[1] << 2); | |
| } | |
| else if (mode == M_PRESET_R && !(grid_connected && grid_control_mode)) { | |
| uint8_t preset = adc[1] >> 6; | |
| uint8_t deadzone = preset & 1; | |
| preset >>= 1; | |
| if (!deadzone || abs(preset - get_preset()) > 1) | |
| process_preset_r_preset(preset); | |
| } | |
| else { | |
| ss_set_param(&scene_state, adc[1] << 2); | |
| } | |
| #ifdef TELETYPE_PROFILE | |
| profile_update(&prof_ADC); | |
| #endif | |
| } | |
| void handler_KeyTimer(int32_t data) { | |
| if (front_timer) { | |
| if (front_timer == 1 && !grid_connected) { | |
| if (mode == M_PRESET_R) { process_preset_r_load(); } | |
| front_timer = 0; | |
| } | |
| else | |
| front_timer--; | |
| } | |
| if (hold_key) { | |
| if (hold_key_count > 4) | |
| process_keypress(hold_key, mod_key, true, false); | |
| else | |
| hold_key_count++; | |
| } | |
| } | |
| void handler_HidConnect(int32_t data) { | |
| timer_add(&hidTimer, 47, &hidTimer_callback, NULL); | |
| } | |
| void handler_HidDisconnect(int32_t data) { | |
| timer_remove(&hidTimer); | |
| } | |
| void handler_HidTimer(int32_t data) { | |
| if (hid_get_frame_dirty()) { | |
| const int8_t* frame = (const int8_t*)hid_get_frame_data(); | |
| mod_key = frame[0]; | |
| for (size_t i = 2; i < 8; i++) { | |
| if (frame[i] == 0) { | |
| if (i == 2) { | |
| hold_key_count = 0; | |
| process_keypress(hold_key, mod_key, false, true); | |
| hold_key = 0; | |
| } | |
| } | |
| else if (frame_compare(frame[i]) == false) { | |
| hold_key = frame[i]; | |
| hold_key_count = 0; | |
| process_keypress(hold_key, mod_key, false, false); | |
| } | |
| } | |
| set_old_frame(frame); | |
| } | |
| hid_clear_frame_dirty(); | |
| } | |
| void handler_MscConnect(int32_t data) { | |
| // disable event handlers while doing USB write | |
| assign_msc_event_handlers(); | |
| // disable timers | |
| u8 flags = irqs_pause(); | |
| // clear screen | |
| for (size_t i = 0; i < 8; i++) { | |
| region_fill(&line[i], 0); | |
| region_draw(&line[i]); | |
| } | |
| // do USB | |
| tele_usb_disk(); | |
| // renable teletype | |
| set_mode(M_LIVE); | |
| assign_main_event_handlers(); | |
| irqs_resume(flags); | |
| } | |
| void handler_Trigger(int32_t data) { | |
| u8 input = device_config.flip ? 7 - data : data; | |
| if (!ss_get_mute(&scene_state, input)) { | |
| bool tr_state = gpio_get_pin_value(A00 + data); | |
| if (tr_state) { | |
| if (scene_state.variables.script_pol[input] & 1) { | |
| run_script(&scene_state, input); | |
| } | |
| } | |
| else { | |
| if (scene_state.variables.script_pol[input] & 2) { | |
| run_script(&scene_state, input); | |
| } | |
| } | |
| } | |
| } | |
| void handler_ScreenRefresh(int32_t data) { | |
| #ifdef TELETYPE_PROFILE | |
| profile_update(&prof_ScreenRefresh); | |
| #endif | |
| uint8_t screen_dirty = 0; | |
| switch (mode) { | |
| case M_PATTERN: screen_dirty = screen_refresh_pattern(); break; | |
| case M_PRESET_W: screen_dirty = screen_refresh_preset_w(); break; | |
| case M_PRESET_R: screen_dirty = screen_refresh_preset_r(); break; | |
| case M_HELP: screen_dirty = screen_refresh_help(); break; | |
| case M_LIVE: screen_dirty = screen_refresh_live(&scene_state); break; | |
| case M_EDIT: screen_dirty = screen_refresh_edit(); break; | |
| } | |
| u8 grid = 0; | |
| for (size_t i = 0; i < 8; i++) | |
| if (screen_dirty & (1 << i)) { | |
| grid = 1; | |
| if (ss_counter < SS_TIMEOUT) region_draw(&line[i]); | |
| } | |
| if (grid_control_mode && grid) scene_state.grid.grid_dirty = 1; | |
| #ifdef TELETYPE_PROFILE | |
| profile_update(&prof_ScreenRefresh); | |
| #endif | |
| } | |
| void handler_EventTimer(int32_t data) { | |
| tele_tick(&scene_state, RATE_CLOCK); | |
| if (ss_counter < SS_TIMEOUT) { | |
| ss_counter++; | |
| if (ss_counter == SS_TIMEOUT) { | |
| u8 empty = 0; | |
| for (int i = 0; i < 64; i++) | |
| for (int j = 0; j < 64; j++) | |
| screen_draw_region(i << 1, j, 2, 1, &empty); | |
| } | |
| } | |
| } | |
| void handler_AppCustom(int32_t data) { | |
| // If we need multiple custom event handlers then we can use an enum in the | |
| // data argument. For now, we're just using it for the metro | |
| if (ss_get_script_len(&scene_state, METRO_SCRIPT)) { | |
| set_metro_icon(true); | |
| run_script(&scene_state, METRO_SCRIPT); | |
| if (grid_connected && grid_control_mode) | |
| grid_metro_triggered(&scene_state); | |
| } | |
| else | |
| set_metro_icon(false); | |
| } | |
| static void handler_FtdiConnect(s32 data) { | |
| ftdi_setup(); | |
| } | |
| static void handler_FtdiDisconnect(s32 data) { | |
| grid_connected = 0; | |
| timers_unset_monome(); | |
| } | |
| static void handler_MonomeConnect(s32 data) { | |
| hold_key = 0; | |
| timers_set_monome(); | |
| grid_connected = 1; | |
| if (grid_control_mode && mode == M_HELP) set_mode(M_LIVE); | |
| grid_set_control_mode(grid_control_mode, mode, &scene_state); | |
| scene_state.grid.grid_dirty = 1; | |
| grid_clear_held_keys(); | |
| } | |
| static void handler_MonomePoll(s32 data) { | |
| monome_read_serial(); | |
| } | |
| static void handler_MonomeRefresh(s32 data) { | |
| grid_refresh(&scene_state); | |
| monomeFrameDirty = 0b1111; | |
| (*monome_refresh)(); | |
| } | |
| static void handler_MonomeGridKey(s32 data) { | |
| if (grid_control_mode && ss_counter >= SS_TIMEOUT) { | |
| exit_screensaver(); | |
| return; | |
| } | |
| if (grid_control_mode) ss_counter = 0; | |
| u8 x, y, z; | |
| monome_grid_key_parse_event_data(data, &x, &y, &z); | |
| grid_process_key(&scene_state, x, y, z, 0); | |
| } | |
| //////////////////////////////////////////////////////////////////////////////// | |
| // event queue | |
| void empty_event_handlers() { | |
| for (size_t i = 0; i < kNumEventTypes; i++) { | |
| app_event_handlers[i] = &handler_None; | |
| } | |
| } | |
| void assign_main_event_handlers() { | |
| empty_event_handlers(); | |
| app_event_handlers[kEventFront] = &handler_Front; | |
| app_event_handlers[kEventPollADC] = &handler_PollADC; | |
| app_event_handlers[kEventKeyTimer] = &handler_KeyTimer; | |
| app_event_handlers[kEventHidConnect] = &handler_HidConnect; | |
| app_event_handlers[kEventHidDisconnect] = &handler_HidDisconnect; | |
| app_event_handlers[kEventHidTimer] = &handler_HidTimer; | |
| app_event_handlers[kEventMscConnect] = &handler_MscConnect; | |
| app_event_handlers[kEventTrigger] = &handler_Trigger; | |
| app_event_handlers[kEventScreenRefresh] = &handler_ScreenRefresh; | |
| app_event_handlers[kEventTimer] = &handler_EventTimer; | |
| app_event_handlers[kEventAppCustom] = &handler_AppCustom; | |
| app_event_handlers[kEventFtdiConnect] = &handler_FtdiConnect; | |
| app_event_handlers[kEventFtdiDisconnect] = &handler_FtdiDisconnect; | |
| app_event_handlers[kEventMonomeConnect] = &handler_MonomeConnect; | |
| app_event_handlers[kEventMonomeDisconnect] = &handler_None; | |
| app_event_handlers[kEventMonomePoll] = &handler_MonomePoll; | |
| app_event_handlers[kEventMonomeRefresh] = &handler_MonomeRefresh; | |
| app_event_handlers[kEventMonomeGridKey] = &handler_MonomeGridKey; | |
| } | |
| static void assign_msc_event_handlers(void) { | |
| empty_event_handlers(); | |
| // one day this could be used to map the front button and pot to be used as | |
| // a UI with a memory stick | |
| } | |
| // app event loop | |
| void check_events(void) { | |
| event_t e; | |
| if (event_next(&e)) { (app_event_handlers)[e.type](e.data); } | |
| } | |
| //////////////////////////////////////////////////////////////////////////////// | |
| // mode handling | |
| // defined in globals.h | |
| void set_mode(tele_mode_t m) { | |
| last_mode = mode; | |
| switch (m) { | |
| case M_LIVE: | |
| set_live_mode(); | |
| mode = M_LIVE; | |
| break; | |
| case M_EDIT: | |
| set_edit_mode(); | |
| mode = M_EDIT; | |
| break; | |
| case M_PATTERN: | |
| set_pattern_mode(); | |
| mode = M_PATTERN; | |
| break; | |
| case M_PRESET_W: | |
| set_preset_w_mode(); | |
| mode = M_PRESET_W; | |
| break; | |
| case M_PRESET_R: | |
| set_preset_r_mode(adc[1] >> 7); | |
| mode = M_PRESET_R; | |
| break; | |
| case M_HELP: | |
| set_help_mode(); | |
| mode = M_HELP; | |
| break; | |
| } | |
| if (mode != M_HELP) flash_update_last_mode(mode); | |
| } | |
| // defined in globals.h | |
| void set_last_mode() { | |
| if (mode == last_mode) return; | |
| if (last_mode == M_LIVE || last_mode == M_EDIT || last_mode == M_PATTERN) | |
| set_mode(last_mode); | |
| else | |
| set_mode(M_LIVE); | |
| } | |
| // defined in globals.h | |
| void clear_delays_and_slews(scene_state_t* ss) { | |
| clear_delays(ss); | |
| for (int i = 0; i < 4; i++) { aout[i].step = 1; } | |
| } | |
| //////////////////////////////////////////////////////////////////////////////// | |
| // key handling | |
| void process_keypress(uint8_t key, uint8_t mod_key, bool is_held_key, | |
| bool is_release) { | |
| // reset inactivity counter | |
| if (ss_counter >= SS_TIMEOUT) { | |
| exit_screensaver(); | |
| return; | |
| } | |
| ss_counter = 0; | |
| // release is a special case for live mode | |
| if (is_release) { | |
| if (mode == M_LIVE) | |
| process_live_keys(key, mod_key, is_held_key, true, &scene_state); | |
| return; | |
| } | |
| // first try global keys | |
| if (process_global_keys(key, mod_key, is_held_key)) return; | |
| switch (mode) { | |
| case M_EDIT: process_edit_keys(key, mod_key, is_held_key); break; | |
| case M_LIVE: | |
| process_live_keys(key, mod_key, is_held_key, false, &scene_state); | |
| break; | |
| case M_PATTERN: process_pattern_keys(key, mod_key, is_held_key); break; | |
| case M_PRESET_W: | |
| process_preset_w_keys(key, mod_key, is_held_key); | |
| break; | |
| case M_PRESET_R: | |
| process_preset_r_keys(key, mod_key, is_held_key); | |
| break; | |
| case M_HELP: process_help_keys(key, mod_key, is_held_key); break; | |
| } | |
| } | |
| bool process_global_keys(uint8_t k, uint8_t m, bool is_held_key) { | |
| if (is_held_key) // none of these want to work with held keys | |
| return false; | |
| // <tab>: change modes, live to edit to pattern and back | |
| if (match_no_mod(m, k, HID_TAB)) { | |
| if (mode == M_LIVE) | |
| set_mode(M_EDIT); | |
| else if (mode == M_EDIT) | |
| set_mode(M_PATTERN); | |
| else | |
| set_mode(M_LIVE); | |
| return true; | |
| } | |
| // <esc>: preset read mode, or return to last mode | |
| else if (match_no_mod(m, k, HID_ESCAPE)) { | |
| if (mode == M_PRESET_R) | |
| set_last_mode(); | |
| else { | |
| set_mode(M_PRESET_R); | |
| } | |
| return true; | |
| } | |
| // alt-<esc>: preset write mode | |
| else if (match_alt(m, k, HID_ESCAPE)) { | |
| set_mode(M_PRESET_W); | |
| return true; | |
| } | |
| // win-<esc>: clear delays, stack and slews | |
| else if (match_win(m, k, HID_ESCAPE)) { | |
| if (!is_held_key) clear_delays_and_slews(&scene_state); | |
| return true; | |
| } | |
| // <alt>-?: help text, or return to last mode | |
| else if (match_shift_alt(m, k, HID_SLASH) || match_alt(m, k, HID_H)) { | |
| if (mode == M_HELP) | |
| set_last_mode(); | |
| else { | |
| set_mode(M_HELP); | |
| } | |
| return true; | |
| } | |
| // <F1> through <F8>: run corresponding script | |
| // <F9>: run metro script | |
| // <F10>: run init script | |
| else if (no_mod(m) && k >= HID_F1 && k <= HID_F10) { | |
| run_script(&scene_state, k - HID_F1); | |
| return true; | |
| } | |
| // alt-<F1> through alt-<F8>: edit corresponding script | |
| // alt-<F9>: edit metro script | |
| // alt-<F10>: edit init script | |
| else if (mod_only_alt(m) && k >= HID_F1 && k <= HID_F10) { | |
| set_edit_mode_script(k - HID_F1); | |
| set_mode(M_EDIT); | |
| return true; | |
| } | |
| // ctrl-<F1> through ctrl-<F8> mute triggers | |
| else if (mod_only_ctrl(m) && k >= HID_F1 && k <= HID_F8) { | |
| bool muted = ss_get_mute(&scene_state, (k - HID_F1)); | |
| ss_set_mute(&scene_state, (k - HID_F1), !muted); | |
| screen_mutes_updated(); | |
| return true; | |
| } | |
| // ctrl-<F9> toggle metro | |
| else if (mod_only_ctrl(m) && k == HID_F9) { | |
| scene_state.variables.m_act = !scene_state.variables.m_act; | |
| tele_metro_updated(); | |
| return true; | |
| } | |
| // <numpad-1> through <numpad-8>: run corresponding script | |
| else if (no_mod(m) && k >= HID_KEYPAD_1 && k <= HID_KEYPAD_8) { | |
| run_script(&scene_state, k - HID_KEYPAD_1); | |
| return true; | |
| } | |
| // <num lock>: jump to pattern mode | |
| else if (match_no_mod(m, k, HID_KEYPAD_NUM_LOCK) || | |
| match_no_mod(m, k, HID_F11)) { | |
| if (mode != M_PATTERN) { set_mode(M_PATTERN); } | |
| return true; | |
| } | |
| // <print screen>: jump to live mode | |
| else if (match_no_mod(m, k, HID_PRINTSCREEN) || | |
| match_no_mod(m, k, HID_F12)) { | |
| if (mode != M_LIVE) { set_mode(M_LIVE); } | |
| return true; | |
| } | |
| else { | |
| return false; | |
| } | |
| } | |
| //////////////////////////////////////////////////////////////////////////////// | |
| // other | |
| void render_init(void) { | |
| region_alloc(&line[0]); | |
| region_alloc(&line[1]); | |
| region_alloc(&line[2]); | |
| region_alloc(&line[3]); | |
| region_alloc(&line[4]); | |
| region_alloc(&line[5]); | |
| region_alloc(&line[6]); | |
| region_alloc(&line[7]); | |
| } | |
| void exit_screensaver(void) { | |
| ss_counter = 0; | |
| set_mode(mode); | |
| } | |
| void update_device_config(u8 refresh) { | |
| screen_set_direction(device_config.flip); | |
| if (refresh) set_mode(mode); | |
| flash_update_device_config(&device_config); | |
| } | |
| //////////////////////////////////////////////////////////////////////////////// | |
| // teletype_io.h | |
| uint32_t tele_get_ticks() { | |
| return get_ticks(); | |
| } | |
| void tele_metro_updated() { | |
| uint32_t metro_time = scene_state.variables.m; | |
| bool m_act = scene_state.variables.m_act; | |
| if (metro_time < METRO_MIN_UNSUPPORTED_MS) { | |
| metro_time = METRO_MIN_UNSUPPORTED_MS; | |
| } | |
| if (m_act && !metro_timer_enabled) { // enable the timer | |
| timer_add(&metroTimer, metro_time, &metroTimer_callback, NULL); | |
| metro_timer_enabled = true; | |
| } | |
| else if (!m_act && metro_timer_enabled) { // disable the timer | |
| timer_remove(&metroTimer); | |
| metro_timer_enabled = false; | |
| } | |
| else if (metro_timer_enabled) { // just update the time | |
| timer_set(&metroTimer, metro_time); | |
| } | |
| if (metro_timer_enabled && ss_get_script_len(&scene_state, METRO_SCRIPT)) | |
| set_metro_icon(true); | |
| else | |
| set_metro_icon(false); | |
| if (grid_connected && grid_control_mode) scene_state.grid.grid_dirty = 1; | |
| edit_mode_refresh(); | |
| } | |
| void tele_metro_reset() { | |
| if (metro_timer_enabled) timer_reset(&metroTimer); | |
| } | |
| void tele_tr(uint8_t i, int16_t v) { | |
| uint32_t pin = B08 + (device_config.flip ? 3 - i : i); | |
| if (v) | |
| gpio_set_pin_high(pin); | |
| else | |
| gpio_set_pin_low(pin); | |
| } | |
| void tele_cv(uint8_t i, int16_t v, uint8_t s) { | |
| int16_t t = v + aout[i].off; | |
| if (t < 0) | |
| t = 0; | |
| else if (t > 16383) | |
| t = 16383; | |
| aout[i].target = t; | |
| if (s) { | |
| aout[i].step = aout[i].slew; | |
| aout[i].delta = ((aout[i].target - aout[i].now) << 16) / aout[i].step; | |
| } | |
| else { | |
| aout[i].step = 1; | |
| aout[i].now = aout[i].target; | |
| } | |
| aout[i].a = aout[i].now << 16; | |
| timer_manual(&cvTimer); | |
| } | |
| void tele_cv_slew(uint8_t i, int16_t v) { | |
| aout[i].slew = v / RATE_CV; | |
| if (aout[i].slew == 0) aout[i].slew = 1; | |
| } | |
| void tele_cv_off(uint8_t i, int16_t v) { | |
| aout[i].off = v; | |
| } | |
| void tele_update_adc(u8 force) { | |
| if (!force && get_ticks() == last_adc_tick) return; | |
| last_adc_tick = get_ticks(); | |
| adc_convert(&adc); | |
| ss_set_in(&scene_state, adc[0] << 2); | |
| ss_set_param(&scene_state, adc[1] << 2); | |
| } | |
| void tele_ii_tx(uint8_t addr, uint8_t* data, uint8_t l) { | |
| i2c_master_tx(addr, data, l); | |
| } | |
| void tele_ii_rx(uint8_t addr, uint8_t* data, uint8_t l) { | |
| i2c_master_rx(addr, data, l); | |
| } | |
| void tele_scene(uint8_t i, uint8_t init_grid) { | |
| preset_select = i; | |
| flash_read(i, &scene_state, &scene_text, init_grid); | |
| if (init_grid) scene_state.grid.scr_dirty = scene_state.grid.grid_dirty = 1; | |
| } | |
| void tele_kill() { | |
| for (int i = 0; i < 4; i++) { | |
| aout[i].step = 1; | |
| tele_tr(i, 0); | |
| } | |
| } | |
| bool tele_get_input_state(uint8_t n) { | |
| return gpio_get_pin_value(A00 + n) > 0; | |
| } | |
| void tele_vars_updated() { | |
| set_vars_updated(); | |
| } | |
| void tele_save_calibration() { | |
| flash_update_cal(&scene_state.cal); | |
| } | |
| void grid_key_press(uint8_t x, uint8_t y, uint8_t z) { | |
| grid_process_key(&scene_state, x, y, z, 1); | |
| } | |
| void device_flip() { | |
| device_config.flip = !device_config.flip; | |
| update_device_config(1); | |
| } | |
| //////////////////////////////////////////////////////////////////////////////// | |
| // main | |
| int main(void) { | |
| sysclk_init(); | |
| init_dbg_rs232(FMCK_HZ); | |
| init_gpio(); | |
| assign_main_event_handlers(); | |
| init_events(); | |
| init_tc(); | |
| init_spi(); | |
| init_adc(); | |
| irq_initialize_vectors(); | |
| register_interrupts(); | |
| cpu_irq_enable(); | |
| init_usb_host(); | |
| init_monome(); | |
| init_oled(); | |
| // wait to allow for any i2c devices to fully initalise | |
| delay_ms(1500); | |
| init_i2c_master(); | |
| print_dbg("\r\n\r\n// teletype! //////////////////////////////// "); | |
| ss_init(&scene_state); | |
| // screen init | |
| render_init(); | |
| if (is_flash_fresh()) { | |
| char s[36]; | |
| strcpy(s, "SCENES WILL BE OVERWRITTEN!"); | |
| region_fill(&line[5], 0); | |
| font_string_region_clip(&line[5], s, 0, 0, 0x4, 0); | |
| region_draw(&line[5]); | |
| strcpy(s, "PRESS TO CONFIRM"); | |
| region_fill(&line[6], 0); | |
| font_string_region_clip(&line[6], s, 0, 0, 0x4, 0); | |
| region_draw(&line[6]); | |
| strcpy(s, "DO NOT PRESS OTHERWISE!"); | |
| region_fill(&line[7], 0); | |
| font_string_region_clip(&line[7], s, 0, 0, 0x4, 0); | |
| region_draw(&line[7]); | |
| ignore_front_press = 1; | |
| } | |
| // prepare flash (if needed) | |
| flash_prepare(); | |
| // load device config | |
| flash_get_device_config(&device_config); | |
| update_device_config(0); | |
| // load calibration data from flash | |
| flash_get_cal(&scene_state.cal); | |
| ss_update_param_scale(&scene_state); | |
| ss_update_in_scale(&scene_state); | |
| // load preset from flash | |
| preset_select = flash_last_saved_scene(); | |
| ss_set_scene(&scene_state, preset_select); | |
| flash_read(preset_select, &scene_state, &scene_text, 1); | |
| // setup daisy chain for two dacs | |
| spi_selectChip(DAC_SPI, DAC_SPI_NPCS); | |
| spi_write(DAC_SPI, 0x80); | |
| spi_write(DAC_SPI, 0xff); | |
| spi_write(DAC_SPI, 0xff); | |
| spi_unselectChip(DAC_SPI, DAC_SPI_NPCS); | |
| timer_add(&clockTimer, RATE_CLOCK, &clockTimer_callback, NULL); | |
| timer_add(&cvTimer, RATE_CV, &cvTimer_callback, NULL); | |
| timer_add(&keyTimer, 71, &keyTimer_callback, NULL); | |
| timer_add(&adcTimer, 61, &adcTimer_callback, NULL); | |
| timer_add(&refreshTimer, 63, &refreshTimer_callback, NULL); | |
| timer_add(&gridFaderTimer, 25, &grid_fader_timer_callback, NULL); | |
| // update IN and PARAM in case Init uses them | |
| tele_update_adc(1); | |
| // manually call tele_metro_updated to sync metro to scene_state | |
| metro_timer_enabled = false; | |
| tele_metro_updated(); | |
| // init chaos generator | |
| chaos_init(); | |
| clear_delays(&scene_state); | |
| aout[0].slew = 1; | |
| aout[1].slew = 1; | |
| aout[2].slew = 1; | |
| aout[3].slew = 1; | |
| init_live_mode(); | |
| set_mode(M_LIVE); | |
| run_script(&scene_state, INIT_SCRIPT); | |
| scene_state.initializing = false; | |
| #ifdef TELETYPE_PROFILE | |
| uint32_t count = 0; | |
| #endif | |
| while (true) { | |
| check_events(); | |
| #ifdef TELETYPE_PROFILE | |
| count = (count + 1) % (FCPU_HZ / 10); | |
| if (count == 0) { | |
| print_dbg("\r\n\r\nProfile Data (us)"); | |
| for (uint8_t i = 0; i < SCRIPT_COUNT - 1; i++) { | |
| print_dbg("\r\nScript "); | |
| print_dbg_ulong(i); | |
| print_dbg(":\t"); | |
| print_dbg_ulong(profile_delta_us(&prof_Script[i])); | |
| } | |
| uint32_t total = 0; | |
| for (uint8_t i = 0; i < DELAY_SIZE; i++) | |
| total += profile_delta_us(&prof_Delay[i]); | |
| print_dbg("\r\nDelays (total):\t"); | |
| print_dbg_ulong(total); | |
| print_dbg("\r\nCV Write:\t"); | |
| print_dbg_ulong(profile_delta_us(&prof_CV)); | |
| print_dbg("\r\nADC Read:\t"); | |
| print_dbg_ulong(profile_delta_us(&prof_ADC)); | |
| print_dbg("\r\nScreen Refresh:\t"); | |
| print_dbg_ulong(profile_delta_us(&prof_ScreenRefresh)); | |
| } | |
| #endif | |
| } | |
| } |