// // Channel render functions. // void draw_param_stub(size_t idx, u8 clr) { if (idx >= 10) { return; } u8 x0 = PARAMS_START_X + (idx % 5) * PARAMS_BOX_OFFSET_X; u8 x1 = x0 + PARAMS_BOX_W; u8 y0 = PARAMS_START_Y; if (idx >= 5) { y0 += PARAMS_BOX_OFFSET_Y; } u8 y1 = y0 + PARAMS_BOX_H; for (size_t i = 0; i < PARAMS_BOX_W; i += 2) { draw_pixel(x0 + i + 1, y0, clr); draw_pixel(x0 + i + 1, y1, clr); } for (size_t i = 0; i < PARAMS_BOX_H; i += 2) { draw_pixel(x0, y0 + i + 1, clr); draw_pixel(x1, y0 + i + 1, clr); } } void draw_channel_sprite(size_t x, size_t y, u8 clr, u8 idx) { draw_rect(x, y, x + CHAN_W, y + CHAN_H - 1, clr); switch (idx) { case 0: { draw_line(x + 2, y + 5, x + 6, y + 5, clr); draw_line(x + 6, y + 2, x + 6, y + 5, clr); draw_line(x + 7, y + 2, x + 12, y + 2, clr); draw_line(x + 12, y + 2, x + 12, y + 5, clr); draw_line(x + 12, y + 5, x + 16, y + 5, clr); } break; case 1: { draw_line(x + 2, y + 5, x + 4, y + 5, clr); draw_line(x + 4, y + 2, x + 4, y + 5, clr); draw_line(x + 4, y + 2, x + 7, y + 2, clr); draw_line(x + 7, y + 2, x + 7, y + 5, clr); draw_line(x + 7, y + 5, x + 11, y + 5, clr); draw_line(x + 11, y + 2, x + 11, y + 5, clr); draw_line(x + 11, y + 2, x + 14, y + 2, clr); draw_line(x + 14, y + 2, x + 14, y + 5, clr); draw_line(x + 14, y + 5, x + 16, y + 5, clr); } break; case 2: { draw_line(x + 2, y + 5, x + 5, y + 2, clr); draw_line(x + 6, y + 2, x + 6, y + 5, clr); draw_line(x + 7, y + 5, x + 10, y + 2, clr); draw_line(x + 11, y + 2, x + 11, y + 5, clr); draw_line(x + 12, y + 5, x + 15, y + 2, clr); draw_line(x + 16, y + 2, x + 16, y + 5, clr); } break; case 3: { u8 y_off[] = { 5, 2, 4, 2, 3, 4, 5, 2, 4, 3, 5, 3, 2, 4, 5 }; for (size_t i = 0; i < 15; i++) { draw_pixel(x + 2 + i, y + y_off[i], clr); } } break; default: break; } } void draw_channels(void) { // NOTE: Different channel colors can be interesting. u8 colors[] = { 1, 1, 1, 1, }; for (size_t i = 0; i < 4; i++) { bool active = false; if (settings.global_mute) { active = !settings.mutes[i]; } else { switch (i) { case 0: { active = patterns[pattern_selection_loc].ch1.active; } break; case 1: { active = patterns[pattern_selection_loc].ch2.active; } break; case 2: { active = patterns[pattern_selection_loc].ch3.active; } break; case 3: { active = patterns[pattern_selection_loc].ch4.active; } break; } if (patterns[pattern_selection_loc].empty) { active = true; } } u8 clr = active ? colors[i] : COL_OFF; size_t y = CHAN_START_Y + i * CHAN_OFFSET_Y; draw_channel_sprite(CHAN_START_X, y, clr, i); } } void draw_channel_cursor(size_t i, u8 clr, bool dither) { size_t offset_x = 0; size_t offset_y = CHAN_H + i * CHAN_OFFSET_Y + 1; size_t x0 = CHAN_START_X + offset_x; size_t x1 = CHAN_START_X + offset_x + CHAN_W; size_t y = CHAN_START_Y + offset_y; if (dither) { for (size_t i = 0; i < CHAN_W - 1; i += 2) { draw_pixel(x0 + i, y, clr); draw_pixel(x0 + i + 1, y + 1, clr); } draw_pixel(x0 + CHAN_W, y, clr); } else { draw_line(x0, y, x1, y, clr); draw_line(x0, y + 1, x1, y + 1, clr); } } // // Trigger render functions. // void clear_trigger(size_t i) { size_t offset_x = TRIG_OFFSET_X * (i % 8); size_t offset_y = i < 8 ? 0 : TRIG_OFFSET_Y; size_t x0 = TRIG_START_X + offset_x + 2; size_t x1 = TRIG_START_X + offset_x + TRIG_W - 1; size_t y0 = TRIG_START_Y + offset_y + 1; size_t y1 = TRIG_START_Y + offset_y + TRIG_H - 4; draw_filled_rect(x0, y0, x1, y1, COL_BG); } void draw_trigger(size_t chan, size_t i) { TriggerNote trig = {0}; switch (chan) { case 0: { trig = patterns[pattern_selection_loc].ch1.notes[i]; } break; case 1: { trig = patterns[pattern_selection_loc].ch2.notes[i]; } break; case 2: { trig = patterns[pattern_selection_loc].ch3.notes[i]; } break; case 3: { trig = patterns[pattern_selection_loc].ch4.notes[i]; } break; } if (trig.active) { size_t offset_x = TRIG_OFFSET_X * (i % 8); size_t offset_y = i < 8 ? 0 : TRIG_OFFSET_Y; size_t x = TRIG_START_X + offset_x; size_t y = TRIG_START_Y + offset_y; const u32 *tile = ¬e_name_sprites[4 * trig.note]; draw_icn(x, y, &tile[0], COL_FG, 1, 0); draw_icn(x + 8, y, &tile[2], COL_FG, 1, 0); } else { clear_trigger(i); } } void draw_trig_cursor(size_t i, u8 clr, bool dither) { size_t offset_x = TRIG_OFFSET_X * (i % 8); size_t offset_y = i < 8 ? 2 : 2 + TRIG_OFFSET_Y; size_t x0 = TRIG_START_X + offset_x + 1; size_t x1 = TRIG_START_X + TRIG_W + offset_x; size_t y = TRIG_START_Y + TRIG_H + offset_y; if (dither) { for (size_t i = 0; i < TRIG_W - 1; i += 2) { draw_pixel(x0 + i, y, clr); draw_pixel(x0 + i + 1, y + 1, clr); } draw_pixel(x0 + TRIG_W - 1, y, clr); } else { draw_line(x0, y, x1, y, clr); draw_line(x0, y + 1, x1, y + 1, clr); } } void draw_right_col_cursor(int i, u8 clr) { size_t x0_pos[] = { // Banks. BANK_START_X - 7, BANK_START_X + 9, BANK_START_X - 7, BANK_START_X + 9, BANK_START_X - 7, BANK_START_X + 9, // Scale. BANK_START_X - 7, // BPM. BANK_START_X - 7, // Settings. BANK_START_X - 7, // Play/stop. BANK_START_X - 7, BANK_START_X + 9, }; size_t x1_pos[] = { // Banks. BANK_START_X + 5, BANK_START_X + 21, BANK_START_X + 5, BANK_START_X + 21, BANK_START_X + 5, BANK_START_X + 21, // Scale. BANK_START_X + 21, // BPM. BANK_START_X + 21, // Settings. BANK_START_X + 21, // Play/stop. BANK_START_X + 5, BANK_START_X + 21, }; size_t y_pos[] = { // Banks. BANK_START_Y + 12, BANK_START_Y + 12, BANK_START_Y + 12 + 18, BANK_START_Y + 12 + 18, BANK_START_Y + 12 + 18 * 2, BANK_START_Y + 12 + 18 * 2, // Scale. BANK_START_Y + 78, // BPM. BANK_START_Y + 108, // Settings. BANK_START_Y + 124, // Play/stop. BANK_START_Y + 138, BANK_START_Y + 138, }; size_t x0 = x0_pos[i]; size_t x1 = x1_pos[i]; size_t y = y_pos[i]; draw_line(x0, y, x1, y, clr); draw_line(x0, y + 1, x1, y + 1, clr); } void draw_current_step(u8 step, u8 clr) { size_t offset_x = TRIG_OFFSET_X * (step % 8); size_t offset_y = step < 8 ? 2 : 2 + TRIG_OFFSET_Y; size_t x0 = TRIG_START_X + 4 + offset_x; size_t x1 = TRIG_START_X - 3 + TRIG_W + offset_x; size_t y = TRIG_START_Y - 5 + TRIG_H + offset_y; draw_line(x0, y, x1, y, clr); } void draw_bank_buttons() { size_t x = BANK_START_X + 1; size_t y = BANK_START_Y; txt_draws_small("BANK", x - 3, y - 10, COL_FG); char bank_names[] = { 'A', 'B', 'C', 'D', 'E', 'F' }; s16 x_offset[] = { -8, 8, -8, 8, -8, 8, }; size_t y_offset[] = { 0, 0, 18, 18, 36, 36, }; for (int i = 0; i < 6; i++) { int color = COL_OFF; if (i == current_bank) { color = COL_FG; } if (i == next_bank && current_bank != next_bank) { color = COL_ACC_0; } u8 x0 = x + x_offset[i]; u8 x1 = x + x_offset[i] + BANK_W; u8 y0 = y + y_offset[i]; u8 y1 = y + y_offset[i] + BANK_H; draw_rect(x0, y0, x1, y1, color); txt_drawc(bank_names[i], x0 + 3, y0 + 1, color); } } void draw_dotted_rect(size_t x0, size_t y0, size_t x1, size_t y1, u8 clr) { size_t w = x1 - x0; size_t h = y1 - y0; for (size_t i = 0; i <= w; i += 2) { draw_pixel(x0 + i, y0, clr); draw_pixel(x0 + i, y1, clr); } for (size_t i = 0; i <= h; i += 2) { draw_pixel(x0, y0 + i, clr); draw_pixel(x1, y0 + i, clr); } } void draw_pattern_buttons() { // Clear patterns. size_t x = PAT_START_X; size_t y = PAT_START_Y; txt_draws_small("PAT", x, y - 10, COL_FG); char pat_names[] = { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', }; for (int i = 0; i < 8; i++) { int color = COL_OFF; if (i == current_pattern) { color = COL_FG; } if (i == next_pattern && current_pattern != next_pattern) { color = COL_ACC_0; } draw_filled_rect(x, y, x + PAT_W, y + PAT_H, COL_BG); if (patterns[i].empty) { draw_dotted_rect(x, y, x + PAT_W, y + PAT_H, color); } else { draw_rect(x, y, x + PAT_W, y + PAT_H, color); } txt_drawc(pat_names[i], x + 4, y + 1, color); y += PAT_OFFSET_Y; } } void draw_pattern_cursor(size_t i, u8 clr, bool dither) { size_t offset_x = 0; size_t offset_y = PAT_H + i * PAT_OFFSET_Y + 2; size_t x0 = PAT_START_X + offset_x; size_t x1 = PAT_START_X + offset_x + PAT_W; size_t y = PAT_START_Y + offset_y; if (dither) { for (size_t i = 0; i < PAT_W; i += 2) { draw_pixel(x0 + i, y, clr); draw_pixel(x0 + i + 1, y + 1, clr); } draw_pixel(x0 + PAT_W, y, clr); } else { draw_line(x0, y, x1, y, clr); draw_line(x0, y + 1, x1, y + 1, clr); } } void draw_play() { size_t x = PLAY_START_X; size_t y = PLAY_START_Y; draw_filled_rect(x, y, x + PLAY_STOP_W, y + PLAY_STOP_H, COL_BG); draw_rect(x, y, x + PLAY_STOP_W, y + PLAY_STOP_H, COL_ACC_2); size_t x_btn = x + PLAY_STOP_W / 2 - 1; if (play_status == 1) { // Pause button draw_filled_rect(x_btn - 1, y + 2, x_btn, y + 6, COL_ACC_2); draw_filled_rect(x_btn + 2, y + 2, x_btn + 3, y + 6, COL_ACC_2); } else { // Play button x += 1; draw_line(x_btn + 0, y + 2, x_btn + 0, y + 6, COL_ACC_2); draw_line(x_btn + 1, y + 3, x_btn + 1, y + 5, COL_ACC_2); draw_line(x_btn + 2, y + 4, x_btn + 2, y + 4, COL_ACC_2); } } void draw_wrench(u8 x, u8 y, u8 clr) { draw_line(x + 10, y + 3, x + 13, y + 3, clr); draw_line(x + 10, y + 5, x + 13, y + 5, clr); draw_line(x + 19, y + 3, x + 22, y + 3, clr); draw_line(x + 19, y + 5, x + 22, y + 5, clr); draw_line(x + 13, y + 4, x + 19, y + 4, clr); } void draw_settings() { size_t x = SETTINGS_START_X; size_t y = SETTINGS_START_Y; draw_rect(x + 2, y, x + R_COL_W, y + PLAY_STOP_H, COL_OFF); draw_wrench(x, y, COL_OFF); } void draw_settings_cursor(void) { u8 y_pos[] = { 49, 49 + 17, 49 + 17 * 3, 49 + 17 * 4, 49 + 17 * 5, 49 + 17 * 6, }; int x = 8; int y = y_pos[settings_cursor_loc]; draw_line(x, y, x + 64, y, COL_ACC_0); draw_line(x, y + 1, x + 64, y + 1, COL_ACC_0); } void draw_stop() { size_t x = STOP_START_X; size_t y = STOP_START_Y; size_t x_btn = x + PLAY_STOP_W / 2 - 2; draw_rect(x, y, x + PLAY_STOP_W, y + PLAY_STOP_H, COL_ACC_1); draw_filled_rect(x_btn, y + 2, x_btn + 4, y + 6, COL_ACC_1); } void draw_bpm() { size_t x = BPM_START_X; size_t y = BPM_START_Y + 2; // Draw bounding box. draw_filled_rect(x, y, x + R_COL_W - 2, y + BPM_H - 3, COL_BG); draw_rect(x, y, x + R_COL_W - 2, y + BPM_H - 3, COL_FG); txt_draws_small("BPM", x + 7, y - 10, COL_FG); if (settings.sync == SYNC_IN_LINK_96BPQ || settings.sync == SYNC_IN_LINK_48BPQ || settings.sync == SYNC_IN_LINK_24BPQ || settings.sync == SYNC_IN_LINK_12BPQ || settings.sync == SYNC_IN_LINK_6BPQ || settings.sync == SYNC_IN_LINK_4BPQ) { txt_draws("SYNC", x + 2, y + 2, COL_FG); } else { // Make sure its horizontally centered if only 2 digits int bpm; if (settings.global_bpm) { bpm = settings.bpm; } else { bpm = patterns[pattern_selection_loc].bpm; } if (bpm >= 100) { txt_drawf("%d", x + 5, y + 2, COL_FG, bpm); } else { txt_drawf("%d", x + 8, y + 2, COL_FG, bpm); } } } void draw_scale() { size_t x = SCALE_START_X; size_t y = SCALE_START_Y; // Draw bounding box. draw_filled_rect(x, y, x + R_COL_W - 2, y + SCALE_H - 3, COL_BG); draw_rect(x, y, x + R_COL_W - 2, y + SCALE_H - 3, COL_FG); txt_draws_small("SCALE", x + 3, y - 10, COL_FG); txt_draws(scale_short[current_scale], x + 2, y + 2, COL_FG); } void draw_triggers(void) { for (size_t i = 0; i < 16; i++) { size_t offset_x = TRIG_OFFSET_X * (i % 8); size_t offset_y = i < 8 ? 0 : 0 + TRIG_OFFSET_Y; size_t x0 = TRIG_START_X + offset_x + 1; size_t x1 = TRIG_START_X + offset_x + TRIG_W; size_t y0 = TRIG_START_Y + offset_y; size_t y1 = TRIG_START_Y + offset_y + TRIG_H; draw_rect(x0, y0, x1, y1, COL_FG); clear_trigger(i); if (!patterns[pattern_selection_loc].empty) { draw_trigger(channel_selection_loc, i); } } } void draw_note(u8 note, u8 clr) { size_t octave = note / 12; size_t value = note % 12; size_t base_x = PIANO_START_X + 2; size_t base_y0 = PIANO_START_Y + 2; size_t base_y1 = PIANO_START_Y - 2; size_t x0 = 0; size_t y0 = 0; size_t x1 = 0; size_t y1 = 0; switch (value) { // White notes. case 0:{ x0 = base_x + octave * 28; x1 = x0 + 1; y0 = base_y0; y1 = base_y1 + PIANO_H; draw_filled_rect(x0, y0, x1, y1, clr); x0 = base_x + octave * 28 + 2; x1 = x0; y0 = base_y1 + PIANO_BLACK_H; y1 = y0 + PIANO_H - PIANO_BLACK_H; draw_filled_rect(x0, y0, x1, y1, clr); } break; case 2:{ x0 = base_x + octave * 28 + 5; x1 = x0; y0 = base_y0; y1 = base_y1 + PIANO_BLACK_H; draw_filled_rect(x0, y0, x1, y1, clr); x0 = base_x + octave * 28 + 4; x1 = x0 + 2; y0 = base_y1 + PIANO_BLACK_H; y1 = y0 + PIANO_H - PIANO_BLACK_H; draw_filled_rect(x0, y0, x1, y1, clr); } break; case 4:{ x0 = base_x + octave * 28 + 9; x1 = x0 + 1; y0 = base_y0; y1 = base_y1 + PIANO_H; draw_filled_rect(x0, y0, x1, y1, clr); x0 = base_x + octave * 28 + 8; x1 = x0 + 2; y0 = base_y1 + PIANO_BLACK_H; y1 = y0 + PIANO_H - PIANO_BLACK_H; draw_filled_rect(x0, y0, x1, y1, clr); } break; case 5:{ x0 = base_x + octave * 28 + 12; x1 = x0 + 1; y0 = base_y0; y1 = base_y1 + PIANO_H; draw_filled_rect(x0, y0, x1, y1, clr); x0 = base_x + octave * 28 + 14; x1 = x0; y0 = base_y1 + PIANO_BLACK_H; y1 = y0 + PIANO_H - PIANO_BLACK_H; draw_filled_rect(x0, y0, x1, y1, clr); } break; case 7:{ x0 = base_x + octave * 28 + 17; x1 = x0; y0 = base_y0; y1 = base_y1 + PIANO_H; draw_filled_rect(x0, y0, x1, y1, clr); x0 = base_x + octave * 28 + 16; x1 = x0 + 2; y0 = base_y1 + PIANO_BLACK_H; y1 = y0 + PIANO_H - PIANO_BLACK_H; draw_filled_rect(x0, y0, x1, y1, clr); } break; case 9:{ x0 = base_x + octave * 28 + 21; x1 = x0; y0 = base_y0; y1 = base_y1 + PIANO_H; draw_filled_rect(x0, y0, x1, y1, clr); x0 = base_x + octave * 28 + 20; x1 = x0 + 2; y0 = base_y1 + PIANO_BLACK_H; y1 = y0 + PIANO_H - PIANO_BLACK_H; draw_filled_rect(x0, y0, x1, y1, clr); } break; case 11: { x0 = base_x + octave * 28 + 25; x1 = x0 + 1; y0 = base_y0; y1 = base_y1 + PIANO_H; draw_filled_rect(x0, y0, x1, y1, clr); x0 = base_x + octave * 28 + 24; x1 = x0 + 2; y0 = base_y1 + PIANO_BLACK_H; y1 = y0 + PIANO_H - PIANO_BLACK_H; draw_filled_rect(x0, y0, x1, y1, clr); } break; default: { // NOTE: Decide if this looks better or nah. Also, not the right // place for it... // if (clr == COL_FG) { // clr = COL_BG; // } y0 = base_y0; y1 = base_y1 + PIANO_BLACK_H - 2; switch (value) { case 1: { x0 = base_x + octave * 28 + 3; } break; case 3: { x0 = base_x + octave * 28 + 7; } break; case 6: { x0 = base_x + octave * 28 + 15; } break; case 8: { x0 = base_x + octave * 28 + 19; } break; case 10: { x0 = base_x + octave * 28 + 23; } break; } x1 = x0; draw_line(x0, y0, x1, y1, clr); } break; } } void draw_piano(void) { size_t x0 = PIANO_START_X; size_t x1 = PIANO_START_X + PIANO_W; size_t y0 = PIANO_START_Y; size_t y1 = PIANO_START_Y + PIANO_H; draw_rect(x0, y0, x1, y1, COL_FG); for (size_t i = 0; i < 12 * 6; i++) { u8 clr = COL_FG; s8 pos = i % 12; switch (pos) { case 0: case 2: case 4: case 5: case 7: case 9: case 11: break; default: { clr = COL_BG; } break; } if (input_handler == handle_right_col_selection && right_col_selection_loc == R_COL_SCALE) { if (pos == current_scale_root) { clr = COL_ACC_1; } else { s8 scale_pos = (pos - current_scale_root) % 12; if (scale_pos < 0) { scale_pos *= -1; scale_pos = 12 - scale_pos; } if (scales[current_scale][scale_pos] == 1) { clr = COL_ACC_0; } } } draw_note(i, clr); } } void draw_params_cursor_wave(size_t i, u8 clr) { size_t x = PARAMS_START_X + (i % 5) * PARAMS_BOX_OFFSET_X; size_t y = PARAMS_START_Y + PARAMS_BOX_H; if (i >= 5) { y += PARAMS_BOX_OFFSET_Y; } u8 x0 = x; u8 x1 = x + PARAMS_BOX_W; u8 y0 = y - 6; u8 y1 = y; draw_filled_rect(x0 + 1, y0, x1 - 1, y1, clr); draw_line(x0, y0 + 1, x0, y1 - 1, clr); draw_line(x1, y0 + 1, x1, y1 - 1, clr); { size_t x = PARAMS_START_X; size_t y = PARAMS_START_Y; switch (i) { case 0: { x += 4 + PARAMS_BOX_OFFSET_X * 0; y += PARAMS_BOX_H - 7; txt_draws_small("shape", x, y, COL_BG); } break; case 1: { x += 6 + PARAMS_BOX_OFFSET_X * 1; y += PARAMS_BOX_H - 7; txt_draws_small("type", x, y, COL_BG); } break; case 2: { x += 4 + PARAMS_BOX_OFFSET_X * 2; y += PARAMS_BOX_H - 7; txt_draws_small("shape", x, y, COL_BG); } break; case 3: { x += 6 + PARAMS_BOX_OFFSET_X * 3; y += PARAMS_BOX_H - 7; txt_draws_small("type", x, y, COL_BG); } break; case 4: { x += 6 + PARAMS_BOX_OFFSET_X * 4; y += PARAMS_BOX_H - 7; txt_draws_small("prob", x, y, COL_BG); } break; case 5: { x += 4 + PARAMS_BOX_OFFSET_X * 0; y += PARAMS_BOX_H - 7 + PARAMS_BOX_OFFSET_Y; txt_draws_small("voice", x, y, COL_BG); } break; case 6: { x += 8 + PARAMS_BOX_OFFSET_X * 1; y += PARAMS_BOX_H - 7 + PARAMS_BOX_OFFSET_Y; txt_draws_small("vol", x, y, COL_BG); } break; case 7: { x += 2 + PARAMS_BOX_OFFSET_X * 2; y += PARAMS_BOX_H - 7 + PARAMS_BOX_OFFSET_Y; txt_draws_small("attack", x, y, COL_BG); } break; case 8: { x += 4 + PARAMS_BOX_OFFSET_X * 3; y += PARAMS_BOX_H - 7 + PARAMS_BOX_OFFSET_Y; txt_draws_small("decay", x, y, COL_BG); } break; case 9: { x += 8 + PARAMS_BOX_OFFSET_X * 4; y += PARAMS_BOX_H - 7 + PARAMS_BOX_OFFSET_Y; txt_draws_small("pan", x, y, COL_BG); } break; default: break; } } } void draw_params_cursor_noise(size_t i, u8 clr) { size_t x = PARAMS_START_X + (i % 5) * PARAMS_BOX_OFFSET_X; size_t y = PARAMS_START_Y + PARAMS_BOX_H; if (i >= 5) { y += PARAMS_BOX_OFFSET_Y; } u8 x0 = x; u8 x1 = x + PARAMS_BOX_W; u8 y0 = y - 6; u8 y1 = y; draw_filled_rect(x0 + 1, y0, x1 - 1, y1, clr); draw_line(x0, y0 + 1, x0, y1 - 1, clr); draw_line(x1, y0 + 1, x1, y1 - 1, clr); { size_t x = PARAMS_START_X; size_t y = PARAMS_START_Y; switch (i) { case 0: { x += 6 + PARAMS_BOX_OFFSET_X * 0; y += PARAMS_BOX_H - 7; txt_draws_small("mode", x, y, COL_BG); } break; case 1: { x += 8 + PARAMS_BOX_OFFSET_X * 1; y += PARAMS_BOX_H - 7; txt_draws_small("vol", x, y, COL_BG); } break; case 2: { x += 6 + PARAMS_BOX_OFFSET_X * 2; y += PARAMS_BOX_H - 7; txt_draws_small("time", x, y, COL_BG); } break; case 3: { x += 8 + PARAMS_BOX_OFFSET_X * 3; y += PARAMS_BOX_H - 7; txt_draws_small("dir", x, y, COL_BG); } break; case 4: { x += 6 + PARAMS_BOX_OFFSET_X * 4; y += PARAMS_BOX_H - 7; txt_draws_small("prob", x, y, COL_BG); } break; case 9: { x += 8 + PARAMS_BOX_OFFSET_X * 4; y += PARAMS_BOX_H - 7 + PARAMS_BOX_OFFSET_Y; txt_draws_small("pan", x, y, COL_BG); } break; default: break; } } } void draw_params_cursor_square(size_t i, u8 clr) { size_t x = PARAMS_START_X + (i % 5) * PARAMS_BOX_OFFSET_X; size_t y = PARAMS_START_Y + PARAMS_BOX_H; if (i >= 5) { y += PARAMS_BOX_OFFSET_Y; } u8 x0 = x; u8 x1 = x + PARAMS_BOX_W; u8 y0 = y - 6; u8 y1 = y; draw_filled_rect(x0 + 1, y0, x1 - 1, y1, clr); draw_line(x0, y0 + 1, x0, y1 - 1, clr); draw_line(x1, y0 + 1, x1, y1 - 1, clr); { size_t x = PARAMS_START_X; size_t y = PARAMS_START_Y; switch (i) { case 0: { x += 4 + PARAMS_BOX_OFFSET_X * 0; y += PARAMS_BOX_H - 7; txt_draws_small("shape", x, y, COL_BG); } break; case 1: { x += 8 + PARAMS_BOX_OFFSET_X * 1; y += PARAMS_BOX_H - 7; txt_draws_small("vol", x, y, COL_BG); } break; case 2: { x += 6 + PARAMS_BOX_OFFSET_X * 2; y += PARAMS_BOX_H - 7; txt_draws_small("time", x, y, COL_BG); } break; case 3: { x += 8 + PARAMS_BOX_OFFSET_X * 3; y += PARAMS_BOX_H - 7; txt_draws_small("dir", x, y, COL_BG); } break; case 4: { x += 6 + PARAMS_BOX_OFFSET_X * 4; y += PARAMS_BOX_H - 7; txt_draws_small("prob", x, y, COL_BG); } break; case 5: { x += 4 + PARAMS_BOX_OFFSET_X * 0; y += PARAMS_BOX_H - 7 + PARAMS_BOX_OFFSET_Y; txt_draws_small("sweep", x, y, COL_BG); } break; case 6: { x += 6 + PARAMS_BOX_OFFSET_X * 1; y += PARAMS_BOX_H - 7 + PARAMS_BOX_OFFSET_Y; txt_draws_small("time", x, y, COL_BG); } break; case 7: { x += 8 + PARAMS_BOX_OFFSET_X * 2; y += PARAMS_BOX_H - 7 + PARAMS_BOX_OFFSET_Y; txt_draws_small("dir", x, y, COL_BG); } break; case 9: { x += 8 + PARAMS_BOX_OFFSET_X * 4; y += PARAMS_BOX_H - 7 + PARAMS_BOX_OFFSET_Y; txt_draws_small("pan", x, y, COL_BG); } break; default: break; } } } void draw_params_cursor(size_t i, u8 clr) { switch (channel_selection_loc) { case 0: { draw_params_cursor_square(i, clr); } break; case 1: { draw_params_cursor_square(i, clr); } break; case 2: { draw_params_cursor_wave(i, clr); } break; case 3: { draw_params_cursor_noise(i, clr); } break; } } IWRAM_CODE void draw_wave_pattern(u8 *pattern, int x, int y, u8 clr) { for (size_t i = 0; i < 16; ++i) { u8 byte = pattern[i]; u8 first = (byte >> 4) & 0xF; u8 second = byte & 0xF; u8 a = x + i * 4; u8 b = y + 16; draw_pixel(a, b - first, clr); draw_pixel(a + 1, b - first, clr); draw_pixel(a + 2, b - second, clr); draw_pixel(a + 3, b - second, clr); } } IWRAM_CODE void clear_parameters(void) { size_t x0 = PARAMS_START_X; size_t y0 = PARAMS_START_Y; size_t x1 = PARAMS_START_X + PARAMS_W; size_t y1 = PARAMS_START_Y + PARAMS_H; draw_filled_rect(x0, y0, x1, y1, COL_BG); } void draw_prob(u8 prob, u8 clr) { size_t x = PARAMS_START_X + PARAMS_BOX_OFFSET_X * 4 + 3; size_t y = PARAMS_START_Y + 5; switch (prob) { case PROB_100: { txt_draws("100", x, y, clr); txt_drawc('%', x + 18, y, clr); } break; case PROB_80: { txt_draws("80", x + 3, y, clr); txt_drawc('%', x + 15, y, clr); } break; case PROB_60: { txt_draws("60", x + 3, y, clr); txt_drawc('%', x + 15, y, clr); } break; case PROB_40: { txt_draws("40", x + 3, y, clr); txt_drawc('%', x + 15, y, clr); } break; case PROB_20: { txt_draws("20", x + 3, y, clr); txt_drawc('%', x + 15, y, clr); } break; case PROB_FIRST: { txt_draws("1st", x + 3, y, clr); } break; case PROB_NOT_FIRST: { txt_draws("1st", x + 3, y, clr); draw_line(x + 3, y - 1, x + 21, y - 1, clr); } break; case PROB_ONE_TWO: { txt_draws("1:2", x + 3, y, clr); } break; case PROB_TWO_TWO: { txt_draws("2:2", x + 3, y, clr); } break; case PROB_ONE_THREE: { txt_draws("1:3", x + 3, y, clr); } break; case PROB_TWO_THREE: { txt_draws("2:3", x + 3, y, clr); } break; case PROB_THREE_THREE: { txt_draws("3:3", x + 3, y, clr); } break; case PROB_ONE_FOUR: { txt_draws("1:4", x + 3, y, clr); } break; case PROB_TWO_FOUR: { txt_draws("2:4", x + 3, y, clr); } break; case PROB_THREE_FOUR: { txt_draws("3:4", x + 3, y, clr); } break; case PROB_FOUR_FOUR: { txt_draws("4:4", x + 3, y, clr); } break; default: break; } } void draw_panning(s8 pan, u8 clr) { switch (settings.sync) { case SYNC_OUT_AUDIO_12BPQ: case SYNC_OUT_AUDIO_6BPQ: case SYNC_OUT_AUDIO_4BPQ: case SYNC_OUT_AUDIO_2BPQ: case SYNC_OUT_LINK_AUDIO_12BPQ: case SYNC_OUT_LINK_AUDIO_6BPQ: case SYNC_OUT_LINK_AUDIO_4BPQ: case SYNC_OUT_LINK_AUDIO_2BPQ: { clr = COL_OFF; } break; default: break; } size_t x = PARAMS_START_X + PARAMS_BOX_OFFSET_X * 4 + 3; size_t y = PARAMS_START_Y + PARAMS_BOX_OFFSET_Y + 5; switch (pan) { case -1: { txt_draws("L", x + 9, y, clr); } break; case 0: { txt_draws("MID", x + 3, y, clr); } break; case 1: { txt_draws("R", x + 9, y, clr); } break; default: break; } } IWRAM_CODE void draw_parameters_wave(ChannelWaveParams *params, bool global) { u8 cols[10] = { COL_FG, COL_FG, COL_FG, COL_FG, COL_FG, COL_FG, COL_FG, COL_FG, COL_FG, COL_FG, }; if (global && input_handler == handle_channel_selection) { for (size_t i = 0; i < 16; i++) { ChannelWaveParams *trig_params = &patterns[pattern_selection_loc].ch3.params[i]; if (params->shape_a != trig_params->shape_a) { cols[0] = COL_OFF; } if (params->type_a != trig_params->type_a) { cols[1] = COL_OFF; } if (params->shape_b != trig_params->shape_b) { cols[2] = COL_OFF; } if (params->type_b != trig_params->type_b) { cols[3] = COL_OFF; } if (params->prob != trig_params->prob) { cols[4] = COL_OFF; } if (params->wave_mode != trig_params->wave_mode) { cols[5] = COL_OFF; } if (params->wave_volume != trig_params->wave_volume) { cols[6] = COL_OFF; } if (params->wave_attack != trig_params->wave_attack) { cols[7] = COL_OFF; } if (params->wave_decay != trig_params->wave_decay) { cols[8] = COL_OFF; } if (params->pan != trig_params->pan) { cols[9] = COL_OFF; } } } // Draw current wave data. { size_t x = PARAMS_START_X; size_t y = PARAMS_START_Y; // Wave Patterns. u8 col_wave_a = COL_OFF; u8 col_wave_b = COL_OFF; if (params->wave_mode == 2) { col_wave_a = cols[0] != COL_OFF && cols[1] != COL_OFF ? COL_ACC_1 : COL_OFF; col_wave_b = cols[2] != COL_OFF && cols[3] != COL_OFF ? COL_ACC_2 : COL_OFF; } else { col_wave_a = cols[0] != COL_OFF && cols[1] != COL_OFF && (params->wave_mode == 0) ? COL_ACC_1 : COL_OFF; col_wave_b = cols[2] != COL_OFF && cols[3] != COL_OFF && (params->wave_mode == 1) ? COL_ACC_1 : COL_OFF; } draw_wave_pattern(waves[params->shape_a][params->type_a], x, y, col_wave_a); draw_wave_pattern(waves[params->shape_b][params->type_b], x + PARAMS_BOX_OFFSET_X * 2, y, col_wave_b); } // Trig probability. draw_prob(params->prob, cols[4]); // Trig pannning. draw_panning(params->pan, cols[9]); // Mode selection. { size_t x = PARAMS_START_X; size_t y = PARAMS_START_Y + PARAMS_BOX_OFFSET_Y; switch (params->wave_mode) { case 0: { txt_draws("A", x + 12, y + 5, cols[5]); } break; case 1: { txt_draws("B", x + 12, y + 5, cols[5]); } break; case 2: { txt_draws("A+B", x + 6, y + 5, cols[5]); } break; } } // Wave volume. { size_t x = PARAMS_START_X + PARAMS_BOX_OFFSET_X + 3; size_t y = PARAMS_START_Y + PARAMS_BOX_OFFSET_Y + 5; switch (params->wave_volume) { case 0: { txt_draws("0", x + 6, y, cols[6]); txt_drawc('%', x + 12, y, cols[6]); } break; case 1: { txt_draws("25", x + 3, y, cols[6]); txt_drawc('%', x + 15, y, cols[6]); } break; case 2: { txt_draws("50", x + 3, y, cols[6]); txt_drawc('%', x + 15, y, cols[6]); } break; case 3: { txt_draws("75", x + 3, y, cols[6]); txt_drawc('%', x + 15, y, cols[6]); } break; case 4: { txt_draws("100", x, y, cols[6]); txt_drawc('%', x + 18, y, cols[6]); } break; } } // Envelope. { if (params->wave_mode == 2) { cols[7] = COL_OFF; cols[8] = COL_OFF; } size_t x = PARAMS_START_X + PARAMS_BOX_OFFSET_X * 2; size_t y = PARAMS_START_Y + 1 + PARAMS_BOX_OFFSET_Y; size_t x0 = x; size_t y0 = y + 15; size_t x1 = x + params->wave_attack * 2; size_t y1 = y; size_t x2 = x + params->wave_attack * 2 + params->wave_decay * 2; size_t y2 = y + 15; u8 col_env = cols[7] != COL_OFF && cols[8] != COL_OFF ? COL_ACC_2 : COL_OFF; if (params->wave_attack != 0) { draw_line(x0, y0, x1, y1, col_env); } if (params->wave_decay == 0) { x2 = x + PARAMS_BOX_OFFSET_X * 2 - 4; y2 = y; draw_line(x1, y1, x2, y2, col_env); } else { draw_line(x1, y1, x2, y2, col_env); draw_line(x2, y2, x + PARAMS_BOX_OFFSET_X * 2 - 4, y2, col_env); } } // Labels. { size_t x = PARAMS_START_X; size_t y = PARAMS_START_Y; txt_draws_small("shape", x + 4 + PARAMS_BOX_OFFSET_X * 0, y + PARAMS_BOX_H - 7, cols[0]); txt_draws_small("type", x + 6 + PARAMS_BOX_OFFSET_X * 1, y + PARAMS_BOX_H - 7, cols[1]); txt_draws_small("shape", x + 4 + PARAMS_BOX_OFFSET_X * 2, y + PARAMS_BOX_H - 7, cols[2]); txt_draws_small("type", x + 6 + PARAMS_BOX_OFFSET_X * 3, y + PARAMS_BOX_H - 7, cols[3]); txt_draws_small("prob", x + 6 + PARAMS_BOX_OFFSET_X * 4, y + PARAMS_BOX_H - 7, cols[4]); y += PARAMS_BOX_OFFSET_Y; txt_draws_small("voice", x + 4 + PARAMS_BOX_OFFSET_X * 0, y + PARAMS_BOX_H - 7, cols[5]); txt_draws_small("vol", x + 8 + PARAMS_BOX_OFFSET_X * 1, y + PARAMS_BOX_H - 7, cols[6]); txt_draws_small("attack", x + 2 + PARAMS_BOX_OFFSET_X * 2, y + PARAMS_BOX_H - 7, cols[7]); txt_draws_small("decay", x + 4 + PARAMS_BOX_OFFSET_X * 3, y + PARAMS_BOX_H - 7, cols[8]); txt_draws_small("pan", x + 8 + PARAMS_BOX_OFFSET_X * 4, y + PARAMS_BOX_H - 7, cols[9]); } } void draw_parameters_square1(ChannelSquare1Params *params, bool global) { u8 cols[10] = { COL_FG, COL_FG, COL_FG, COL_FG, COL_FG, COL_FG, COL_FG, COL_FG, COL_FG, COL_FG, }; // Adjust colors for global trigger parameters. if (global && input_handler == handle_channel_selection) { for (size_t i = 0; i < 16; i++) { ChannelSquare1Params *trig_params; trig_params = &patterns[pattern_selection_loc].ch1.params[i]; if (params->duty_cycle != trig_params->duty_cycle) { cols[0] = COL_OFF; } if (params->env_volume != trig_params->env_volume) { cols[1] = COL_OFF; } if (params->env_time != trig_params->env_time) { cols[2] = COL_OFF; } if (params->env_direction != trig_params->env_direction) { cols[3] = COL_OFF; } if (params->env_direction != trig_params->env_direction) { cols[3] = COL_OFF; } if (params->prob != trig_params->prob) { cols[4] = COL_OFF; } if (params->sweep_number != trig_params->sweep_number) { cols[5] = COL_OFF; } if (params->sweep_time != trig_params->sweep_time) { cols[6] = COL_OFF; } if (params->sweep_direction != trig_params->sweep_direction) { cols[7] = COL_OFF; } if (params->pan != trig_params->pan) { cols[9] = COL_OFF; } } } // Duty cycle / shape. { size_t x = PARAMS_START_X + 3; size_t y = PARAMS_START_Y + 1; size_t x0 = x + 2; size_t x1 = x0; size_t x2 = x0; size_t x3 = x0; size_t x4 = x0; size_t x5 = x0; size_t y0 = y + 14; size_t y1 = y + 2; switch (params->duty_cycle) { case 0: { x1 += 4; x2 += 6; x3 += 13; x4 += 15; x5 += 20; } break; case 1: { x1 += 4; x2 += 7; x3 += 13; x4 += 16; x5 += 20; } break; case 2: { x1 += 3; x2 += 8; x3 += 12; x4 += 17; x5 += 20; } break; case 3: { x1 += 2; x2 += 9; x3 += 11; x4 += 18; x5 += 20; } break; } u8 col_shape = cols[0] != COL_OFF ? COL_ACC_1 : COL_OFF; draw_line(x0, y0, x1, y0, col_shape); draw_line(x1, y1, x1, y0, col_shape); draw_line(x1, y1, x2, y1, col_shape); draw_line(x2, y1, x2, y0, col_shape); draw_line(x2, y0, x3, y0, col_shape); draw_line(x3, y1, x3, y0, col_shape); draw_line(x3, y1, x4, y1, col_shape); draw_line(x4, y1, x4, y0, col_shape); draw_line(x4, y0, x5, y0, col_shape); } // Envelope. { size_t x = PARAMS_START_X + PARAMS_BOX_OFFSET_X * 1 + 1; size_t y = PARAMS_START_Y + 1; size_t x0 = x; size_t y0 = y + 15 - params->env_volume; size_t x1 = x + (3 * PARAMS_BOX_OFFSET_X) * params->env_time / 8 + 7; size_t y1 = params->env_direction == 0 ? y + 15 : y; size_t x2 = x + PARAMS_BOX_OFFSET_X * 3 - 5; size_t y2 = y1; // Env. u8 col_env = cols[1] != COL_OFF && cols[2] != COL_OFF && cols[3] != COL_OFF ? COL_ACC_2 : COL_OFF; if (params->env_time == 0) { draw_line(x0, y0, x2, y0, col_env); } else { draw_line(x0, y0, x1, y1, col_env); draw_line(x1, y1, x2, y2, col_env); } } // Trig probability. draw_prob(params->prob, cols[4]); // Trig pannning. draw_panning(params->pan, cols[9]); // Sweep. { size_t x = PARAMS_START_X; size_t y = PARAMS_START_Y + PARAMS_BOX_OFFSET_Y; // Bounding box. u8 x0 = x + 1; u8 x1 = x + PARAMS_BOX_OFFSET_X * 3 - 5; u8 y0 = y + 2; u8 y1 = y + PARAMS_BOX_H - 8; u8 col_box = cols[5] != COL_OFF && cols[6] != COL_OFF && cols[7] != COL_OFF ? COL_FG : COL_OFF; draw_line(x0, y0, x1, y0, col_box); draw_line(x0, y1, x1, y1, col_box); draw_line(x0 - 1, y0 + 1, x0 - 1, y1 - 1, col_box); draw_line(x1 + 1, y0 + 1, x1 + 1, y1 - 1, col_box); // Number. switch (params->sweep_number) { case 0: { txt_draws("0", x + 12, y + 5, cols[5]); } break; case 1: { txt_draws("1", x + 12, y + 5, cols[5]); } break; case 2: { txt_draws("2", x + 12, y + 5, cols[5]); } break; case 3: { txt_draws("3", x + 12, y + 5, cols[5]); } break; case 4: { txt_draws("4", x + 12, y + 5, cols[5]); } break; case 5: { txt_draws("5", x + 12, y + 5, cols[5]); } break; case 6: { txt_draws("6", x + 12, y + 5, cols[5]); } break; case 7: { txt_draws("7", x + 12, y + 5, cols[5]); } break; } // Time. x += PARAMS_BOX_OFFSET_X; switch (params->sweep_time) { case 0: { txt_draws("Off", x + 6, y + 5, cols[6]); } break; case 1: { txt_draws("7ms", x + 6, y + 5, cols[6]); } break; case 2: { txt_draws("15ms", x + 3, y + 5, cols[6]); } break; case 3: { txt_draws("23ms", x + 3, y + 5, cols[6]); } break; case 4: { txt_draws("31ms", x + 3, y + 5, cols[6]); } break; case 5: { txt_draws("39ms", x + 3, y + 5, cols[6]); } break; case 6: { txt_draws("46ms", x + 3, y + 5, cols[6]); } break; case 7: { txt_draws("54ms", x + 3, y + 5, cols[6]); } break; } // Direction. x += PARAMS_BOX_OFFSET_X; char arr_down[2] = { 0x19, 0 }; char arr_up[2] = { 0x18, 0 }; switch (params->sweep_direction) { case 0: { txt_draws(arr_up, x + 12, y + 5, cols[7]); } break; case 1: { txt_draws(arr_down, x + 12, y + 5, cols[7]); } break; } } // Labels. { size_t x = PARAMS_START_X; size_t y = PARAMS_START_Y; txt_draws_small("shape", x + 4 + PARAMS_BOX_OFFSET_X * 0, y + PARAMS_BOX_H - 7, cols[0]); txt_draws_small("vol", x + 8 + PARAMS_BOX_OFFSET_X * 1, y + PARAMS_BOX_H - 7, cols[1]); txt_draws_small("time", x + 6 + PARAMS_BOX_OFFSET_X * 2, y + PARAMS_BOX_H - 7, cols[2]); txt_draws_small("dir", x + 8 + PARAMS_BOX_OFFSET_X * 3, y + PARAMS_BOX_H - 7, cols[3]); txt_draws_small("prob", x + 6 + PARAMS_BOX_OFFSET_X * 4, y + PARAMS_BOX_H - 7, cols[4]); y += PARAMS_BOX_OFFSET_Y; txt_draws_small("sweep", x + 4 + PARAMS_BOX_OFFSET_X * 0, y + PARAMS_BOX_H - 7, cols[5]); txt_draws_small("time", x + 6 + PARAMS_BOX_OFFSET_X * 1, y + PARAMS_BOX_H - 7, cols[6]); txt_draws_small("dir", x + 8 + PARAMS_BOX_OFFSET_X * 2, y + PARAMS_BOX_H - 7, cols[7]); txt_draws_small("pan", x + 8 + PARAMS_BOX_OFFSET_X * 4, y + PARAMS_BOX_H - 7, cols[9]); } // Empty spacers. { draw_param_stub(8, COL_OFF); } } void draw_parameters_square2(ChannelSquare2Params *params, bool global) { u8 cols[10] = { COL_FG, COL_FG, COL_FG, COL_FG, COL_FG, COL_FG, COL_FG, COL_FG, COL_FG, COL_FG, }; // Adjust colors for global trigger parameters. if (global && input_handler == handle_channel_selection) { for (size_t i = 0; i < 16; i++) { ChannelSquare2Params *trig_params; trig_params = &patterns[pattern_selection_loc].ch2.params[i]; if (params->duty_cycle != trig_params->duty_cycle) { cols[0] = COL_OFF; } if (params->env_volume != trig_params->env_volume) { cols[1] = COL_OFF; } if (params->env_time != trig_params->env_time) { cols[2] = COL_OFF; } if (params->env_direction != trig_params->env_direction) { cols[3] = COL_OFF; } if (params->env_direction != trig_params->env_direction) { cols[3] = COL_OFF; } if (params->prob != trig_params->prob) { cols[4] = COL_OFF; } if (params->pan != trig_params->pan) { cols[9] = COL_OFF; } } } // Duty cycle / shape. { size_t x = PARAMS_START_X + 3; size_t y = PARAMS_START_Y + 1; size_t x0 = x + 2; size_t x1 = x0; size_t x2 = x0; size_t x3 = x0; size_t x4 = x0; size_t x5 = x0; size_t y0 = y + 14; size_t y1 = y + 2; switch (params->duty_cycle) { case 0: { x1 += 4; x2 += 6; x3 += 13; x4 += 15; x5 += 20; } break; case 1: { x1 += 4; x2 += 7; x3 += 13; x4 += 16; x5 += 20; } break; case 2: { x1 += 3; x2 += 8; x3 += 12; x4 += 17; x5 += 20; } break; case 3: { x1 += 2; x2 += 9; x3 += 11; x4 += 18; x5 += 20; } break; } u8 col_shape = cols[0] != COL_OFF ? COL_ACC_1 : COL_OFF; draw_line(x0, y0, x1, y0, col_shape); draw_line(x1, y1, x1, y0, col_shape); draw_line(x1, y1, x2, y1, col_shape); draw_line(x2, y1, x2, y0, col_shape); draw_line(x2, y0, x3, y0, col_shape); draw_line(x3, y1, x3, y0, col_shape); draw_line(x3, y1, x4, y1, col_shape); draw_line(x4, y1, x4, y0, col_shape); draw_line(x4, y0, x5, y0, col_shape); } // Envelope. { size_t x = PARAMS_START_X + PARAMS_BOX_OFFSET_X * 1 + 1; size_t y = PARAMS_START_Y + 1; size_t x0 = x; size_t y0 = y + 15 - params->env_volume; size_t x1 = x + (3 * PARAMS_BOX_OFFSET_X) * params->env_time / 8 + 7; size_t y1 = params->env_direction == 0 ? y + 15 : y; size_t x2 = x + PARAMS_BOX_OFFSET_X * 3 - 5; size_t y2 = y1; // Env. u8 col_env = cols[1] != COL_OFF && cols[2] != COL_OFF && cols[3] != COL_OFF ? COL_ACC_2 : COL_OFF; if (params->env_time == 0) { draw_line(x0, y0, x2, y0, col_env); } else { draw_line(x0, y0, x1, y1, col_env); draw_line(x1, y1, x2, y2, col_env); } } // Trig probability. draw_prob(params->prob, cols[4]); // Trig pannning. draw_panning(params->pan, cols[9]); // Labels. { size_t x = PARAMS_START_X; size_t y = PARAMS_START_Y; txt_draws_small("shape", x + 4 + PARAMS_BOX_OFFSET_X * 0, y + PARAMS_BOX_H - 7, cols[0]); txt_draws_small("vol", x + 8 + PARAMS_BOX_OFFSET_X * 1, y + PARAMS_BOX_H - 7, cols[1]); txt_draws_small("time", x + 6 + PARAMS_BOX_OFFSET_X * 2, y + PARAMS_BOX_H - 7, cols[2]); txt_draws_small("dir", x + 8 + PARAMS_BOX_OFFSET_X * 3, y + PARAMS_BOX_H - 7, cols[3]); txt_draws_small("prob", x + 6 + PARAMS_BOX_OFFSET_X * 4, y + PARAMS_BOX_H - 7, cols[4]); y += PARAMS_BOX_OFFSET_Y; txt_draws_small("pan", x + 8 + PARAMS_BOX_OFFSET_X * 4, y + PARAMS_BOX_H - 7, cols[9]); } // Empty spacers. { draw_param_stub(5, COL_OFF); draw_param_stub(6, COL_OFF); draw_param_stub(7, COL_OFF); draw_param_stub(8, COL_OFF); } } void draw_parameters_noise(ChannelNoiseParams* params, bool global) { u8 cols[10] = { COL_FG, COL_FG, COL_FG, COL_FG, COL_FG, COL_FG, COL_FG, COL_FG, COL_FG, COL_FG, }; if (global && input_handler == handle_channel_selection) { for (size_t i = 0; i < 16; i++) { ChannelNoiseParams *trig_params = &patterns[pattern_selection_loc].ch4.params[i]; if (params->bit_mode != trig_params->bit_mode) { cols[0] = COL_OFF; } if (params->env_volume != trig_params->env_volume) { cols[1] = COL_OFF; } if (params->env_time != trig_params->env_time) { cols[2] = COL_OFF; } if (params->env_direction != trig_params->env_direction) { cols[3] = COL_OFF; } if (params->env_direction != trig_params->env_direction) { cols[3] = COL_OFF; } if (params->prob != trig_params->prob) { cols[4] = COL_OFF; } if (params->pan != trig_params->pan) { cols[9] = COL_OFF; } } } // Mode. { size_t x = PARAMS_START_X; size_t y = PARAMS_START_Y; switch (params->bit_mode) { case 0: { txt_draws("A", x + 12, y + 5, cols[0]); } break; case 1: { txt_draws("B", x + 12, y + 5, cols[0]); } break; } } // Envelope. { size_t x = PARAMS_START_X + PARAMS_BOX_OFFSET_X * 1 + 1; size_t y = PARAMS_START_Y + 1; size_t x0 = x; size_t y0 = y + 15 - params->env_volume; size_t x1 = x + (3 * PARAMS_BOX_OFFSET_X) * params->env_time / 8 + 7; size_t y1 = params->env_direction == 0 ? y + 15 : y; size_t x2 = x + PARAMS_BOX_OFFSET_X * 3 - 5; size_t y2 = y1; // Env. u8 col_env = cols[1] != COL_OFF && cols[2] != COL_OFF && cols[3] != COL_OFF ? COL_ACC_2 : COL_OFF; if (params->env_time == 0) { draw_line(x0, y0, x2, y0, col_env); } else { draw_line(x0, y0, x1, y1, col_env); draw_line(x1, y1, x2, y2, col_env); } } // Trig probability. draw_prob(params->prob, cols[4]); // Trig pannning. draw_panning(params->pan, cols[9]); // Labels. { size_t x = PARAMS_START_X; size_t y = PARAMS_START_Y; txt_draws_small("mode", x + 6 + PARAMS_BOX_OFFSET_X * 0, y + PARAMS_BOX_H - 7, cols[0]); txt_draws_small("vol", x + 8 + PARAMS_BOX_OFFSET_X * 1, y + PARAMS_BOX_H - 7, cols[1]); txt_draws_small("time", x + 6 + PARAMS_BOX_OFFSET_X * 2, y + PARAMS_BOX_H - 7, cols[2]); txt_draws_small("dir", x + 8 + PARAMS_BOX_OFFSET_X * 3, y + PARAMS_BOX_H - 7, cols[3]); txt_draws_small("prob", x + 6 + PARAMS_BOX_OFFSET_X * 4, y + PARAMS_BOX_H - 7, cols[4]); y += PARAMS_BOX_OFFSET_Y; txt_draws_small("pan", x + 8 + PARAMS_BOX_OFFSET_X * 4, y + PARAMS_BOX_H - 7, cols[9]); } // Empty spacers. { draw_param_stub(5, COL_OFF); draw_param_stub(6, COL_OFF); draw_param_stub(7, COL_OFF); draw_param_stub(8, COL_OFF); } } void draw_parameters(void) { clear_parameters(); Pattern *pat = &patterns[pattern_selection_loc]; // DEBUG: Draw the parameter window // draw_rect(PARAMS_START_X, PARAMS_START_Y, PARAMS_START_X + PARAMS_W, PARAMS_START_Y + PARAMS_H, COL_OFF); // DEBUG: Drawing the reference grid overlay // for (size_t i = 0; i < 10; i++) { // draw_param_stub(i, COL_OFF); // } if (input_handler == handle_trigger_selection || input_handler == handle_param_selection_sq1 || input_handler == handle_param_selection_sq2 || input_handler == handle_param_selection_wave || input_handler == handle_param_selection_noise) { if (!pat->empty) { switch (channel_selection_loc) { case 0: { draw_parameters_square1(&pat->ch1.params[trig_selection_loc], false); } break; case 1: { draw_parameters_square2(&pat->ch2.params[trig_selection_loc], false); } break; case 2: { draw_parameters_wave(&pat->ch3.params[trig_selection_loc], false); } break; case 3: { draw_parameters_noise(&pat->ch4.params[trig_selection_loc], false); } break; } } else { switch (channel_selection_loc) { case 0: { draw_parameters_square1(&default_ch1.params, false); } break; case 1: { draw_parameters_square2(&default_ch2.params, false); } break; case 2: { draw_parameters_wave(&default_ch3.params, true); } break; case 3: { draw_parameters_noise(&default_ch4.params, true); } break; } } return; } if (input_handler == handle_channel_selection || input_handler == handle_param_selection_ch1 || input_handler == handle_param_selection_ch2 || input_handler == handle_param_selection_ch3 || input_handler == handle_param_selection_ch4) { switch (channel_selection_loc) { case 0: { draw_parameters_square1(&ch1_params, true); } break; case 1: { draw_parameters_square2(&ch2_params, true); } break; case 2: { draw_parameters_wave(&ch3_params, true); } break; case 3: { draw_parameters_noise(&ch4_params, true); } break; } return; } } void clear_cursors(void) { draw_trig_cursor(last_trig_loc, COL_BG, false); draw_channel_cursor(last_channel_loc, COL_BG, false); draw_pattern_cursor(last_pattern_loc, COL_BG, false); draw_right_col_cursor(last_right_col_loc, COL_BG); for (size_t i = 0; i < 16; i++) { draw_current_step(i, COL_OFF); } } void draw_pattern_chain_cursor(void) { switch (param_selection_loc) { case CHAIN_BTN_ENABLE: { size_t x0 = PAT_TRIG_START_X + 24; size_t x1 = x0 + 30; size_t y = PAT_TRIG_START_Y - 16 + 1 + 10; draw_line(x0, y, x1, y, COL_ACC_0); draw_line(x0, y + 1, x1, y + 1, COL_ACC_0); } break; case CHAIN_BTN_CLEAR: { size_t x0 = PAT_TRIG_START_X + 24 + 42; size_t x1 = x0 + 30; size_t y = PAT_TRIG_START_Y - 16 + 1 + 10; draw_line(x0, y, x1, y, COL_ACC_0); draw_line(x0, y + 1, x1, y + 1, COL_ACC_0); } break; case CHAIN_BTN_RANDOM: { size_t x0 = PAT_TRIG_START_X + 24 + 42 * 2; size_t x1 = x0 + 30; size_t y = PAT_TRIG_START_Y - 16 + 1 + 10; draw_line(x0, y, x1, y, COL_ACC_0); draw_line(x0, y + 1, x1, y + 1, COL_ACC_0); } break; default: { size_t offset_x = (PAT_TRIG_H + 7) * (param_selection_loc % 8); size_t offset_y = param_selection_loc < 8 ? 0 : 0 + PAT_TRIG_OFFSET_Y; size_t x0 = PAT_TRIG_START_X + offset_x; size_t x1 = PAT_TRIG_START_X + offset_x + PAT_TRIG_W; size_t y = PAT_TRIG_START_Y + offset_y + PAT_TRIG_H + 2; draw_line(x0, y, x1, y, COL_ACC_0); draw_line(x0, y + 1, x1, y + 1, COL_ACC_0); } break; } } void draw_cursors(void) { clear_cursors(); draw_current_step(step_counter, COL_ACC_1); if (input_handler == handle_trigger_selection) { draw_trig_cursor(trig_selection_loc, COL_ACC_0, false); } if (input_handler == handle_channel_selection) { draw_channel_cursor(channel_selection_loc, COL_ACC_0, false); } else { draw_channel_cursor(channel_selection_loc, COL_ACC_0, true); } if (input_handler == handle_pattern_selection) { draw_pattern_cursor(pattern_selection_loc, COL_ACC_0, false); } else { draw_pattern_cursor(pattern_selection_loc, COL_ACC_0, true); } if (input_handler == handle_right_col_selection) { draw_right_col_cursor(right_col_selection_loc, COL_ACC_0); } if (input_handler == handle_param_selection_sq1 || input_handler == handle_param_selection_sq2 || input_handler == handle_param_selection_wave || input_handler == handle_param_selection_noise) { draw_params_cursor(param_selection_loc, COL_ACC_0); draw_trig_cursor(trig_selection_loc, COL_ACC_0, true); } if (input_handler == handle_param_selection_ch1 || input_handler == handle_param_selection_ch2 || input_handler == handle_param_selection_ch3 || input_handler == handle_param_selection_ch4) { draw_params_cursor(param_selection_loc, COL_ACC_0); } if (input_handler == handle_pattern_chain) { draw_pattern_chain_cursor(); } } TriggerNote * get_current_trig(void); void draw_piano_notes(void) { draw_piano(); if (input_handler == handle_channel_selection) { // Show note on current channel only. Pattern *pat = &patterns[current_pattern]; if (pat->empty || play_status == 0) { return; } u8 step = step_counter % 16; switch (channel_selection_loc) { case 0: { if (pat->ch1.active && pat->ch1.notes[step].active) { draw_note(pat->ch1.notes[step].note, COL_OFF); } } break; case 1: { if (pat->ch2.active && pat->ch2.notes[step].active) { draw_note(pat->ch2.notes[step].note, COL_OFF); } } break; case 2: { if (pat->ch3.active && pat->ch3.notes[step].active) { draw_note(pat->ch3.notes[step].note, COL_OFF); } } break; } } else if (input_handler == handle_trigger_selection || input_handler == handle_param_selection_sq1 || input_handler == handle_param_selection_sq2 || input_handler == handle_param_selection_wave || input_handler == handle_param_selection_noise) { // Show currently selected trigger note. TriggerNote *trig = get_current_trig(); if (trig->active && !patterns[pattern_selection_loc].empty) { draw_note(trig->note, COL_OFF); } } else { // Show last/current played notes in all channels. if (play_status == 1) { Pattern *pat = &patterns[current_pattern]; if (pat->empty || play_status == 0 || input_handler == handle_right_col_selection) { return; } u8 step = step_counter % 16; if (pat->ch3.active && pat->ch3.notes[step].active) { draw_note(pat->ch3.notes[step].note, COL_OFF); } if (pat->ch2.active && pat->ch2.notes[step].active) { draw_note(pat->ch2.notes[step].note, COL_OFF); } if (pat->ch1.active && pat->ch1.notes[step].active) { draw_note(pat->ch1.notes[step].note, COL_OFF); } } } } void draw_notif_param_edit_prob(u8 prob, u8 x, u8 y, u8 color) { txt_draws_small("PROB/CONDITION: ", x + 2, y + 1, color); switch (prob) { case PROB_100: { txt_draws_small("100", x + 2 + 16 * 4, y + 1, color); txt_drawc_small('%', x + 2 + 19 * 4, y + 1, color); } break; case PROB_80: { txt_draws_small("80", x + 2 + 16 * 4, y + 1, color); txt_drawc_small('%', x + 2 + 18 * 4, y + 1, color); } break; case PROB_60: { txt_draws_small("60", x + 2 + 16 * 4, y + 1, color); txt_drawc_small('%', x + 2 + 18 * 4, y + 1, color); } break; case PROB_40: { txt_draws_small("40", x + 2 + 16 * 4, y + 1, color); txt_drawc_small('%', x + 2 + 18 * 4, y + 1, color); } break; case PROB_20: { txt_draws_small("20", x + 2 + 16 * 4, y + 1, color); txt_drawc_small('%', x + 2 + 18 * 4, y + 1, color); } break; case PROB_FIRST: { txt_draws_small("first bar", x + 2 + 16 * 4, y + 1, color); } break; case PROB_NOT_FIRST: { txt_draws_small("not first bar", x + 2 + 16 * 4, y + 1, color); } break; case PROB_ONE_TWO: { txt_draws_small("1st bar out of 2", x + 2 + 16 * 4, y + 1, color); } break; case PROB_TWO_TWO: { txt_draws_small("2nd bar out of 2", x + 2 + 16 * 4, y + 1, color); } break; case PROB_ONE_THREE: { txt_draws_small("1st bar out of 3", x + 2 + 16 * 4, y + 1, color); } break; case PROB_TWO_THREE: { txt_draws_small("2nd bar out of 3", x + 2 + 16 * 4, y + 1, color); } break; case PROB_THREE_THREE: { txt_draws_small("3rd bar out of 3", x + 2 + 16 * 4, y + 1, color); } break; case PROB_ONE_FOUR: { txt_draws_small("1st bar out of 4", x + 2 + 16 * 4, y + 1, color); } break; case PROB_TWO_FOUR: { txt_draws_small("2nd bar out of 4", x + 2 + 16 * 4, y + 1, color); } break; case PROB_THREE_FOUR: { txt_draws_small("3rd bar out of 4", x + 2 + 16 * 4, y + 1, color); } break; case PROB_FOUR_FOUR: { txt_draws_small("4th bar out of 4", x + 2 + 16 * 4, y + 1, color); } break; } } void draw_notif_param_edit_pan(s8 pan, u8 x, u8 y, u8 color) { txt_draws_small("panning: ", x + 2, y + 1, color); switch (pan) { case -1: { txt_draws_small("left", x + 2 + 9 * 4, y + 1, color); } break; case 0: { txt_draws_small("middle", x + 2 + 9 * 4, y + 1, color); } break; case 1: { txt_draws_small("right", x + 2 + 9 * 4, y + 1, color); } break; } } void draw_notif_param_env_vol(u8 vol, u8 x, u8 y, u8 color) { txt_draws_small("VOL: ", x + 2, y + 1, color); switch (vol) { case 0: { txt_draws_small("0", x + 2 + 5 * 4, y + 1, color); txt_drawc_small('%', x + 2 + 6 * 4, y + 1, color); } break; case 1: { txt_draws_small("6", x + 2 + 5 * 4, y + 1, color); txt_drawc_small('%', x + 2 + 6 * 4, y + 1, color); } break; case 2: { txt_draws_small("13", x + 2 + 5 * 4, y + 1, color); txt_drawc_small('%', x + 2 + 7 * 4, y + 1, color); } break; case 3: { txt_draws_small("20", x + 2 + 5 * 4, y + 1, color); txt_drawc_small('%', x + 2 + 7 * 4, y + 1, color); } break; case 4: { txt_draws_small("26", x + 2 + 5 * 4, y + 1, color); txt_drawc_small('%', x + 2 + 7 * 4, y + 1, color); } break; case 5: { txt_draws_small("33", x + 2 + 5 * 4, y + 1, color); txt_drawc_small('%', x + 2 + 7 * 4, y + 1, color); } break; case 6: { txt_draws_small("40", x + 2 + 5 * 4, y + 1, color); txt_drawc_small('%', x + 2 + 7 * 4, y + 1, color); } break; case 7: { txt_draws_small("46", x + 2 + 5 * 4, y + 1, color); txt_drawc_small('%', x + 2 + 7 * 4, y + 1, color); } break; case 8: { txt_draws_small("53", x + 2 + 5 * 4, y + 1, color); txt_drawc_small('%', x + 2 + 7 * 4, y + 1, color); } break; case 9: { txt_draws_small("60", x + 2 + 5 * 4, y + 1, color); txt_drawc_small('%', x + 2 + 7 * 4, y + 1, color); } break; case 10: { txt_draws_small("66", x + 2 + 5 * 4, y + 1, color); txt_drawc_small('%', x + 2 + 7 * 4, y + 1, color); } break; case 11: { txt_draws_small("73", x + 2 + 5 * 4, y + 1, color); txt_drawc_small('%', x + 2 + 7 * 4, y + 1, color); } break; case 12: { txt_draws_small("80", x + 2 + 5 * 4, y + 1, color); txt_drawc_small('%', x + 2 + 7 * 4, y + 1, color); } break; case 13: { txt_draws_small("86", x + 2 + 5 * 4, y + 1, color); txt_drawc_small('%', x + 2 + 7 * 4, y + 1, color); } break; case 14: { txt_draws_small("93", x + 2 + 5 * 4, y + 1, color); txt_drawc_small('%', x + 2 + 7 * 4, y + 1, color); } break; case 15: { txt_draws_small("100", x + 2 + 5 * 4, y + 1, color); txt_drawc_small('%', x + 2 + 8 * 4, y + 1, color); } break; } } void draw_notif_param_env_time(u8 time, u8 x, u8 y, u8 color) { txt_draws_small("TIME: ", x + 2, y + 1, color); switch (time) { case 0: { txt_draws_small("off", x + 2 + 6 * 4, y + 1, color); } break; case 1: { txt_draws_small("150ms", x + 2 + 6 * 4, y + 1, color); } break; case 2: { txt_draws_small("310ms", x + 2 + 6 * 4, y + 1, color); } break; case 3: { txt_draws_small("460ms", x + 2 + 6 * 4, y + 1, color); } break; case 4: { txt_draws_small("620ms", x + 2 + 6 * 4, y + 1, color); } break; case 5: { txt_draws_small("780ms", x + 2 + 6 * 4, y + 1, color); } break; case 6: { txt_draws_small("930ms", x + 2 + 6 * 4, y + 1, color); } break; case 7: { txt_draws_small("1s", x + 2 + 6 * 4, y + 1, color); } break; } } void draw_notif_param_env_dir(u8 dir, u8 x, u8 y, u8 color) { txt_draws_small("DIRECTION: ", x + 2, y + 1, color); switch (dir) { case 0: { txt_draws_small("down", x + 2 + 11 * 4, y + 1, color); } break; case 1: { txt_draws_small("up", x + 2 + 11 * 4, y + 1, color); } break; } } void draw_notif_bar() { u8 x0 = NOTIF_START_X; u8 y0 = NOTIF_START_Y - 7; u8 x1 = NOTIF_START_X + NOTIF_W; u8 y1 = y0 + NOTIF_H; u8 color = COL_FG; draw_filled_rect(x0, y0, x1, y1, COL_BG); draw_rect(x0, y0, x1, y1, color); if (notif.time > 0) { char msg[32] = {0}; if (notif.time <= 32) { for (s16 i = 0; i < notif.time; i++) { msg[i] = notif.msg[i + 32 - notif.time]; } } else { for (size_t i = 0; i < 32; i++) { msg[i] = notif.msg[i]; } } txt_draws_small(msg, x0 + 2, y0 + 1, color); } else { if (play_status && chain.len != 0 && chain.enabled) { u8 x = x0 + 2; u8 y = y0 + 1; txt_draws_small("CHAIN: ", x, y, color); x += 4 * 7; bool first = true; for (size_t i = 0, k = 0; i < MAX_CHAIN; i++) { if (chain.active[i] != 1) { continue; } if (!first) { txt_drawc_small('-', x + 4 * k++, y, COL_FG); } u8 color = COL_FG; if (i == chain.current) { color = COL_ACC_2; } txt_drawc_small('A' + chain.chain[i], x + 4 * k++, y, color); first = false; } return; } Pattern *pat = &patterns[pattern_selection_loc]; if (input_handler == handle_param_selection_sq1 || input_handler == handle_param_selection_ch1) { ChannelSquare1Params *params; if (input_handler == handle_param_selection_sq1) { params = &pat->ch1.params[trig_selection_loc]; } else { params = &ch1_params; } if (input_handler == handle_param_selection_ch1) { txt_draws_small("(ALL)", x0 + NOTIF_W - 7 * 3 - 3, y0 + 1, color); } switch (param_selection_loc) { case 0: { txt_draws_small("SHAPE: ", x0 + 2, y0 + 1, color); switch (params->duty_cycle) { case 0: { txt_draws_small("12", x0 + 2 + 7 * 4, y0 + 1, color); } break; case 1: { txt_draws_small("25", x0 + 2 + 7 * 4, y0 + 1, color); } break; case 2: { txt_draws_small("50", x0 + 2 + 7 * 4, y0 + 1, color); } break; case 3: { txt_draws_small("75", x0 + 2 + 7 * 4, y0 + 1, color); } break; } txt_drawc_small('%', x0 + 2 + 9 * 4, y0 + 1, color); } break; case 1: { draw_notif_param_env_vol(params->env_volume, x0, y0, color); } break; case 2: { draw_notif_param_env_time(params->env_time, x0, y0, color); } break; case 3: { draw_notif_param_env_dir(params->env_direction, x0, y0, color); } break; case 4: { draw_notif_param_edit_prob(params->prob, x0, y0, color); } break; case 5: { txt_draws_small("SWEEP BITS: ", x0 + 2, y0 + 1, color); switch (params->sweep_number) { case 0: { txt_draws_small("0", x0 + 2 + 12 * 4, y0 + 1, color); } break; case 1: { txt_draws_small("1", x0 + 2 + 12 * 4, y0 + 1, color); } break; case 2: { txt_draws_small("2", x0 + 2 + 12 * 4, y0 + 1, color); } break; case 3: { txt_draws_small("3", x0 + 2 + 12 * 4, y0 + 1, color); } break; case 4: { txt_draws_small("4", x0 + 2 + 12 * 4, y0 + 1, color); } break; case 5: { txt_draws_small("5", x0 + 2 + 12 * 4, y0 + 1, color); } break; case 6: { txt_draws_small("6", x0 + 2 + 12 * 4, y0 + 1, color); } break; case 7: { txt_draws_small("7", x0 + 2 + 12 * 4, y0 + 1, color); } break; } } break; case 6: { txt_draws_small("SWEEP TIME: ", x0 + 2, y0 + 1, color); switch (params->sweep_time) { case 0: { txt_draws_small("off", x0 + 2 + 12 * 4, y0 + 1, color); } break; case 1: { txt_draws_small("7ms", x0 + 2 + 12 * 4, y0 + 1, color); } break; case 2: { txt_draws_small("15ms", x0 + 2 + 12 * 4, y0 + 1, color); } break; case 3: { txt_draws_small("23ms", x0 + 2 + 12 * 4, y0 + 1, color); } break; case 4: { txt_draws_small("31ms", x0 + 2 + 12 * 4, y0 + 1, color); } break; case 5: { txt_draws_small("39ms", x0 + 2 + 12 * 4, y0 + 1, color); } break; case 6: { txt_draws_small("46ms", x0 + 2 + 12 * 4, y0 + 1, color); } break; case 7: { txt_draws_small("54ms", x0 + 2 + 12 * 4, y0 + 1, color); } break; } } break; case 7: { txt_draws_small("SWEEP DIRECTION: ", x0 + 2, y0 + 1, color); switch (params->sweep_direction) { case 0: { txt_draws_small("up", x0 + 2 + 17 * 4, y0 + 1, color); } break; case 1: { txt_draws_small("down", x0 + 2 + 17 * 4, y0 + 1, color); } break; } } break; case 9: { draw_notif_param_edit_pan(params->pan, x0, y0, color); } break; } return; } if (input_handler == handle_param_selection_sq2 || input_handler == handle_param_selection_ch2) { ChannelSquare2Params *params; if (input_handler == handle_param_selection_sq2) { params = &pat->ch2.params[trig_selection_loc]; } else { params = &ch2_params; } switch (param_selection_loc) { case 0: { txt_draws_small("SHAPE: ", x0 + 2, y0 + 1, color); switch (params->duty_cycle) { case 0: { txt_draws_small("12", x0 + 2 + 7 * 4, y0 + 1, color); } break; case 1: { txt_draws_small("25", x0 + 2 + 7 * 4, y0 + 1, color); } break; case 2: { txt_draws_small("50", x0 + 2 + 7 * 4, y0 + 1, color); } break; case 3: { txt_draws_small("75", x0 + 2 + 7 * 4, y0 + 1, color); } break; } txt_drawc_small('%', x0 + 2 + 9 * 4, y0 + 1, color); } break; case 1: { draw_notif_param_env_vol(params->env_volume, x0, y0, color); } break; case 2: { draw_notif_param_env_time(params->env_time, x0, y0, color); } break; case 3: { draw_notif_param_env_dir(params->env_direction, x0, y0, color); } break; case 4: { draw_notif_param_edit_prob(params->prob, x0, y0, color); } break; case 9: { draw_notif_param_edit_pan(params->pan, x0, y0, color); } break; } return; } if (input_handler == handle_param_selection_wave || input_handler == handle_param_selection_ch3) { ChannelWaveParams *params; if (input_handler == handle_param_selection_wave) { params = &pat->ch3.params[trig_selection_loc]; } else { params = &ch3_params; } switch (param_selection_loc) { case 0: { txt_draws_small("SHAPE A: ", x0 + 2, y0 + 1, color); switch (params->shape_a) { case 0: { txt_draws_small("sin", x0 + 2 + 9 * 4, y0 + 1, color); } break; case 1: { txt_draws_small("saw", x0 + 2 + 9 * 4, y0 + 1, color); } break; case 2: { txt_draws_small("square", x0 + 2 + 9 * 4, y0 + 1, color); } break; } } break; case 1: { txt_draws_small("TYPE A: ", x0 + 2, y0 + 1, color); switch (params->type_a) { case 0: { txt_draws_small("a", x0 + 2 + 8 * 4, y0 + 1, color); } break; case 1: { txt_draws_small("b", x0 + 2 + 8 * 4, y0 + 1, color); } break; case 2: { txt_draws_small("c", x0 + 2 + 8 * 4, y0 + 1, color); } break; case 3: { txt_draws_small("d", x0 + 2 + 8 * 4, y0 + 1, color); } break; case 4: { txt_draws_small("e", x0 + 2 + 8 * 4, y0 + 1, color); } break; case 5: { txt_draws_small("f", x0 + 2 + 8 * 4, y0 + 1, color); } break; case 6: { txt_draws_small("g", x0 + 2 + 8 * 4, y0 + 1, color); } break; case 7: { txt_draws_small("h", x0 + 2 + 8 * 4, y0 + 1, color); } break; } } break; case 2: { txt_draws_small("SHAPE B: ", x0 + 2, y0 + 1, color); switch (params->shape_b) { case 0: { txt_draws_small("sin", x0 + 2 + 9 * 4, y0 + 1, color); } break; case 1: { txt_draws_small("saw", x0 + 2 + 9 * 4, y0 + 1, color); } break; case 2: { txt_draws_small("square", x0 + 2 + 9 * 4, y0 + 1, color); } break; } } break; case 3: { txt_draws_small("TYPE B: ", x0 + 2, y0 + 1, color); switch (params->type_b) { case 0: { txt_draws_small("a", x0 + 2 + 8 * 4, y0 + 1, color); } break; case 1: { txt_draws_small("b", x0 + 2 + 8 * 4, y0 + 1, color); } break; case 2: { txt_draws_small("c", x0 + 2 + 8 * 4, y0 + 1, color); } break; case 3: { txt_draws_small("d", x0 + 2 + 8 * 4, y0 + 1, color); } break; case 4: { txt_draws_small("e", x0 + 2 + 8 * 4, y0 + 1, color); } break; case 5: { txt_draws_small("f", x0 + 2 + 8 * 4, y0 + 1, color); } break; case 6: { txt_draws_small("g", x0 + 2 + 8 * 4, y0 + 1, color); } break; case 7: { txt_draws_small("h", x0 + 2 + 8 * 4, y0 + 1, color); } break; } } break; case 4: { draw_notif_param_edit_prob(params->prob, x0, y0, color); } break; case 5: { txt_draws_small("VOICE: ", x0 + 2, y0 + 1, color); switch (params->wave_mode) { case 0: { txt_draws_small("A", x0 + 2 + 7 * 4, y0 + 1, color); } break; case 1: { txt_draws_small("B", x0 + 2 + 7 * 4, y0 + 1, color); } break; case 2: { txt_draws_small("A + B", x0 + 2 + 7 * 4, y0 + 1, color); } break; } } break; case 6: { txt_draws_small("VOLUME: ", x0 + 2, y0 + 1, color); switch (params->wave_volume) { case 0: { txt_draws_small("0", x0 + 2 + 8 * 4, y0 + 1, color); txt_drawc_small('%', x0 + 2 + 9 * 4, y0 + 1, color); } break; case 1: { txt_draws_small("25", x0 + 2 + 8 * 4, y0 + 1, color); txt_drawc_small('%', x0 + 2 + 10 * 4, y0 + 1, color); } break; case 2: { txt_draws_small("50", x0 + 2 + 8 * 4, y0 + 1, color); txt_drawc_small('%', x0 + 2 + 10 * 4, y0 + 1, color); } break; case 3: { txt_draws_small("75", x0 + 2 + 8 * 4, y0 + 1, color); txt_drawc_small('%', x0 + 2 + 10 * 4, y0 + 1, color); } break; case 4: { txt_draws_small("100", x0 + 2 + 8 * 4, y0 + 1, color); txt_drawc_small('%', x0 + 2 + 11 * 4, y0 + 1, color); } break; } } break; case 7: { if (params->wave_attack) { txt_drawf_small("ATTACK: %d", x0 + 2, y0 + 1, color, params->wave_attack); } else { txt_draws_small("ATTACK: OFF", x0 + 2, y0 + 1, color, params->wave_decay); } } break; case 8: { if (params->wave_decay) { txt_drawf_small("DECAY: %d", x0 + 2, y0 + 1, color, params->wave_decay); } else { txt_draws_small("DECAY: OFF", x0 + 2, y0 + 1, color, params->wave_decay); } } break; case 9: { draw_notif_param_edit_pan(params->pan, x0, y0, color); } break; } return; } if (input_handler == handle_param_selection_noise || input_handler == handle_param_selection_ch4) { ChannelNoiseParams *params; if (input_handler == handle_param_selection_noise) { params = &pat->ch4.params[trig_selection_loc]; } else { params = &ch4_params; } switch (param_selection_loc) { case 0: { txt_draws_small("MODE: ", x0 + 2, y0 + 1, color); switch (params->bit_mode) { case 0: { txt_draws_small("a", x0 + 2 + 6 * 4, y0 + 1, color); } break; case 1: { txt_draws_small("b", x0 + 2 + 6 * 4, y0 + 1, color); } break; } } break; case 1: { draw_notif_param_env_vol(params->env_volume, x0, y0, color); } break; case 2: { draw_notif_param_env_time(params->env_time, x0, y0, color); } break; case 3: { draw_notif_param_env_dir(params->env_direction, x0, y0, color); } break; case 4: { draw_notif_param_edit_prob(params->prob, x0, y0, color); } break; case 9: { draw_notif_param_edit_pan(params->pan, x0, y0, color); } break; } return; } if (settings.help == TOGGLE_ON) { if (input_handler == handle_trigger_selection) { if (key_pressed(KEY_B) && ctrl.key_b > 10) { txt_draws_small("D-PAD:NUDGE TRIGGER", x0 + 2, y0 + 1, color); } else if (key_pressed(KEY_SELECT)) { txt_draws_small("SEL+B:COPY TRIG SEL+A:PASTE TRIG", x0 + 2, y0 + 1, color); } else { txt_draws_small("L/R:NOTE SEL+L/R:OCTAVE A:PARAMS B:TOGGLE", x0 + 2, y0 + 1, color); } } else if (input_handler == handle_channel_selection) { if (key_pressed(KEY_SELECT)) { txt_draws_small("SEL+B:COPY CHANNEL SEL+A:PASTE CHANNEL", x0 + 2, y0 + 1, color); } else { txt_draws_small("L/R:NOTE SEL+L/R:OCTAVE A:PARAMS B:TOGGLE", x0 + 2, y0 + 1, color); } } else if (input_handler == handle_param_selection_sq1 || input_handler == handle_param_selection_sq2 || input_handler == handle_param_selection_wave || input_handler == handle_param_selection_noise) { txt_draws_small("L/R:ADJUST SELECT:COPY PARAMETERS", x0 + 2, y0 + 1, color); } else if (input_handler == handle_pattern_selection) { if (key_pressed(KEY_SELECT)) { txt_draws_small("SEL+B:COPY PATTERN SEL+A:PASTE PATTERN", x0 + 2, y0 + 1, color); } else { txt_draws_small("L/R:CHAIN A:PARAMS B:QUEUE SEL+L+R:CLEAR", x0 + 2, y0 + 1, color); } } else if (input_handler == handle_pattern_chain) { switch (param_selection_loc) { case CHAIN_BTN_ENABLE: { txt_draws_small("A:BACK B:TOGGLE CHAIN", x0 + 2, y0 + 1, color); } break; case CHAIN_BTN_CLEAR: { txt_draws_small("A:BACK B:CLEAR CHAIN", x0 + 2, y0 + 1, color); } break; case CHAIN_BTN_RANDOM: { txt_draws_small("A:BACK B:RANDOMIZE CHAIN", x0 + 2, y0 + 1, color); } break; default: { txt_draws_small("L/R:CHANGE PATTERN A:BACK B:TOGGLE STEP", x0 + 2, y0 + 1, color); } break; } } else if (input_handler == handle_right_col_selection) { if (right_col_selection_loc == R_COL_STOP) { txt_draws_small("B:STOP", x0 + 2, y0 + 1, color); } else if (right_col_selection_loc == R_COL_PLAY) { if (play_status == 0) { txt_draws_small("B:PLAY", x0 + 2, y0 + 1, color); } else { txt_draws_small("B:PAUSE", x0 + 2, y0 + 1, color); } } else if (right_col_selection_loc == R_COL_BPM) { if (key_pressed(KEY_SELECT)) { int bpm; if (settings.global_bpm) { bpm = settings.bpm; } else { bpm = patterns[pattern_selection_loc].bpm; } txt_drawf_small("TEMPO: %d bpm", x0 + 2, y0 + 1, color, bpm); } else { txt_draws_small("L/R:TEMPO (+1) SEL+L/R:TEMPO (+10)", x0 + 2, y0 + 1, color); } } else if (right_col_selection_loc == R_COL_SETTINGS) { txt_draws_small("B:SETTINGS", x0 + 2, y0 + 1, color); } else if (right_col_selection_loc == R_COL_SCALE) { if (key_pressed(KEY_SELECT)) { const char *roots[12] = { "C ", "C#", "D ", "D#", "E ", "F ", "F#", "G ", "G#", "A ", "A#", "B ", }; txt_drawf_small("ROOT: %s SCALE: %s", x0 + 2, y0 + 1, color, roots[current_scale_root], scale_long[current_scale]); } else { txt_draws_small("L/R:SCALE SEL+L/R:ROOT NOTE", x0 + 2, y0 + 1, color); } } else if (right_col_selection_loc == R_COL_BANK_A || right_col_selection_loc == R_COL_BANK_B || right_col_selection_loc == R_COL_BANK_C || right_col_selection_loc == R_COL_BANK_D || right_col_selection_loc == R_COL_BANK_E || right_col_selection_loc == R_COL_BANK_F) { if (settings.auto_save) { txt_draws_small("B:SAVE CURRENT BANK AND SWITCH", x0 + 2, y0 + 1, color); } else { txt_draws_small("B:SWITCH BANK", x0 + 2, y0 + 1, color); } } } return; } if (input_handler == handle_right_col_selection) { switch (right_col_selection_loc) { case R_COL_BANK_A: { txt_draws_small("BANK A", x0 + 2, y0 + 1, color); } break; case R_COL_BANK_B: { txt_draws_small("BANK B", x0 + 2, y0 + 1, color); } break; case R_COL_BANK_C: { txt_draws_small("BANK C", x0 + 2, y0 + 1, color); } break; case R_COL_BANK_D: { txt_draws_small("BANK D", x0 + 2, y0 + 1, color); } break; case R_COL_BANK_E: { txt_draws_small("BANK E", x0 + 2, y0 + 1, color); } break; case R_COL_BANK_F: { txt_draws_small("BANK F", x0 + 2, y0 + 1, color); } break; case R_COL_STOP: { txt_draws_small("STOP", x0 + 2, y0 + 1, color); } break; case R_COL_PLAY: { if (play_status == 0) { txt_draws_small("PLAY", x0 + 2, y0 + 1, color); } else { txt_draws_small("PAUSE", x0 + 2, y0 + 1, color); } } break; case R_COL_SETTINGS: { txt_draws_small("SETTINGS", x0 + 2, y0 + 1, color); } break; case R_COL_SCALE: { const char *roots[12] = { "C ", "C#", "D ", "D#", "E ", "F ", "F#", "G ", "G#", "A ", "A#", "B ", }; txt_drawf_small("ROOT: %s SCALE: %s", x0 + 2, y0 + 1, color, roots[current_scale_root], scale_long[current_scale]); } break; case R_COL_BPM: { int bpm; if (settings.global_bpm) { bpm = settings.bpm; } else { bpm = patterns[pattern_selection_loc].bpm; } txt_drawf_small("TEMPO: %d bpm", x0 + 2, y0 + 1, color, bpm); } break; } return; } if (play_status == 0) { txt_draws_small("STOPPED", x0 + 2, y0 + 1, color); } else { txt_draws_small("PLAYING", x0 + 2, y0 + 1, color); } } } void draw_pattern_chain() { clear_parameters(); // Pattern chain triggers. for (size_t i = 0; i < 16; i++) { u8 color = COL_FG; size_t offset_x = PAT_TRIG_OFFSET_X * (i % 8); size_t offset_y = i < 8 ? 0 : 0 + PAT_TRIG_OFFSET_Y; size_t x0 = PAT_TRIG_START_X + offset_x; size_t x1 = PAT_TRIG_START_X + offset_x + PAT_TRIG_W; size_t y0 = PAT_TRIG_START_Y + offset_y; size_t y1 = PAT_TRIG_START_Y + offset_y + PAT_TRIG_H; if (!chain.enabled) { color = COL_OFF; } draw_rect(x0, y0, x1, y1, color); if (chain.active[i]) { txt_drawc('A' + chain.chain[i], x0 + 4, y0 + 3, color); } color = COL_OFF; if (chain.current == i && chain.len != 0 && chain.enabled) { color = COL_ACC_2; if (chain.active[i]) { txt_drawc('A' + chain.chain[i], x0 + 4, y0 + 3, color); } draw_rect(x0, y0, x1, y1, color); } draw_line(x0 + 5, y1 - 2, x1 - 5, y1 - 2, color); } // Pattern chain buttons. size_t x0 = PAT_TRIG_START_X + 24; size_t x1 = x0 + 30; size_t y0 = PAT_TRIG_START_Y - 16 + 1; size_t y1 = y0 + 8; txt_draws_small("toggle", x0 + 2, y0, COL_FG); draw_rect(x0, y0, x1, y1, COL_FG); x0 += 42; x1 += 42; txt_draws_small("clear", x0 + 4, y0, COL_FG); draw_rect(x0, y0, x1, y1, COL_FG); x0 += 42; x1 += 42; txt_draws_small("random", x0 + 2, y0, COL_FG); draw_rect(x0, y0, x1, y1, COL_FG); }