// // Channel render functions. // void draw_channel_sprite(size_t x, size_t y, u8 clr, u8 idx) { draw_icn(x, y, &ch_btn_sprite[0 + 6 * idx], clr, 0, 0); draw_icn(x + 8, y, &ch_btn_sprite[2 + 6 * idx], clr, 0, 0); draw_icn(x + 16, y, &ch_btn_sprite[4 + 6 * idx], clr, 0, 0); } 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; 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; } 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) { 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; switch (settings.cursor) { case CURSOR_THICK_LINE: { draw_line(x0, y, x1, y, clr); draw_line(x0, y + 1, x1, y + 1, clr); } break; default: { draw_line(x0, y, x1, y, clr); } break; } } // // 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 + 1; 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) { 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; size_t x1 = TRIG_START_X + TRIG_W + offset_x; size_t y = TRIG_START_Y + TRIG_H + offset_y; switch (settings.cursor) { case CURSOR_THICK_LINE: { draw_line(x0, y, x1, y, clr); draw_line(x0, y + 1, x1, y + 1, clr); } break; default: { draw_line(x0, y, x1, y, clr); } break; } } void draw_right_col_cursor(int i, u8 clr) { size_t x0 = 0; size_t x1 = 0; size_t y = 0; switch (i) { case R_COL_BPM: { x0 = BPM_START_X; x1 = x0 + R_COL_W; y = BPM_START_Y + BPM_H + 0; } break; case R_COL_STOP: { x0 = STOP_START_X; x1 = x0 + PLAY_STOP_W; y = STOP_START_Y + PLAY_STOP_H + 2; } break; case R_COL_PLAY: { x0 = PLAY_START_X; x1 = x0 + PLAY_STOP_W; y = PLAY_START_Y + PLAY_STOP_H + 2; } break; case R_COL_SETTINGS: { x0 = SETTINGS_START_X + 2; x1 = x0 + R_COL_W - 4; y = SETTINGS_START_Y + PLAY_STOP_H + 2; } break; case R_COL_BANK_A: { x0 = BANK_START_X; x1 = x0 + PAT_W; y = BANK_START_Y + PAT_H + 2; } break; case R_COL_BANK_B: { x0 = BANK_START_X; x1 = x0 + PAT_W; y = BANK_START_Y + PAT_H + 2 + 1 * PAT_OFFSET_Y; } break; case R_COL_BANK_C: { x0 = BANK_START_X; x1 = x0 + PAT_W; y = BANK_START_Y + PAT_H + 2 + 2 * PAT_OFFSET_Y; } break; case R_COL_BANK_D: { x0 = BANK_START_X; x1 = x0 + PAT_W; y = BANK_START_Y + PAT_H + 2 + 3 * PAT_OFFSET_Y; } break; } switch (settings.cursor) { case CURSOR_THICK_LINE: { draw_line(x0, y, x1, y, clr); draw_line(x0, y + 1, x1, y + 1, clr); } break; default: { draw_line(x0, y, x1, y, clr); } break; } } 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 + 3 + 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; size_t y = BANK_START_Y; txt_drawf_small("BANK", x - 2, y - 10, COL_FG); char bank_names[] = { 'A', 'B', 'C', 'D', }; for (int i = 0; i < 4; i++) { int color = COL_OFF; if (i == current_bank) { color = COL_FG; } draw_filled_rect(x, y, x + PAT_W, y + PAT_H, COL_BG); draw_rect(x, y, x + PAT_W, y + PAT_H, color); txt_drawc(bank_names[i], x + 4, y + 1, color); y += PAT_OFFSET_Y; } } void draw_pattern_buttons() { // Clear patterns. size_t x = PAT_START_X; size_t y = PAT_START_Y; draw_filled_rect(x, y, x + PAT_W, y + PAT_H * 8, COL_BG); txt_drawf_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_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) { 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; switch (settings.cursor) { case CURSOR_THICK_LINE: { draw_line(x0, y, x1, y, clr); draw_line(x0, y + 1, x1, y + 1, clr); } break; default: { draw_line(x0, y, x1, y, clr); } break; } } 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 + 3, x_btn, y + 7, COL_ACC_2); draw_filled_rect(x_btn + 2, y + 3, x_btn + 3, y + 7, COL_ACC_2); } else { // Play button x += 1; draw_line(x_btn + 0, y + 2, x_btn + 0, y + 8, COL_ACC_2); draw_line(x_btn + 1, y + 3, x_btn + 1, y + 7, COL_ACC_2); draw_line(x_btn + 2, y + 4, x_btn + 2, y + 6, COL_ACC_2); draw_line(x_btn + 3, y + 5, x_btn + 3, y + 5, COL_ACC_2); } } void draw_settings() { size_t x = SETTINGS_START_X; size_t y = SETTINGS_START_Y; draw_rect(x + 2, y, x + R_COL_W - 2, y + PLAY_STOP_H, COL_OFF); draw_line(x + 6, y + 4, x + 9, y + 4, COL_OFF); draw_line(x + 6, y + 6, x + 9, y + 6, COL_OFF); draw_line(x + 15, y + 4, x + 18, y + 4, COL_OFF); draw_line(x + 15, y + 6, x + 18, y + 6, COL_OFF); draw_line(x + 9, y + 5, x + 15, y + 5, COL_OFF); } 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 + 3, x_btn + 4, y + 7, COL_ACC_1); draw_settings(); } 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, y + BPM_H - 4, COL_BG); draw_rect(x, y, x + R_COL_W, y + BPM_H - 4, COL_FG); draw_line(x + 5, y, x + 19, y, COL_BG); txt_drawf_small("BPM", x + 5, y - 4, COL_FG); // Make sure its horizontally centered if only 2 digits int bpm = patterns[pattern_selection_loc].bpm; if (bpm >= 100) { txt_drawf("%d", x + 3, y + 5, COL_FG, bpm); } else { txt_drawf("%d", x + 6, y + 5, COL_FG, bpm); } } 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; 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); 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 x0 = 0; size_t y0 = 0; size_t x1 = 0; size_t y1 = 0; switch (value) { // White notes. case 0:{ x0 = PIANO_START_X + 2 + octave * 28; x1 = x0 + 1; y0 = PIANO_START_Y + 2; y1 = PIANO_START_Y - 2 + PIANO_H; draw_filled_rect(x0, y0, x1, y1, clr); x0 = PIANO_START_X + 2 + octave * 28 + 2; x1 = x0; y0 = y0 + 9; draw_filled_rect(x0, y0, x1, y1, clr); } break; case 2:{ x0 = PIANO_START_X + 2 + octave * 28 + 5; x1 = x0; y0 = PIANO_START_Y + 2; y1 = PIANO_START_Y - 2 + 12; draw_filled_rect(x0, y0, x1, y1, clr); x0 = PIANO_START_X + 2 + octave * 28 + 4; x1 = x0 + 2; y0 = PIANO_START_Y - 2 + 13; y1 = y0 + 7; draw_filled_rect(x0, y0, x1, y1, clr); } break; case 4:{ x0 = PIANO_START_X + 2 + octave * 28 + 9; x1 = x0 + 1; y0 = PIANO_START_Y + 2; y1 = PIANO_START_Y - 2 + 12; draw_filled_rect(x0, y0, x1, y1, clr); x0 = PIANO_START_X + 2 + octave * 28 + 8; x1 = x0 + 2; y0 = PIANO_START_Y - 2 + 13; y1 = y0 + 7; draw_filled_rect(x0, y0, x1, y1, clr); } break; case 5:{ x0 = PIANO_START_X + 2 + octave * 28 + 12; x1 = x0 + 1; y0 = PIANO_START_Y + 2; y1 = PIANO_START_Y - 2 + PIANO_H; draw_filled_rect(x0, y0, x1, y1, clr); x0 = PIANO_START_X + 2 + octave * 28 + 14; x1 = x0; y0 = y0 + 9; draw_filled_rect(x0, y0, x1, y1, clr); } break; case 7:{ x0 = PIANO_START_X + 2 + octave * 28 + 17; x1 = x0; y0 = PIANO_START_Y + 2; y1 = PIANO_START_Y - 2 + 12; draw_filled_rect(x0, y0, x1, y1, clr); x0 = PIANO_START_X + 2 + octave * 28 + 16; x1 = x0 + 2; y0 = PIANO_START_Y - 2 + 13; y1 = y0 + 7; draw_filled_rect(x0, y0, x1, y1, clr); } break; case 9:{ x0 = PIANO_START_X + 2 + octave * 28 + 21; x1 = x0; y0 = PIANO_START_Y + 2; y1 = PIANO_START_Y - 2 + 12; draw_filled_rect(x0, y0, x1, y1, clr); x0 = PIANO_START_X + 2 + octave * 28 + 20; x1 = x0 + 2; y0 = PIANO_START_Y - 2 + 13; y1 = y0 + 7; draw_filled_rect(x0, y0, x1, y1, clr); } break; case 11: { x0 = PIANO_START_X + 2 + octave * 28 + 25; x1 = x0 + 1; y0 = PIANO_START_Y + 2; y1 = PIANO_START_Y - 2 + 12; draw_filled_rect(x0, y0, x1, y1, clr); x0 = PIANO_START_X + 2 + octave * 28 + 24; x1 = x0 + 2; y0 = PIANO_START_Y - 2 + 13; y1 = y0 + 7; draw_filled_rect(x0, y0, x1, y1, clr); } break; default: { if (clr == COL_FG) { clr = COL_BG; } y0 = PIANO_START_Y + 2; y1 = PIANO_START_Y - 2 + 11; switch (value) { case 1: { x0 = PIANO_START_X + 2 + octave * 28 + 3; } break; case 3: { x0 = PIANO_START_X + 2 + octave * 28 + 7; } break; case 6: { x0 = PIANO_START_X + 2 + octave * 28 + 15; } break; case 8: { x0 = PIANO_START_X + 2 + octave * 28 + 19; } break; case 10: { x0 = PIANO_START_X + 2 + 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++) { draw_note(i, COL_FG); } } void draw_params_cursor_wave(size_t i, u8 clr) { u8 x_positions[] = { // 32 half bytes (Wave A). 0, 4, 8, 12, 16, 20, 24, 28, 34, 38, 42, 46, 50, 54, 58, 62, 0, 4, 8, 12, 16, 20, 24, 28, 34, 38, 42, 46, 50, 54, 58, 62, // 32 half bytes (Wave B). 70, 74, 78, 82, 86, 90, 94, 98, 104, 108, 112, 116, 120, 124, 128, 132, 70, 74, 78, 82, 86, 90, 94, 98, 104, 108, 112, 116, 120, 124, 128, 132, // Default wave A. 1, 18, 35, 52, // Default wave B. 71, 88, 105, 122, // Mode selection. 141, // Volume selection. 141, }; u8 y_positions[] = { // 32 half bytes (Wave A) 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, // 32 half bytes (Wave B) 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, // Default wave A. 21, 21, 21, 21, // Default wave B. 21, 21, 21, 21, // Mode selection. 21, // Volume selection. 1, }; size_t cursor_length = 0; if (i < 64) { cursor_length = 4; } else if (i < 72) { cursor_length = 13; } else { cursor_length = 30; } size_t x = PARAMS_START_X + x_positions[i] - 1; size_t y = PARAMS_START_Y + PARAMS_H - 25 + y_positions[i]; switch (settings.cursor) { case CURSOR_THICK_LINE: { draw_line(x, y, x + cursor_length, y, clr); draw_line(x, y + 1, x + cursor_length, y + 1, clr); } break; default: { draw_line(x, y, x + cursor_length, y, clr); } break; } } void draw_params_cursor_noise(size_t i, u8 clr) { u8 x_positions[] = { 0, // Bit mode. 31, // Env. Vol. 59, // Env. Direction. 87, // Env. Time. }; u8 y_positions[] = { 20, // Bit mode. 20, // Env. Vol. 20, // Env. Direction. 20, // Env. Time. }; size_t cursor_length = 24; size_t x = PARAMS_START_X + x_positions[i] + 30; size_t y = PARAMS_START_Y + PARAMS_H - 24 + y_positions[i]; switch (settings.cursor) { case CURSOR_THICK_LINE: { draw_line(x, y, x + cursor_length, y, clr); draw_line(x, y + 1, x + cursor_length, y + 1, clr); } break; default: { draw_line(x, y, x + cursor_length, y, clr); } break; } } void draw_params_cursor_square(size_t i, u8 clr, bool sweep) { size_t x_offset = sweep ? 0 : 30; u8 x_positions[] = { 0, // Duty. 31, // Env. Vol. 59, // Env. Direction. 87, // Env. Time. 118, // Sweep Number. 146, // Sweep Time. 132, // Sweep Direction. }; u8 y_positions[] = { 20, // Duty. 20, // Env. Vol. 20, // Env. Direction. 20, // Env. Time. 20, // Sweep Number. 20, // Sweep Time. 0, // Sweep Direction. }; size_t cursor_length = 24; size_t x = PARAMS_START_X + x_positions[i] + x_offset; size_t y = PARAMS_START_Y + PARAMS_H - 24 + y_positions[i]; switch (settings.cursor) { case CURSOR_THICK_LINE: { draw_line(x, y, x + cursor_length, y, clr); draw_line(x, y + 1, x + cursor_length, y + 1, clr); } break; default: { draw_line(x, y, x + cursor_length, y, clr); } break; } } void draw_params_cursor(size_t i, u8 clr) { switch (channel_selection_loc) { case 0: { draw_params_cursor_square(i, clr, true); } break; case 1: { draw_params_cursor_square(i, clr, false); } 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 -1; size_t y0 = PARAMS_START_Y; size_t x1 = PARAMS_START_X + PARAMS_W; size_t y1 = PARAMS_START_Y + PARAMS_H - 1; draw_filled_rect(x0, y0, x1, y1, COL_BG); } IWRAM_CODE void draw_parameters_wave(ChannelWaveParams *params, bool global) { u8 col_fg = COL_FG; if (global && input_handler == handle_channel_selection) { col_fg = COL_OFF; } // Draw current wave data. { u8 *wave_a = params->wave_a; u8 *wave_b = params->wave_b; size_t x = PARAMS_START_X; size_t y = PARAMS_START_Y + 12; // Wave Patterns. draw_wave_pattern(wave_a, x, y + 1, COL_ACC_1); draw_wave_pattern(wave_b, x + 70, y + 1, COL_ACC_2); // Wave text. x -= 2; txt_drawf_small("%02x%02x%02x%02x", x, y + 19, col_fg, wave_a[0], wave_a[1], wave_a[2], wave_a[3]); txt_drawf_small("%02x%02x%02x%02x", x + 34, y + 19, col_fg, wave_a[4], wave_a[5], wave_a[6], wave_a[7]); txt_drawf_small("%02x%02x%02x%02x", x, y + 28, col_fg, wave_a[8], wave_a[9], wave_a[10], wave_a[11]); txt_drawf_small("%02x%02x%02x%02x", x + 34, y + 28, col_fg, wave_a[12], wave_a[13], wave_a[14], wave_a[15]); x += 70; txt_drawf_small("%02x%02x%02x%02x", x, y + 19, col_fg, wave_b[0], wave_b[1], wave_b[2], wave_b[3]); txt_drawf_small("%02x%02x%02x%02x", x + 34, y + 19, col_fg, wave_b[4], wave_b[5], wave_b[6], wave_b[7]); txt_drawf_small("%02x%02x%02x%02x", x, y + 28, col_fg, wave_b[8], wave_b[9], wave_b[10], wave_b[11]); txt_drawf_small("%02x%02x%02x%02x", x + 34, y + 28, col_fg, wave_b[12], wave_b[13], wave_b[14], wave_b[15]); } // Draw default wave buttons. { const u32 *tile = default_wave_buttons; size_t x = PARAMS_START_X; size_t y = PARAMS_START_Y + PARAMS_H - 13; for (size_t i = 0, k = 0; i < 4 * 2; i += 2, k++) { draw_icn(x + 17 * k , y, &tile[i * 2 + 0], col_fg, 1, 0); draw_icn(x + 17 * k + 8, y, &tile[i * 2 + 2], col_fg, 1, 0); } for (size_t i = 0, k = 0; i < 4 * 2; i += 2, k++) { draw_icn(x + 70 + 17 * k , y, &tile[i * 2 + 0], col_fg, 1, 0); draw_icn(x + 70 + 17 * k + 8, y, &tile[i * 2 + 2], col_fg, 1, 0); } } // Mode selection. { size_t x = PARAMS_START_X + 140; size_t y = PARAMS_START_Y + PARAMS_H - 23; draw_line(x, y + 4, x + 5, y + 4, col_fg); draw_line(x + 25, y + 4, x + 30, y + 4, col_fg); draw_line(x, y + 5, x, y + 16, col_fg); draw_line(x + 30, y + 5, x + 30, y + 17, col_fg); draw_line(x, y + 17, x + 30, y + 17, col_fg); txt_drawf_small("mode", x + 6, y, col_fg); switch (params->wave_mode) { case 0: { txt_drawf("A", x + 12, y + 7, col_fg); } break; case 1: { txt_drawf("B", x + 12, y + 7, col_fg); } break; case 2: { txt_drawf("A+B", x + 6, y + 7, col_fg); } break; } } // Wave volume. { size_t x = PARAMS_START_X + 140; size_t y = PARAMS_START_Y + PARAMS_H - 46; draw_line(x, y + 7, x + 7, y + 7, col_fg); draw_line(x + 23, y + 7, x + 30, y + 7, col_fg); draw_line(x, y + 8, x, y + 19, col_fg); draw_line(x + 30, y + 8, x + 30, y + 19, col_fg); draw_line(x, y + 20, x + 30, y + 20, col_fg); txt_drawf_small("vol", x + 8, y + 3, col_fg); switch (params->wave_volume) { case 0: { txt_drawf("0", x + 12, y + 10, col_fg); } break; case 1: { txt_drawf("25", x + 9, y + 10, col_fg); } break; case 2: { txt_drawf("50", x + 9, y + 10, col_fg); } break; case 3: { txt_drawf("75", x + 9, y + 10, col_fg); } break; case 4: { txt_drawf("100", x + 6, y + 10, col_fg); } break; } } } void draw_parameters_square(ChannelSquareParams *params, bool sweep, bool global) { size_t x_offset = sweep ? 0 : 30; u8 col_fg = COL_FG; if (global && input_handler == handle_channel_selection) { col_fg = COL_OFF; } // Duty cycle. { // Shape drawing. { size_t x = PARAMS_START_X + x_offset; size_t y = PARAMS_START_Y + PARAMS_H - 47; 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; } draw_line(x0, y0, x1, y0, COL_ACC_1); draw_line(x1, y1, x1, y0, COL_ACC_1); draw_line(x1, y1, x2, y1, COL_ACC_1); draw_line(x2, y1, x2, y0, COL_ACC_1); draw_line(x2, y0, x3, y0, COL_ACC_1); draw_line(x3, y1, x3, y0, COL_ACC_1); draw_line(x3, y1, x4, y1, COL_ACC_1); draw_line(x4, y1, x4, y0, COL_ACC_1); draw_line(x4, y0, x5, y0, COL_ACC_1); // Bounding box. draw_rect(x, y - 3, x + 24, y + 18, COL_ACC_1); } // Param box. { size_t x = PARAMS_START_X + x_offset; size_t y = PARAMS_START_Y + PARAMS_H - 26; draw_line(x, y + 7, x + 2, y + 7, col_fg); draw_line(x + 22, y + 7, x + 24, y + 7, col_fg); draw_line(x, y + 8, x, y + 19, col_fg); draw_line(x + 24, y + 8, x + 24, y + 19, col_fg); draw_line(x, y + 20, x + 24, y + 20, col_fg); txt_drawf_small("duty", x + 3, y + 3, col_fg); switch (params->duty_cycle) { case 0: { txt_drawf("12", x + 6, y + 10, col_fg); } break; case 1: { txt_drawf("25", x + 6, y + 10, col_fg); } break; case 2: { txt_drawf("50", x + 6, y + 10, col_fg); } break; case 3: { txt_drawf("75", x + 6, y + 10, col_fg); } break; } } } // Envelope. { // Env. drawing. { // Bounding box. { size_t x0 = PARAMS_START_X + 31 + x_offset; size_t y0 = PARAMS_START_Y + PARAMS_H - 50; size_t x1 = x0 + 79; size_t y1 = y0 + 21; draw_rect(x0, y0, x1, y1, COL_ACC_2); } size_t x = PARAMS_START_X + 42 + x_offset; size_t y = PARAMS_START_Y + PARAMS_H - 47; size_t x0 = x; size_t y0 = y + 15 - params->env_volume; size_t x1 = x + 8 * params->env_time; size_t y1 = params->env_direction == 0 ? y + 15 : y; size_t x2 = x + 8 * 7 + 1; size_t y2 = y1; // Env. if (params->env_time == 0) { draw_line(x1, y0, x2, y0, COL_ACC_2); } else { draw_line(x0, y0, x1, y1, COL_ACC_2); draw_line(x1, y1, x2, y2, COL_ACC_2); } } // Env. volume. { size_t x = PARAMS_START_X + 31 + x_offset; size_t y = PARAMS_START_Y + PARAMS_H - 26; draw_line(x, y + 7, x + 4, y + 7, col_fg); draw_line(x + 20, y + 7, x + 24, y + 7, col_fg); draw_line(x, y + 8, x, y + 19, col_fg); draw_line(x + 24, y + 8, x + 24, y + 19, col_fg); draw_line(x, y + 20, x + 24, y + 20, col_fg); txt_drawf_small("vol", x + 5, y + 3, col_fg); switch (params->env_volume) { case 0: { txt_drawf("0", x + 9, y + 10, col_fg); } break; case 1: { txt_drawf("6", x + 9, y + 10, col_fg); } break; case 2: { txt_drawf("13", x + 6, y + 10, col_fg); } break; case 3: { txt_drawf("20", x + 6, y + 10, col_fg); } break; case 4: { txt_drawf("26", x + 6, y + 10, col_fg); } break; case 5: { txt_drawf("33", x + 6, y + 10, col_fg); } break; case 6: { txt_drawf("40", x + 6, y + 10, col_fg); } break; case 7: { txt_drawf("46", x + 6, y + 10, col_fg); } break; case 8: { txt_drawf("53", x + 6, y + 10, col_fg); } break; case 9: { txt_drawf("60", x + 6, y + 10, col_fg); } break; case 10: { txt_drawf("66", x + 6, y + 10, col_fg); } break; case 11: { txt_drawf("73", x + 6, y + 10, col_fg); } break; case 12: { txt_drawf("80", x + 6, y + 10, col_fg); } break; case 13: { txt_drawf("86", x + 6, y + 10, col_fg); } break; case 14: { txt_drawf("93", x + 6, y + 10, col_fg); } break; case 15: { txt_drawf("100", x + 3, y + 10, col_fg); } break; } } // Env. direction { size_t x = PARAMS_START_X + 59 + x_offset; size_t y = PARAMS_START_Y + PARAMS_H - 26; draw_line(x, y + 7, x + 4, y + 7, col_fg); draw_line(x + 20, y + 7, x + 24, y + 7, col_fg); draw_line(x, y + 8, x, y + 19, col_fg); draw_line(x + 24, y + 8, x + 24, y + 19, col_fg); draw_line(x, y + 20, x + 24, y + 20, col_fg); txt_drawf_small("dir", x + 5, y + 3, col_fg); char arr_up[2] = { 0x19, 0 }; char arr_down[2] = { 0x18, 0 }; switch (params->env_direction) { case 0: { txt_drawf(arr_up, x + 9, y + 11, col_fg); } break; case 1: { txt_drawf(arr_down, x + 9, y + 11, col_fg); } break; } } // Env. time. { size_t x = PARAMS_START_X + 87 + x_offset; size_t y = PARAMS_START_Y + PARAMS_H - 26; draw_line(x, y + 7, x + 2, y + 7, col_fg); draw_line(x + 22, y + 7, x + 24, y + 7, col_fg); draw_line(x, y + 8, x, y + 19, col_fg); draw_line(x + 24, y + 8, x + 24, y + 19, col_fg); draw_line(x, y + 20, x + 24, y + 20, col_fg); txt_drawf_small("time", x + 3, y + 3, col_fg); switch (params->env_time) { case 0: { txt_drawf("0", x + 9, y + 10, col_fg); } break; case 1: { txt_drawf("14", x + 6, y + 10, col_fg); } break; case 2: { txt_drawf("28", x + 6, y + 10, col_fg); } break; case 3: { txt_drawf("42", x + 6, y + 10, col_fg); } break; case 4: { txt_drawf("57", x + 6, y + 10, col_fg); } break; case 5: { txt_drawf("71", x + 6, y + 10, col_fg); } break; case 6: { txt_drawf("85", x + 6, y + 10, col_fg); } break; case 7: { txt_drawf("100", x + 3, y + 10, col_fg); } break; } } } // Sweep number. if (sweep) { size_t x = PARAMS_START_X + 118; size_t y = PARAMS_START_Y + PARAMS_H - 26; draw_line(x, y + 7, x + 4, y + 7, col_fg); draw_line(x + 20, y + 7, x + 24, y + 7, col_fg); draw_line(x, y + 8, x, y + 19, col_fg); draw_line(x + 24, y + 8, x + 24, y + 19, col_fg); draw_line(x, y + 20, x + 24, y + 20, col_fg); txt_drawf_small("num", x + 5, y + 3, col_fg); switch (params->sweep_number) { case 0: { txt_drawf("0", x + 9, y + 10, col_fg); } break; case 1: { txt_drawf("1", x + 9, y + 10, col_fg); } break; case 2: { txt_drawf("2", x + 9, y + 10, col_fg); } break; case 3: { txt_drawf("3", x + 9, y + 10, col_fg); } break; case 4: { txt_drawf("4", x + 9, y + 10, col_fg); } break; case 5: { txt_drawf("5", x + 9, y + 10, col_fg); } break; case 6: { txt_drawf("6", x + 9, y + 10, col_fg); } break; case 7: { txt_drawf("7", x + 9, y + 10, col_fg); } break; } } // Sweep time. if (sweep) { size_t x = PARAMS_START_X + 146; size_t y = PARAMS_START_Y + PARAMS_H - 26; draw_line(x, y + 7, x + 2, y + 7, col_fg); draw_line(x + 22, y + 7, x + 24, y + 7, col_fg); draw_line(x, y + 8, x, y + 19, col_fg); draw_line(x + 24, y + 8, x + 24, y + 19, col_fg); draw_line(x, y + 20, x + 24, y + 20, col_fg); txt_drawf_small("time", x + 3, y + 3, col_fg); switch (params->sweep_time) { case 0: { txt_drawf("0", x + 9, y + 10, col_fg); } break; case 1: { txt_drawf("1", x + 9, y + 10, col_fg); } break; case 2: { txt_drawf("2", x + 9, y + 10, col_fg); } break; case 3: { txt_drawf("3", x + 9, y + 10, col_fg); } break; case 4: { txt_drawf("4", x + 9, y + 10, col_fg); } break; case 5: { txt_drawf("5", x + 9, y + 10, col_fg); } break; case 6: { txt_drawf("6", x + 9, y + 10, col_fg); } break; case 7: { txt_drawf("7", x + 9, y + 10, col_fg); } break; } } // Sweep direction. if (sweep) { size_t x = PARAMS_START_X + 132; size_t y = PARAMS_START_Y + PARAMS_H - 46; draw_line(x, y + 7, x + 4, y + 7, col_fg); draw_line(x + 20, y + 7, x + 24, y + 7, col_fg); draw_line(x, y + 8, x, y + 19, col_fg); draw_line(x + 24, y + 8, x + 24, y + 19, col_fg); draw_line(x, y + 20, x + 24, y + 20, col_fg); txt_drawf_small("dir", x + 5, y + 3, col_fg); char arr_up[2] = { 0x19, 0 }; char arr_down[2] = { 0x18, 0 }; switch (params->sweep_direction) { case 0: { txt_drawf(arr_up, x + 9, y + 11, col_fg); } break; case 1: { txt_drawf(arr_down, x + 9, y + 11, col_fg); } break; } } // Labels. { size_t x = PARAMS_START_X + x_offset; size_t y = PARAMS_START_Y + PARAMS_H - 48; txt_drawf_small("shape", x + 1, y - 12, col_fg); txt_drawf_small("envelope", x + 54, y - 12, col_fg); if (sweep) { txt_drawf_small("sweep", x + 133, y - 12, col_fg); } } } void draw_parameters_noise(ChannelNoiseParams* params, bool global) { size_t x_offset = 30; u8 col_fg = COL_FG; if (global && input_handler == handle_channel_selection) { col_fg = COL_OFF; } // Bit mode. { // Param box. { size_t x = PARAMS_START_X + x_offset; size_t y = PARAMS_START_Y + PARAMS_H - 26; draw_line(x, y + 7, x + 2, y + 7, col_fg); draw_line(x + 22, y + 7, x + 24, y + 7, col_fg); draw_line(x, y + 8, x, y + 19, col_fg); draw_line(x + 24, y + 8, x + 24, y + 19, col_fg); draw_line(x, y + 20, x + 24, y + 20, col_fg); txt_drawf_small("mode", x + 3, y + 3, col_fg); switch (params->bit_mode) { case 0: { txt_drawf("A", x + 9, y + 10, col_fg); } break; case 1: { txt_drawf("B", x + 9, y + 10, col_fg); } break; } } } // Envelope. { // Env. drawing. { // Bounding box. { size_t x0 = PARAMS_START_X + 31 + x_offset; size_t y0 = PARAMS_START_Y + PARAMS_H - 50; size_t x1 = x0 + 79; size_t y1 = y0 + 21; draw_rect(x0, y0, x1, y1, COL_ACC_2); } size_t x = PARAMS_START_X + 42 + x_offset; size_t y = PARAMS_START_Y + PARAMS_H - 47; size_t x0 = x; size_t y0 = y + 15 - params->env_volume; size_t x1 = x + 8 * params->env_time; size_t y1 = params->env_direction == 0 ? y + 15 : y; size_t x2 = x + 8 * 7 + 1; size_t y2 = y1; // Env. if (params->env_time == 0) { draw_line(x1, y0, x2, y0, COL_ACC_2); } else { draw_line(x0, y0, x1, y1, COL_ACC_2); draw_line(x1, y1, x2, y2, COL_ACC_2); } } // Env. volume. { size_t x = PARAMS_START_X + 31 + x_offset; size_t y = PARAMS_START_Y + PARAMS_H - 26; draw_line(x, y + 7, x + 4, y + 7, col_fg); draw_line(x + 20, y + 7, x + 24, y + 7, col_fg); draw_line(x, y + 8, x, y + 19, col_fg); draw_line(x + 24, y + 8, x + 24, y + 19, col_fg); draw_line(x, y + 20, x + 24, y + 20, col_fg); txt_drawf_small("vol", x + 5, y + 3, col_fg); switch (params->env_volume) { case 0: { txt_drawf("0", x + 9, y + 10, col_fg); } break; case 1: { txt_drawf("6", x + 9, y + 10, col_fg); } break; case 2: { txt_drawf("13", x + 6, y + 10, col_fg); } break; case 3: { txt_drawf("20", x + 6, y + 10, col_fg); } break; case 4: { txt_drawf("26", x + 6, y + 10, col_fg); } break; case 5: { txt_drawf("33", x + 6, y + 10, col_fg); } break; case 6: { txt_drawf("40", x + 6, y + 10, col_fg); } break; case 7: { txt_drawf("46", x + 6, y + 10, col_fg); } break; case 8: { txt_drawf("53", x + 6, y + 10, col_fg); } break; case 9: { txt_drawf("60", x + 6, y + 10, col_fg); } break; case 10: { txt_drawf("66", x + 6, y + 10, col_fg); } break; case 11: { txt_drawf("73", x + 6, y + 10, col_fg); } break; case 12: { txt_drawf("80", x + 6, y + 10, col_fg); } break; case 13: { txt_drawf("86", x + 6, y + 10, col_fg); } break; case 14: { txt_drawf("93", x + 6, y + 10, col_fg); } break; case 15: { txt_drawf("100", x + 3, y + 10, col_fg); } break; } } // Env. direction { size_t x = PARAMS_START_X + 59 + x_offset; size_t y = PARAMS_START_Y + PARAMS_H - 26; draw_line(x, y + 7, x + 4, y + 7, col_fg); draw_line(x + 20, y + 7, x + 24, y + 7, col_fg); draw_line(x, y + 8, x, y + 19, col_fg); draw_line(x + 24, y + 8, x + 24, y + 19, col_fg); draw_line(x, y + 20, x + 24, y + 20, col_fg); txt_drawf_small("dir", x + 5, y + 3, col_fg); char arr_up[2] = { 0x19, 0 }; char arr_down[2] = { 0x18, 0 }; switch (params->env_direction) { case 0: { txt_drawf(arr_up, x + 9, y + 11, col_fg); } break; case 1: { txt_drawf(arr_down, x + 9, y + 11, col_fg); } break; } } // Env. time. { size_t x = PARAMS_START_X + 87 + x_offset; size_t y = PARAMS_START_Y + PARAMS_H - 26; draw_line(x, y + 7, x + 2, y + 7, col_fg); draw_line(x + 22, y + 7, x + 24, y + 7, col_fg); draw_line(x, y + 8, x, y + 19, col_fg); draw_line(x + 24, y + 8, x + 24, y + 19, col_fg); draw_line(x, y + 20, x + 24, y + 20, col_fg); txt_drawf_small("time", x + 3, y + 3, col_fg); switch (params->env_time) { case 0: { txt_drawf("0", x + 9, y + 10, col_fg); } break; case 1: { txt_drawf("14", x + 6, y + 10, col_fg); } break; case 2: { txt_drawf("28", x + 6, y + 10, col_fg); } break; case 3: { txt_drawf("42", x + 6, y + 10, col_fg); } break; case 4: { txt_drawf("57", x + 6, y + 10, col_fg); } break; case 5: { txt_drawf("71", x + 6, y + 10, col_fg); } break; case 6: { txt_drawf("85", x + 6, y + 10, col_fg); } break; case 7: { txt_drawf("100", x + 3, y + 10, col_fg); } break; } } } // Labels. { size_t x = PARAMS_START_X + x_offset; size_t y = PARAMS_START_Y + PARAMS_H - 48; txt_drawf_small("envelope", x + 54, y - 12, col_fg); } } void draw_parameters(void) { clear_parameters(); Pattern *pat = &patterns[pattern_selection_loc]; 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) { switch (channel_selection_loc) { case 0: { draw_parameters_square(&pat->ch1.params[trig_selection_loc], true, false); } break; case 1: { draw_parameters_square(&pat->ch2.params[trig_selection_loc], false, 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; } 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_square(&ch1_params, true, true); } break; case 1: { draw_parameters_square(&ch2_params, false, 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); draw_channel_cursor(last_channel_loc, COL_BG); draw_pattern_cursor(last_pattern_loc, COL_BG); 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_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); } if (input_handler == handle_channel_selection) { draw_channel_cursor(channel_selection_loc, COL_ACC_0); } else { draw_channel_cursor(channel_selection_loc, COL_OFF); } if (input_handler == handle_pattern_selection) { draw_pattern_cursor(pattern_selection_loc, COL_ACC_0); } else { draw_pattern_cursor(pattern_selection_loc, COL_OFF); } 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_OFF); } 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); } } 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]; u8 step = (step_counter - 1) % 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(); draw_note(trig->note, COL_OFF); } else { // Show last/current played notes in all channels. if (play_status == 1) { Pattern *pat = &patterns[current_pattern]; u8 step = (step_counter - 1) % 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); } } } }