#include "rng.c" #include "text.h" // // Assets. // #define ASSETS_DATA ((u32*)(MEM_VRAM + KB(32))) static const u32 note_names[] = { 0x000000e0, 0x202020e0, 0x0000000e, 0x080e020e, 0x00000098, 0xa8a8a898, 0x00000038, 0x203b0a39, 0x00000060, 0xa0a0a060, 0x0000000e, 0x080e020e, 0x000000b8, 0x889888b8, 0x00000038, 0x203b0a39, 0x000000e0, 0x206020e0, 0x0000000e, 0x080e020e, 0x000000e0, 0x20602020, 0x0000000e, 0x080e020e, 0x000000b8, 0x8888a8b8, 0x00000038, 0x203b0a39, 0x000000e0, 0x2020a0e0, 0x0000000e, 0x080e020e, 0x000000b8, 0xa8a8b8a8, 0x00000038, 0x203b0a39, 0x000000e0, 0xa0a0e0a0, 0x0000000e, 0x080e020e, 0x000000b8, 0xa898a8b8, 0x00000038, 0x203b0a39, 0x000000e0, 0xa060a0e0, 0x0000000e, 0x080e020e, 0x000000e0, 0x202020e0, 0x0000000e, 0x080c080e, 0x00000098, 0xa8a8a898, 0x00000038, 0x20332239, 0x00000060, 0xa0a0a060, 0x0000000e, 0x080c080e, 0x000000b8, 0x889888b8, 0x00000038, 0x20332239, 0x000000e0, 0x206020e0, 0x0000000e, 0x080c080e, 0x000000e0, 0x20602020, 0x0000000e, 0x080c080e, 0x000000b8, 0x8888a8b8, 0x00000038, 0x20332239, 0x000000e0, 0x2020a0e0, 0x0000000e, 0x080c080e, 0x000000b8, 0xa8a8b8a8, 0x00000038, 0x20332239, 0x000000e0, 0xa0a0e0a0, 0x0000000e, 0x080c080e, 0x000000b8, 0xa898a8b8, 0x00000038, 0x20332239, 0x000000e0, 0xa060a0e0, 0x0000000e, 0x080c080e, 0x000000e0, 0x202020e0, 0x0000000a, 0x0a0e0808, 0x00000098, 0xa8a8a898, 0x00000028, 0x283b2221, 0x00000060, 0xa0a0a060, 0x0000000a, 0x0a0e0808, 0x000000b8, 0x889888b8, 0x00000028, 0x283b2221, 0x000000e0, 0x206020e0, 0x0000000a, 0x0a0e0808, 0x000000e0, 0x20602020, 0x0000000a, 0x0a0e0808, 0x000000b8, 0x8888a8b8, 0x00000028, 0x283b2221, 0x000000e0, 0x2020a0e0, 0x0000000a, 0x0a0e0808, 0x000000b8, 0xa8a8b8a8, 0x00000028, 0x283b2221, 0x000000e0, 0xa0a0e0a0, 0x0000000a, 0x0a0e0808, 0x000000b8, 0xa898a8b8, 0x00000028, 0x283b2221, 0x000000e0, 0xa060a0e0, 0x0000000a, 0x0a0e0808, 0x000000e0, 0x202020e0, 0x0000000e, 0x020e080e, 0x00000098, 0xa8a8a898, 0x00000038, 0x083b2239, 0x00000060, 0xa0a0a060, 0x0000000e, 0x020e080e, 0x000000b8, 0x889888b8, 0x00000038, 0x083b2239, 0x000000e0, 0x206020e0, 0x0000000e, 0x020e080e, 0x000000e0, 0x20602020, 0x0000000e, 0x020e080e, 0x000000b8, 0x8888a8b8, 0x00000038, 0x083b2239, 0x000000e0, 0x2020a0e0, 0x0000000e, 0x020e080e, 0x000000b8, 0xa8a8b8a8, 0x00000038, 0x083b2239, 0x000000e0, 0xa0a0e0a0, 0x0000000e, 0x020e080e, 0x000000b8, 0xa898a8b8, 0x00000038, 0x083b2239, 0x000000e0, 0xa060a0e0, 0x0000000e, 0x020e080e, 0x000000e0, 0x202020e0, 0x0000000e, 0x020e0a0e, 0x00000098, 0xa8a8a898, 0x00000038, 0x083b2a39, 0x00000060, 0xa0a0a060, 0x0000000e, 0x020e0a0e, 0x000000b8, 0x889888b8, 0x00000038, 0x083b2a39, 0x000000e0, 0x206020e0, 0x0000000e, 0x020e0a0e, 0x000000e0, 0x20602020, 0x0000000e, 0x020e0a0e, 0x000000b8, 0x8888a8b8, 0x00000038, 0x083b2a39, 0x000000e0, 0x2020a0e0, 0x0000000e, 0x020e0a0e, 0x000000b8, 0xa8a8b8a8, 0x00000038, 0x083b2a39, 0x000000e0, 0xa0a0e0a0, 0x0000000e, 0x020e0a0e, 0x000000b8, 0xa898a8b8, 0x00000038, 0x083b2a39, 0x000000e0, 0xa060a0e0, 0x0000000e, 0x020e0a0e, 0x000000e0, 0x202020e0, 0x0000000e, 0x08040202, 0x00000098, 0xa8a8a898, 0x00000038, 0x20130a09, 0x00000060, 0xa0a0a060, 0x0000000e, 0x08040202, 0x000000b8, 0x889888b8, 0x00000038, 0x20130a09, 0x000000e0, 0x206020e0, 0x0000000e, 0x08040202, 0x000000e0, 0x20602020, 0x0000000e, 0x08040202, 0x000000b8, 0x8888a8b8, 0x00000038, 0x20130a09, 0x000000e0, 0x2020a0e0, 0x0000000e, 0x08040202, 0x000000b8, 0xa8a8b8a8, 0x00000038, 0x20130a09, 0x000000e0, 0xa0a0e0a0, 0x0000000e, 0x08040202, 0x000000b8, 0xa898a8b8, 0x00000038, 0x20130a09, 0x000000e0, 0xa060a0e0, 0x0000000e, 0x08040202, 0x000000e0, 0x202020e0, 0x0000000e, 0x0a0e0a0e, }; static const u32 channel_buttons[] = { 0xff017111, 0x117101ff, 0xff008585, 0x879500ff, 0x0f080808, 0x0808080f, 0xff01b989, 0x89b901ff, 0xff004242, 0x434a00ff, 0x0f080909, 0x0909080f, 0xff015d45, 0xc55d01ff, 0xff00a1a1, 0xa1a500ff, 0x0f080a0a, 0x0a0a080f, 0xff015d45, 0xc55d01ff, 0xff00a1a1, 0xa12500ff, 0x0f080a0a, 0x0a0b080f, 0xff01c141, 0xc14101ff, 0xff00151c, 0x141400ff, 0x0f080808, 0x0808080f, }; // // Globals. // typedef enum { SEQ_SELECT_TRIGGER, SEQ_SELECT_CHANNEL, SEQ_SELECT_PARAMETER, } SeqSelect; static int bpm = 115; static int step_counter = 0; int trig_selection_loc = 0; int param_selection_loc = 64; int channel_selection_loc = 0; SeqSelect current_selection = SEQ_SELECT_TRIGGER; // // Wave data. // // TODO: Make them u32. static const u8 sine_wave[16] = { 0x89, 0xBC, 0xDE, 0xEF, 0xFE, 0xED, 0xCB, 0x98, 0x76, 0x43, 0x21, 0x10, 0x01, 0x12, 0x34, 0x67, }; static const u8 saw_wave[16] = { 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, }; static const u8 square_wave[16] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }; // TODO: Split into individual trigger types. typedef struct SeqTrigger { bool trigger; Note note; u8 env_volume; u8 env_time; u8 env_direction; u8 duty_cycle; u8 sweep_number; u8 sweep_time; u8 sweep_direction; u8 wave_volume; u8 wave_mode; u8 wave_a[16]; u8 wave_b[16]; } SeqTrigger; static SeqTrigger sequences[3][16] = { // Synth 1. { {true, NOTE_C_4, 8, 4, 0, 2, 0, 0, 0, 0, 0, {0}, {0}}, {true, NOTE_D_4, 8, 4, 0, 2, 0, 0, 0, 0, 0, {0}, {0}}, {false, NOTE_E_4, 8, 4, 0, 2, 0, 0, 0, 0, 0, {0}, {0}}, {false, NOTE_F_4, 8, 4, 0, 2, 0, 0, 0, 0, 0, {0}, {0}}, {true, NOTE_G_4, 8, 4, 0, 2, 0, 0, 0, 0, 0, {0}, {0}}, {true, NOTE_A_4, 8, 4, 0, 2, 0, 0, 0, 0, 0, {0}, {0}}, {false, NOTE_B_4, 8, 4, 0, 2, 0, 0, 0, 0, 0, {0}, {0}}, {false, NOTE_C_5, 8, 4, 0, 2, 0, 0, 0, 0, 0, {0}, {0}}, {true, NOTE_C_4, 8, 4, 0, 2, 0, 0, 0, 0, 0, {0}, {0}}, {true, NOTE_C_4, 8, 4, 0, 2, 0, 0, 0, 0, 0, {0}, {0}}, {false, NOTE_C_4, 8, 4, 0, 2, 0, 0, 0, 0, 0, {0}, {0}}, {false, NOTE_C_4, 8, 4, 0, 2, 0, 0, 0, 0, 0, {0}, {0}}, {true, NOTE_C_4, 8, 4, 0, 2, 0, 0, 0, 0, 0, {0}, {0}}, {false, NOTE_C_4, 8, 4, 0, 2, 0, 0, 0, 0, 0, {0}, {0}}, {false, NOTE_C_4, 8, 4, 0, 2, 0, 0, 0, 0, 0, {0}, {0}}, {false, NOTE_C_4, 8, 4, 0, 2, 0, 0, 0, 0, 0, {0}, {0}}, }, // Synth 2. { {false, NOTE_G_5, 8, 4, 0, 2, 0, 0, 0, 0, 0, {0}, {0}}, {false, NOTE_G_5, 8, 4, 0, 2, 0, 0, 0, 0, 0, {0}, {0}}, {false, NOTE_G_5, 8, 4, 0, 2, 0, 0, 0, 0, 0, {0}, {0}}, {false, NOTE_G_5, 8, 4, 0, 2, 0, 0, 0, 0, 0, {0}, {0}}, {false, NOTE_G_5, 8, 4, 0, 2, 0, 0, 0, 0, 0, {0}, {0}}, {false, NOTE_G_5, 8, 4, 0, 2, 0, 0, 0, 0, 0, {0}, {0}}, {false, NOTE_G_5, 8, 4, 0, 2, 0, 0, 0, 0, 0, {0}, {0}}, {false, NOTE_G_5, 8, 4, 0, 2, 0, 0, 0, 0, 0, {0}, {0}}, {false, NOTE_C_5, 8, 4, 0, 2, 0, 0, 0, 0, 0, {0}, {0}}, {false, NOTE_B_4, 8, 4, 0, 2, 0, 0, 0, 0, 0, {0}, {0}}, {false, NOTE_A_4, 8, 4, 0, 2, 0, 0, 0, 0, 0, {0}, {0}}, {false, NOTE_G_4, 8, 4, 0, 2, 0, 0, 0, 0, 0, {0}, {0}}, {false, NOTE_F_4, 8, 4, 0, 2, 0, 0, 0, 0, 0, {0}, {0}}, {false, NOTE_E_4, 8, 4, 0, 2, 0, 0, 0, 0, 0, {0}, {0}}, {false, NOTE_D_4, 8, 4, 0, 2, 0, 0, 0, 0, 0, {0}, {0}}, {false, NOTE_C_4, 8, 4, 0, 2, 0, 0, 0, 0, 0, {0}, {0}}, }, // Synth 3. { {true, NOTE_C_5, 8, 4, 0, 2, 0, 0, 0, 3, 0, {0}, {0}}, {true, NOTE_D_5, 8, 4, 0, 2, 0, 0, 0, 3, 0, {0}, {0}}, {true, NOTE_E_5, 8, 4, 0, 2, 0, 0, 0, 3, 0, {0}, {0}}, {true, NOTE_F_5, 8, 4, 0, 2, 0, 0, 0, 3, 0, {0}, {0}}, {true, NOTE_G_5, 8, 4, 0, 2, 0, 0, 0, 3, 0, {0}, {0}}, {true, NOTE_A_5, 8, 4, 0, 2, 0, 0, 0, 3, 0, {0}, {0}}, {true, NOTE_B_5, 8, 4, 0, 2, 0, 0, 0, 3, 0, {0}, {0}}, {true, NOTE_C_6, 8, 4, 0, 2, 0, 0, 0, 3, 0, {0}, {0}}, {true, NOTE_C_5, 8, 4, 0, 2, 0, 0, 0, 3, 0, {0}, {0}}, {true, NOTE_D_5, 8, 4, 0, 2, 0, 0, 0, 3, 0, {0}, {0}}, {true, NOTE_E_5, 8, 4, 0, 2, 0, 0, 0, 3, 0, {0}, {0}}, {true, NOTE_F_5, 8, 4, 0, 2, 0, 0, 0, 3, 0, {0}, {0}}, {true, NOTE_G_5, 8, 4, 0, 2, 0, 0, 0, 3, 0, {0}, {0}}, {true, NOTE_A_5, 8, 4, 0, 2, 0, 0, 0, 3, 0, {0}, {0}}, {true, NOTE_B_5, 8, 4, 0, 2, 0, 0, 0, 3, 0, {0}, {0}}, {true, NOTE_C_6, 8, 4, 0, 2, 0, 0, 0, 3, 0, {0}, {0}}, }, }; #define TRIG_W 15 #define TRIG_H 24 #define TRIG_START_X 66 #define TRIG_START_Y 90 #define TRIG_OFFSET_X (TRIG_W + 4) #define TRIG_OFFSET_Y (TRIG_H + 7) 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 - 1; draw_filled_rect(x0, y0, x1, y1, 0); } void draw_trigger(size_t chan, size_t i) { if (sequences[chan][i].trigger) { 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; Tile *tiles = ASSETS_DATA; tiles += 2 * sequences[chan][i].note; draw_tile(x, y, tiles, true); draw_tile(x + 8, y, tiles + 1, true); } 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 ? 1 : 1 + 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; draw_line(x0, y, x1, y, clr); } 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, 1); draw_trigger(channel_selection_loc, i); } } #define CHAN_START_X 35 #define CHAN_START_Y 90 #define CHAN_OFFSET_Y 12 void draw_channels(void) { // Contains 5 channel buttons: Ch. 1-4 + FM. We are only drawing the DMG // channels for now, since FM may take some time to develop. Tile channel_tiles[3 * 4] = {0}; unpack_tiles(channel_buttons, channel_tiles, 3 * 4); size_t k = 0; for (size_t i = 0; i < 4; i++) { size_t y = CHAN_START_Y + i * CHAN_OFFSET_Y; draw_tile(CHAN_START_X, y, channel_tiles + k++, false); draw_tile(CHAN_START_X + 8, y, channel_tiles + k++, false); draw_tile(CHAN_START_X + 16, y, channel_tiles + k++, false); } } void irq_timer_0(void) { Note active_note; { SeqTrigger *trig = &sequences[0][step_counter]; active_note = trig->note; if (trig->trigger) { SOUND_SQUARE1_SWEEP = SOUND_SWEEP_NUMBER(trig->sweep_number) | SOUND_SWEEP_DIR(trig->sweep_direction) | SOUND_SWEEP_TIME(trig->sweep_time); SOUND_SQUARE1_CTRL = SOUND_SQUARE_ENV_VOL(trig->env_volume) | SOUND_SQUARE_ENV_TIME(trig->env_time) | SOUND_SQUARE_ENV_DIR(trig->env_direction) | SOUND_SQUARE_DUTY(trig->duty_cycle); SOUND_SQUARE1_FREQ = SOUND_FREQ_RESET | sound_rates[active_note]; } } { SeqTrigger *trig = &sequences[1][step_counter]; active_note = trig->note; if (trig->trigger) { SOUND_SQUARE2_CTRL = SOUND_SQUARE_ENV_VOL(trig->env_volume) | SOUND_SQUARE_ENV_TIME(trig->env_time) | SOUND_SQUARE_ENV_DIR(trig->env_direction) | SOUND_SQUARE_DUTY(trig->duty_cycle); SOUND_SQUARE2_FREQ = SOUND_FREQ_RESET | sound_rates[active_note]; } } { SeqTrigger *trig = &sequences[2][step_counter]; active_note = trig->note; if (trig->trigger) { // Update both banks. // TODO: Actually depends on which bank is selected, no need to // update both if only one is playing. // TODO: Should we compare if previous and current wave are the // same before updating? SOUND_WAVE_MODE = SOUND_WAVE_BANK_SELECT(1); dma_copy(SOUND_WAVE_RAM, trig->wave_a, 16, 3); SOUND_WAVE_MODE = SOUND_WAVE_BANK_SELECT(0); dma_copy(SOUND_WAVE_RAM, trig->wave_b, 16, 3); switch (trig->wave_mode) { case 0: { SOUND_WAVE_MODE = SOUND_WAVE_BANK_MODE(0) | SOUND_WAVE_BANK_SELECT(0); } break; case 1: { SOUND_WAVE_MODE = SOUND_WAVE_BANK_MODE(0) | SOUND_WAVE_BANK_SELECT(1); } break; case 2: { SOUND_WAVE_MODE = SOUND_WAVE_BANK_MODE(1) | SOUND_WAVE_BANK_SELECT(0); } break; case 3: { SOUND_WAVE_MODE = SOUND_WAVE_BANK_MODE(1) | SOUND_WAVE_BANK_SELECT(1); } break; } SOUND_WAVE_MODE |= SOUND_WAVE_ENABLE; switch (trig->wave_volume) { case 0: { SOUND_WAVE_CTRL = SOUND_WAVE_MUTE; } break; case 1: { SOUND_WAVE_CTRL = SOUND_WAVE_VOL_25; } break; case 2: { SOUND_WAVE_CTRL = SOUND_WAVE_VOL_50; } break; case 3: { SOUND_WAVE_CTRL = SOUND_WAVE_VOL_75; } break; case 4: { SOUND_WAVE_CTRL = SOUND_WAVE_VOL_100; } break; } SOUND_WAVE_FREQ = SOUND_FREQ_RESET | sound_rates[active_note]; } } step_counter = (step_counter + 1) % 16; } void set_time(int bpm) { // The number of ticks of a 1024 cycle clock in a step based on the BPM can // be calculated as: // X bpm -> 60000 / 4 / bpm = Y ms = Ye-3 s // Y ms -> Ye-3 / 59.99e-9 / 1024 = Z ticks // We have to operate on integer values, so the numbers have been // precalculated to `n_ticks = 244181 / bmp` int n_ticks = -244181 / bpm; irs_set(IRQ_TIMER_0, irq_timer_0); TIMER_DATA_0 = n_ticks; TIMER_CTRL_0 = TIMER_CTRL_IRQ | TIMER_CTRL_ENABLE | TIMER_CTRL_FREQ_3; } #define SEQ_N_CHANNELS 3 void trigger_cursor(void) { SeqTrigger *trig = &sequences[channel_selection_loc][trig_selection_loc]; if (key_tap(KEY_LEFT)) { if (trig_selection_loc == 0 || trig_selection_loc == 8) { // current_selection = SEQ_SELECT_CHANNEL; } else { draw_trig_cursor(trig_selection_loc, 0); trig_selection_loc = MAX(trig_selection_loc - 1, 0); draw_trig_cursor(trig_selection_loc, 3); } } else if (key_tap(KEY_RIGHT)) { if (trig_selection_loc != 7) { draw_trig_cursor(trig_selection_loc, 0); trig_selection_loc = MIN(trig_selection_loc + 1, 15); draw_trig_cursor(trig_selection_loc, 3); } } else if (key_tap(KEY_UP) || key_tap(KEY_DOWN)) { draw_trig_cursor(trig_selection_loc, 0); trig_selection_loc = (trig_selection_loc + 8) % 16; draw_trig_cursor(trig_selection_loc, 3); } else if (key_tap(KEY_B)) { trig->trigger ^= 1; } else if (key_tap(KEY_L)) { if (trig->trigger) { trig->note = MAX(trig->note - 1, NOTE_C_2); } } else if (key_tap(KEY_R)) { if (trig->trigger) { trig->note = MIN( trig->note + 1, NOTE_C_8); } } else if (key_tap(KEY_A)) { // Switch to parameter selection. // current_selection = SEQ_SELECT_PARAMETER; } } void (*input_handler)(void); void handle_sequencer_input(void) { poll_keys(); input_handler(); // SeqTrigger *trig = &sequences[channel_selection_loc][trig_selection_loc]; // if (current_selection == SEQ_SELECT_TRIGGER) { // if (key_tap(KEY_LEFT)) { // if (trig_selection_loc == 0 || trig_selection_loc == 8) { // current_selection = SEQ_SELECT_CHANNEL; // } else { // trig_selection_loc = MAX(trig_selection_loc - 1, 0); // } // } else if (key_tap(KEY_RIGHT)) { // if (trig_selection_loc != 7) { // trig_selection_loc = MIN(trig_selection_loc + 1, 15); // } // } else if (key_tap(KEY_UP) || key_tap(KEY_DOWN)) { // trig_selection_loc = (trig_selection_loc + 8) % 16; // } else if (key_tap(KEY_B)) { // trig->trigger ^= 1; // } else if (key_tap(KEY_L)) { // if (trig->trigger) { // trig->note = MAX(trig->note - 1, NOTE_C_2); // } // } else if (key_tap(KEY_R)) { // if (trig->trigger) { // trig->note = MIN( trig->note + 1, NOTE_C_8); // } // } else if (key_tap(KEY_A)) { // // Switch to parameter selection. // current_selection = SEQ_SELECT_PARAMETER; // } // } else if (current_selection == SEQ_SELECT_PARAMETER) { // if (channel_selection_loc < 2) { // // Move through the selected synth parameters. // if (key_tap(KEY_LEFT)) { // int max_param = 6; // if (channel_selection_loc == 1) { // max_param = 3; // } // if (param_selection_loc == 0) { // param_selection_loc = max_param; // } else { // param_selection_loc = MAX(param_selection_loc - 1, 0); // } // } // if (key_tap(KEY_RIGHT)) { // int max_param = 6; // if (channel_selection_loc == 1) { // max_param = 3; // } // if (param_selection_loc == max_param) { // param_selection_loc = 0; // } else { // param_selection_loc = MIN(param_selection_loc + 1, max_param); // } // } // // Adjust the parameters up or down. // if (key_tap(KEY_L) || key_tap(KEY_R)) { // int inc; // if (key_tap(KEY_L)) { // inc = -1; // } else { // inc = 1; // } // switch (param_selection_loc) { // case 0: { // trig->env_volume = CLAMP(trig->env_volume + inc, 0, 15); // } break; // case 1: { // trig->env_time = CLAMP(trig->env_time + inc, 0, 7); // } break; // case 2: { // trig->env_direction ^= 1; // } break; // case 3: { // trig->duty_cycle = CLAMP(trig->duty_cycle + inc, 0, 3); // } break; // case 4: { // trig->sweep_number = CLAMP(trig->sweep_number + inc, 0, 7); // } break; // case 5: { // trig->sweep_time = CLAMP(trig->sweep_time + inc, 0, 7); // } break; // case 6: { // if (trig->sweep_direction == 0) { // trig->sweep_direction = 1; // } else { // trig->sweep_direction = 0; // } // } break; // } // } // } else if (channel_selection_loc == 2) { // if (key_tap(KEY_LEFT) || key_tap(KEY_RIGHT)) { // int inc = 0; // int loc = param_selection_loc; // if (key_tap(KEY_RIGHT)) { // if (loc == 15 || loc == 31) { // inc = 17; // } else if (loc != 47 && loc != 63){ // inc = 1; // } // } else { // if (loc == 32 || loc == 48) { // inc = -17; // } else if (loc != 16 && loc != 64){ // inc = -1; // } // } // param_selection_loc = CLAMP(loc + inc, 0, 71); // } // if (key_tap(KEY_UP) || key_tap(KEY_DOWN)) { // int inc = 0; // int loc = param_selection_loc; // if (key_tap(KEY_UP)) { // if ((loc >= 16 && loc < 32) || (loc >= 48 && loc < 64)) { // inc = -16; // } else if (loc == 64) { // inc = -48; // } else if (loc == 65) { // inc = -45; // } else if (loc == 66) { // inc = -42; // } else if (loc == 67) { // inc = -39; // } else if (loc == 68) { // inc = -20; // } else if (loc == 69) { // inc = -17; // } else if (loc == 70) { // inc = -14; // } else if (loc == 71) { // inc = -11; // } // } else { // if (loc < 16 || (loc >= 32 && loc < 48)) { // inc = 16; // } else if (loc >= 16 && loc <= 19){ // inc = 48 - (loc - 16); // } else if (loc >= 20 && loc <= 23){ // inc = 45 - (loc - 20); // } else if (loc >= 24 && loc <= 27){ // inc = 42 - (loc - 24); // } else if (loc >= 28 && loc <= 31){ // inc = 39 - (loc - 28); // } else if (loc >= 48 && loc <= 51){ // inc = 20 - (loc - 48); // } else if (loc >= 52 && loc <= 55){ // inc = 17 - (loc - 52); // } else if (loc >= 56 && loc <= 59){ // inc = 14 - (loc - 56); // } else if (loc >= 60 && loc <= 63){ // inc = 11 - (loc - 60); // } // } // param_selection_loc = CLAMP(loc + inc, 0, 71); // } // if (key_tap(KEY_R) || key_tap(KEY_L)) { // int odd = param_selection_loc & 0x1; // int inc; // if (key_tap(KEY_R)) { // inc = 1; // } else { // inc = -1; // } // // Wave: AA BB CC DD ... // // ^^ // // |`- odd // // `-- even // if (param_selection_loc < 32) { // u8 byte_number = param_selection_loc >> 1; // u8 byte = sequences[2][trig_selection_loc].wave_a[byte_number]; // if (odd) { // byte = (~0xF & byte) | ((byte + inc) & 0xF); // } else { // byte = (0xF & byte) | (((byte >> 4) + inc) & 0xF) << 4; // } // sequences[2][trig_selection_loc].wave_a[byte_number] = byte; // } else if (param_selection_loc < 64){ // u8 byte_number = (param_selection_loc - 32) >> 1; // u8 byte = sequences[2][trig_selection_loc].wave_b[byte_number]; // if (odd) { // byte = (~0xF & byte) | (byte + inc); // } else { // byte = (0xF & byte) | ((byte >> 4) + inc) << 4; // } // sequences[2][trig_selection_loc].wave_b[byte_number] = byte; // } else if (param_selection_loc == 64){ // dma_copy(&trig->wave_a, &sine_wave, 16); // } else if (param_selection_loc == 65){ // dma_copy(&trig->wave_a, &saw_wave, 16); // } else if (param_selection_loc == 66){ // dma_copy(&trig->wave_a, &square_wave, 16); // } else if (param_selection_loc == 67){ // u32 rand_wave[4] = { // rng32(), rng32(), rng32(), rng32(), // }; // dma_copy(&trig->wave_a, &rand_wave, 16); // } else if (param_selection_loc == 68){ // dma_copy(&trig->wave_b, &sine_wave, 16); // } else if (param_selection_loc == 69){ // dma_copy(&trig->wave_b, &saw_wave, 16); // } else if (param_selection_loc == 70){ // dma_copy(&trig->wave_b, &square_wave, 16); // } else if (param_selection_loc == 71){ // u32 rand_wave[4] = { // rng32(), rng32(), rng32(), rng32(), // }; // dma_copy(&trig->wave_b, &rand_wave, 16); // } // } // } // // Go back to trigger selection. // if (key_tap(KEY_A)) { // current_selection = SEQ_SELECT_TRIGGER; // } // // Enable disable trigger. // if (key_tap(KEY_B)) { // trig->trigger ^= 1; // } // } else if (current_selection == SEQ_SELECT_CHANNEL) { // if (key_tap(KEY_RIGHT)) { // current_selection = SEQ_SELECT_TRIGGER; // trig_selection_loc = 0; // param_selection_loc = 0; // } // if (key_tap(KEY_UP)) { // if (channel_selection_loc == 0) { // channel_selection_loc = SEQ_N_CHANNELS - 1; // } else { // channel_selection_loc = MAX(channel_selection_loc - 1, 0); // } // } // if (key_tap(KEY_DOWN)) { // if (channel_selection_loc == SEQ_N_CHANNELS - 1) { // channel_selection_loc = 0; // } else { // channel_selection_loc = MIN(channel_selection_loc + 1, SEQ_N_CHANNELS); // } // } // } if (key_tap(KEY_START)) { step_counter = 0; if ((TIMER_CTRL_0 & TIMER_CTRL_ENABLE) == 0) { set_time(bpm); } else { TIMER_CTRL_0 ^= TIMER_CTRL_ENABLE; SOUND_SQUARE1_CTRL = 0; SOUND_SQUARE2_CTRL = 0; SOUND_WAVE_CTRL = 0; } } if (key_tap(KEY_SELECT)) { TIMER_CTRL_0 ^= TIMER_CTRL_ENABLE; SOUND_SQUARE1_CTRL = 0; SOUND_SQUARE2_CTRL = 0; SOUND_WAVE_CTRL = 0; } // if (key_tap(KEY_LEFT) // || key_tap(KEY_RIGHT) // || key_tap(KEY_UP) // || key_tap(KEY_DOWN) // || key_tap(KEY_L) // || key_tap(KEY_R) // ) { // } } void load_note_names(void) { unpack_tiles(note_names, ASSETS_DATA, 2 * 73); } void sequencer_init(void) { // TODO: Unpack non-sprite tiles directly on the VRAM. load_note_names(); // Initialize background objects and sprites. draw_triggers(); draw_channels(); // Initialize input handler. input_handler = trigger_cursor; draw_trig_cursor(trig_selection_loc, 3); // Initialize sound system. SOUND_STATUS = SOUND_ENABLE; SOUND_DMG_MASTER = sound_volume(SOUND_SQUARE1 | SOUND_SQUARE2 | SOUND_WAVE, 3); SOUND_DSOUND_MASTER = SOUND_DMG25; }