From ea2c7552dd414422bfe23117dc8440f4183611c7 Mon Sep 17 00:00:00 2001 From: Bad Diode Date: Sat, 29 Jul 2023 20:56:09 +0200 Subject: Add per-trig panning support --- src/drawing.c | 72 +++++++++++++++++++++++++++++++++++++++++++++-- src/gba/gba.h | 17 +++++++++++ src/main.c | 4 ++- src/sequencer.c | 87 +++++++++++++++++++++++++++++++++++++++++++++++++++++---- 4 files changed, 171 insertions(+), 9 deletions(-) (limited to 'src') diff --git a/src/drawing.c b/src/drawing.c index a48a1ea..3ed294e 100644 --- a/src/drawing.c +++ b/src/drawing.c @@ -515,6 +515,11 @@ draw_params_cursor_wave(size_t i, u8 clr) { y += PARAMS_BOX_H - 7 + PARAMS_BOX_OFFSET_Y; txt_drawf_small("vol", x, y, COL_BG); } break; + case 9: { + x += 8 + PARAMS_BOX_OFFSET_X * 4; + y += PARAMS_BOX_H - 7 + PARAMS_BOX_OFFSET_Y; + txt_drawf_small("pan", x, y, COL_BG); + } break; default: break; } } @@ -563,6 +568,11 @@ draw_params_cursor_noise(size_t i, u8 clr) { y += PARAMS_BOX_H - 7; txt_drawf_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_drawf_small("pan", x, y, COL_BG); + } break; default: break; } } @@ -626,6 +636,11 @@ draw_params_cursor_square(size_t i, u8 clr) { y += PARAMS_BOX_H - 7 + PARAMS_BOX_OFFSET_Y; txt_drawf_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_drawf_small("pan", x, y, COL_BG); + } break; default: break; } } @@ -710,6 +725,33 @@ draw_prob(u8 prob, u8 clr) { } } +void +draw_panning(s8 pan, u8 clr) { + switch (settings.sync) { + case SYNC_OUT_LINK_AUDIO_4: + case SYNC_OUT_LINK_AUDIO_8: + case SYNC_OUT_LINK_AUDIO_16: + case SYNC_OUT_AUDIO_4: + case SYNC_OUT_AUDIO_8: + case SYNC_OUT_AUDIO_16: { 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_drawf("L", x + 9, y, clr); + } break; + case 0: { + txt_drawf("MID", x + 3, y, clr); + } break; + case 1: { + txt_drawf("R", x + 9, y, clr); + } break; + default: break; + } +} + IWRAM_CODE void draw_parameters_wave(ChannelWaveParams *params, bool global) { @@ -731,6 +773,9 @@ draw_parameters_wave(ChannelWaveParams *params, bool global) { // Trig probability. draw_prob(params->prob, col_fg); + // Trig pannning. + draw_panning(params->pan, col_fg); + // Mode selection. { size_t x = PARAMS_START_X; @@ -782,13 +827,13 @@ draw_parameters_wave(ChannelWaveParams *params, bool global) { y += PARAMS_BOX_OFFSET_Y; txt_drawf_small("mode", x + 6 + PARAMS_BOX_OFFSET_X * 0, y + PARAMS_BOX_H - 7, col_fg); txt_drawf_small("vol", x + 8 + PARAMS_BOX_OFFSET_X * 1, y + PARAMS_BOX_H - 7, col_fg); + txt_drawf_small("pan", x + 8 + PARAMS_BOX_OFFSET_X * 4, y + PARAMS_BOX_H - 7, col_fg); } // Empty spacers. { draw_param_stub(7, COL_OFF); draw_param_stub(8, COL_OFF); - draw_param_stub(9, COL_OFF); } } @@ -877,6 +922,9 @@ draw_parameters_square(ChannelSquareParams *params, bool sweep, bool global) { // Trig probability. draw_prob(params->prob, col_fg); + // Trig pannning. + draw_panning(params->pan, col_fg); + // Sweep. if (sweep) { size_t x = PARAMS_START_X; @@ -942,6 +990,7 @@ draw_parameters_square(ChannelSquareParams *params, bool sweep, bool global) { txt_drawf_small("time", x + 6 + PARAMS_BOX_OFFSET_X * 1, y + PARAMS_BOX_H - 7, col_fg); txt_drawf_small("dir", x + 8 + PARAMS_BOX_OFFSET_X * 2, y + PARAMS_BOX_H - 7, col_fg); } + txt_drawf_small("pan", x + 8 + PARAMS_BOX_OFFSET_X * 4, y + PARAMS_BOX_H - 7, col_fg); } // Empty spacers. @@ -952,7 +1001,6 @@ draw_parameters_square(ChannelSquareParams *params, bool sweep, bool global) { draw_param_stub(7, COL_OFF); } draw_param_stub(8, COL_OFF); - draw_param_stub(9, COL_OFF); } } @@ -996,6 +1044,9 @@ draw_parameters_noise(ChannelNoiseParams* params, bool global) { // Trig probability. draw_prob(params->prob, col_fg); + // Trig pannning. + draw_panning(params->pan, col_fg); + // Labels. { size_t x = PARAMS_START_X; @@ -1005,6 +1056,8 @@ draw_parameters_noise(ChannelNoiseParams* params, bool global) { txt_drawf_small("time", x + 6 + PARAMS_BOX_OFFSET_X * 2, y + PARAMS_BOX_H - 7, col_fg); txt_drawf_small("dir", x + 8 + PARAMS_BOX_OFFSET_X * 3, y + PARAMS_BOX_H - 7, col_fg); txt_drawf_small("prob", x + 6 + PARAMS_BOX_OFFSET_X * 4, y + PARAMS_BOX_H - 7, col_fg); + y += PARAMS_BOX_OFFSET_Y; + txt_drawf_small("pan", x + 8 + PARAMS_BOX_OFFSET_X * 4, y + PARAMS_BOX_H - 7, col_fg); } // Empty spacers. @@ -1013,7 +1066,6 @@ draw_parameters_noise(ChannelNoiseParams* params, bool global) { draw_param_stub(6, COL_OFF); draw_param_stub(7, COL_OFF); draw_param_stub(8, COL_OFF); - draw_param_stub(9, COL_OFF); } } @@ -1198,6 +1250,16 @@ draw_notif_param_edit_prob(u8 prob, u8 x, u8 y, u8 color) { } } +void +draw_notif_param_edit_pan(s8 pan, u8 x, u8 y, u8 color) { + txt_drawf_small("panning: ", x + 2, y + 1, color); + switch (pan) { + case -1: { txt_drawf_small("left", x + 2 + 9 * 4, y + 1, color); } break; + case 0: { txt_drawf_small("middle", x + 2 + 9 * 4, y + 1, color); } break; + case 1: { txt_drawf_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_drawf_small("VOL: ", x + 2, y + 1, color); @@ -1324,6 +1386,7 @@ draw_notif_bar() { case 1: { txt_drawf_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; } @@ -1351,6 +1414,7 @@ draw_notif_bar() { 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; } @@ -1425,6 +1489,7 @@ draw_notif_bar() { case 4: { txt_drawf_small("100", x0 + 2 + 8 * 4, y0 + 1, color); txt_drawc_small('%', x0 + 2 + 11 * 4, y0 + 1, color); } break; } } break; + case 9: { draw_notif_param_edit_pan(params->pan, x0, y0, color); } break; } return; } @@ -1449,6 +1514,7 @@ draw_notif_bar() { 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; } diff --git a/src/gba/gba.h b/src/gba/gba.h index 74b6942..8ba3de5 100644 --- a/src/gba/gba.h +++ b/src/gba/gba.h @@ -524,6 +524,23 @@ sound_volume(SoundChannel channels, u8 volume, u8 pan) { return left | right; } +inline u16 dmg_stereo_vol(u8 vol) { + return vol | (vol << 0x4); +} + +inline u16 +channel_vol(SoundChannel channel, s8 pan) { + u16 left = (channel << 0xc); + u16 right = (channel << 0x8); + if (pan == -1) { + return left; + } + if (pan == +1) { + return right; + } + return left | right; +} + // Sound Direct Sound master bits. #define SOUND_DMG25 0x0 #define SOUND_DMG50 0x1 diff --git a/src/main.c b/src/main.c index 28e5e6b..aa559c7 100644 --- a/src/main.c +++ b/src/main.c @@ -17,7 +17,7 @@ WITH REGARD TO THIS SOFTWARE. // + Change cursor behaviour for new parameter pages // + Allow prob control (% based or 1:2, etc.) // + Display notification when editing a parameter with the highest priority -// - Add panning support. +// + Add panning support. // - Add custom user themes // - Animations for cursor movement/current step highlight. (A fade out maybe?) // @@ -132,6 +132,8 @@ render(void) { render_sequencer(); } break; } + // DEBUG: Ensuring the saved data don't exceed SRAM size (32k). + // txt_drawf("SIZE: %lu", 0, 0, COL_ACC_1, sizeof(Metadata) + sizeof(patterns) * 4 + sizeof(chain) * 4); } void diff --git a/src/sequencer.c b/src/sequencer.c index 1b9787f..a3bdf3f 100644 --- a/src/sequencer.c +++ b/src/sequencer.c @@ -98,6 +98,8 @@ should_play(u8 prob) { void play_step(void) { + bool update_sound = false; + static s8 pan[4] = {0}; Pattern *pat = &patterns[current_pattern]; if (current_pattern != next_pattern && step_counter == 15) { current_pattern = next_pattern; @@ -144,6 +146,10 @@ play_step(void) { asm("nop"); asm("nop"); SOUND_SQUARE1_FREQ = SOUND_FREQ_RESET | sound_rates[trig->note]; } + if (pan[0] != params->pan) { + update_sound = true; + } + pan[0] = params->pan; } } else { SOUND_SQUARE1_SWEEP = 0; @@ -164,6 +170,10 @@ play_step(void) { | SOUND_SQUARE_ENV_TIME(params->env_time) | SOUND_SQUARE_ENV_DIR(params->env_direction) | SOUND_SQUARE_DUTY(params->duty_cycle); + if (pan[1] != params->pan) { + update_sound = true; + } + pan[1] = params->pan; } } else { SOUND_SQUARE2_FREQ = 0; @@ -206,6 +216,10 @@ play_step(void) { case 3: { SOUND_WAVE_CTRL = SOUND_WAVE_VOL_75; } break; case 4: { SOUND_WAVE_CTRL = SOUND_WAVE_VOL_100; } break; } + if (pan[2] != params->pan) { + update_sound = true; + } + pan[2] = params->pan; } else { // NOTE: Maybe in the future we want an AD, but for now this is // probably better than holding the notes. Or perhaps add a hold @@ -249,6 +263,10 @@ play_step(void) { | SOUND_NOISE_ENV_TIME(params->env_time) | SOUND_NOISE_ENV_DIR(params->env_direction); } + if (pan[3] != params->pan) { + update_sound = true; + } + pan[3] = params->pan; } else { SOUND_NOISE_FREQ = 0; SOUND_NOISE_CTRL = 0; @@ -266,6 +284,25 @@ play_step(void) { case SYNC_OUT_LINK_AUDIO_4: { if (step_counter % 4 == 0) { gate_on(); audio_sync_click = true; } } break; default: break; } + + // Adjust per-channel panning. + switch (settings.sync) { + case SYNC_OUT_LINK_AUDIO_4: + case SYNC_OUT_LINK_AUDIO_8: + case SYNC_OUT_LINK_AUDIO_16: + case SYNC_OUT_AUDIO_4: + case SYNC_OUT_AUDIO_8: + case SYNC_OUT_AUDIO_16: break; + default: { + if (update_sound) { + SOUND_DMG_MASTER = dmg_stereo_vol(3) + | channel_vol(SOUND_SQUARE1, pan[0]) + | channel_vol(SOUND_SQUARE2, pan[1]) + | channel_vol(SOUND_WAVE, pan[2]) + | channel_vol(SOUND_NOISE, pan[3]); + } + } break; + } if (step_counter == 15) { bar_counter++; } @@ -759,8 +796,10 @@ set_param_selection_sq1(ChannelSquareParams *params, InputHandler return_handler if (key_tap(KEY_RIGHT)) { if (param_selection_loc == 4) { param_selection_loc = 0; - } else if (param_selection_loc == 7) { + } else if (param_selection_loc == 9) { param_selection_loc = 5; + } else if (param_selection_loc == 7) { + param_selection_loc = 9; } else if (param_selection_loc < 7) { param_selection_loc++; } @@ -768,6 +807,8 @@ set_param_selection_sq1(ChannelSquareParams *params, InputHandler return_handler if (param_selection_loc == 0) { param_selection_loc = 4; } else if (param_selection_loc == 5) { + param_selection_loc = 9; + } else if (param_selection_loc == 9) { param_selection_loc = 7; } else if (param_selection_loc > 0) { param_selection_loc--; @@ -778,7 +819,11 @@ set_param_selection_sq1(ChannelSquareParams *params, InputHandler return_handler } if (key_tap(KEY_UP) || key_tap(KEY_DOWN)) { - if (param_selection_loc < 3) { + if (param_selection_loc == 4) { + param_selection_loc = 9; + } else if (param_selection_loc == 9) { + param_selection_loc = 4; + } else if (param_selection_loc < 3) { param_selection_loc += 5; } else if (param_selection_loc > 4) { param_selection_loc -= 5; @@ -812,6 +857,7 @@ set_param_selection_sq1(ChannelSquareParams *params, InputHandler return_handler case 5: { params->sweep_number = CLAMP(params->sweep_number + inc, 0, 7); } break; case 6: { params->sweep_time = CLAMP(params->sweep_time + inc, 0, 7); } break; case 7: { params->sweep_direction ^= 1; } break; + case 9: { params->pan = CLAMP(params->pan + inc, -1, 1); } break; } redraw_params = true; return true; @@ -844,7 +890,7 @@ set_param_selection_sq2(ChannelSquareParams *params, InputHandler return_handler param_selection_loc = 4; } else if (param_selection_loc == 5) { param_selection_loc = 7; - } else if (param_selection_loc > 0) { + } else if (param_selection_loc > 0 && param_selection_loc != 9) { param_selection_loc--; } } @@ -852,6 +898,16 @@ set_param_selection_sq2(ChannelSquareParams *params, InputHandler return_handler return false; } + if (key_tap(KEY_UP) || key_tap(KEY_DOWN)) { + if (param_selection_loc == 4) { + param_selection_loc = 9; + } else if (param_selection_loc == 9) { + param_selection_loc = 4; + } + redraw_params = true; + return false; + } + // Adjust parameter. if (key_tap(KEY_R) || key_tap(KEY_L)) { int inc; @@ -874,6 +930,7 @@ set_param_selection_sq2(ChannelSquareParams *params, InputHandler return_handler } break; case 3: { params->env_direction ^= 1; } break; case 4: { params->prob = CLAMP(params->prob + inc, 0, PROB_NUM - 1); } break; + case 9: { params->pan = CLAMP(params->pan + inc, -1, 1); } break; } redraw_params = true; return true; @@ -897,6 +954,8 @@ set_param_selection_wave(ChannelWaveParams *params, InputHandler return_handler) if (param_selection_loc == 4) { param_selection_loc = 0; } else if (param_selection_loc == 6) { + param_selection_loc = 9; + } else if (param_selection_loc == 9) { param_selection_loc = 5; } else if (param_selection_loc < 6) { param_selection_loc++; @@ -905,6 +964,8 @@ set_param_selection_wave(ChannelWaveParams *params, InputHandler return_handler) if (param_selection_loc == 0) { param_selection_loc = 4; } else if (param_selection_loc == 5) { + param_selection_loc = 9; + } else if (param_selection_loc == 9) { param_selection_loc = 6; } else if (param_selection_loc > 0) { param_selection_loc--; @@ -915,7 +976,11 @@ set_param_selection_wave(ChannelWaveParams *params, InputHandler return_handler) } if (key_tap(KEY_UP) || key_tap(KEY_DOWN)) { - if (param_selection_loc < 2) { + if (param_selection_loc == 4) { + param_selection_loc = 9; + } else if (param_selection_loc == 9) { + param_selection_loc = 4; + } else if (param_selection_loc < 2) { param_selection_loc += 5; } else if (param_selection_loc > 4) { param_selection_loc -= 5; @@ -940,6 +1005,7 @@ set_param_selection_wave(ChannelWaveParams *params, InputHandler return_handler) case 4: { params->prob = CLAMP(params->prob + inc, 0, PROB_NUM - 1); } break; case 5: { params->wave_mode = CLAMP(params->wave_mode + inc, 0, 2); } break; case 6: { params->wave_volume = CLAMP(params->wave_volume + inc, 0, 4); } break; + case 9: { params->pan = CLAMP(params->pan + inc, -1, 1); } break; } redraw_params = true; return true; @@ -972,7 +1038,7 @@ set_param_selection_noise(ChannelNoiseParams *params, InputHandler return_handle param_selection_loc = 4; } else if (param_selection_loc == 5) { param_selection_loc = 7; - } else if (param_selection_loc > 0) { + } else if (param_selection_loc > 0 && param_selection_loc != 9) { param_selection_loc--; } } @@ -980,6 +1046,16 @@ set_param_selection_noise(ChannelNoiseParams *params, InputHandler return_handle return false; } + if (key_tap(KEY_UP) || key_tap(KEY_DOWN)) { + if (param_selection_loc == 4) { + param_selection_loc = 9; + } else if (param_selection_loc == 9) { + param_selection_loc = 4; + } + redraw_params = true; + return false; + } + // Adjust parameter. if (key_tap(KEY_R) || key_tap(KEY_L)) { int inc; @@ -1002,6 +1078,7 @@ set_param_selection_noise(ChannelNoiseParams *params, InputHandler return_handle } break; case 3: { params->env_direction ^= 1; } break; case 4: { params->prob = CLAMP(params->prob + inc, 0, PROB_NUM - 1); } break; + case 9: { params->pan = CLAMP(params->pan + inc, -1, 1); } break; } redraw_params = true; return true; -- cgit v1.2.1