#define SPRITE_NOTE_NAMES_W 16 #define SPRITE_NOTE_NAMES_H 8 u32 sprite_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, 0x00000008, 0x0c0a0e08, 0x00000098, 0xa8a8a898, 0x00000020, 0x302b3a21, 0x00000060, 0xa0a0a060, 0x00000008, 0x0c0a0e08, 0x000000b8, 0x889888b8, 0x00000020, 0x302b3a21, 0x000000e0, 0x206020e0, 0x00000008, 0x0c0a0e08, 0x000000e0, 0x20602020, 0x00000008, 0x0c0a0e08, 0x000000b8, 0x8888a8b8, 0x00000020, 0x302b3a21, 0x000000e0, 0x2020a0e0, 0x00000008, 0x0c0a0e08, 0x000000b8, 0xa8a8b8a8, 0x00000020, 0x302b3a21, 0x000000e0, 0xa0a0e0a0, 0x00000008, 0x0c0a0e08, 0x000000b8, 0xa898a8b8, 0x00000020, 0x302b3a21, 0x000000e0, 0xa060a0e0, 0x00000008, 0x0c0a0e08, 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, }; typedef struct SeqTrigger { bool trigger; Note note; // TODO: ... } SeqTrigger; static SeqTrigger sequence_synth[] = { {true, NOTE_D_4}, {true, NOTE_F_4}, {true, NOTE_A_4}, {true, NOTE_C_5}, {true, NOTE_D_4}, {false, NOTE_C_SHARP_4}, {false, NOTE_D_4}, {false, NOTE_D_4}, {true, NOTE_D_4}, {true, NOTE_F_4}, {true, NOTE_A_4}, {true, NOTE_C_5}, {true, NOTE_D_4}, {false, NOTE_D_4}, {true, NOTE_A_4}, {false, NOTE_A_5}, }; static int step_counter = 0; static Note active_note; void irq_timer_0(void) { active_note = sequence_synth[step_counter].note; if (sequence_synth[step_counter].trigger) { SOUND_SQUARE1_CTRL = SOUND_SQUARE_ENV_VOL(13) | SOUND_SQUARE_ENV_TIME(4) | SOUND_SQUARE_DUTY(2); SOUND_SQUARE1_FREQ = SOUND_SQUARE_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; } static size_t note_sprites_id = 0; // // Sequencer sprite memory. // // Currently we load all sequencer sprite memory in the VRAM: // // - Note names: 0-73x2 tiles // - ... // // The order of OBJs correspond to: // // - 000-015 step note names. // - 015-029 step trigger steps. // - 030-045 step duration indicators. // - 046-046 current step marker. // - 047-047 trigger selection indicator. // size_t obj_counter = 0; typedef struct SeqSprite { u8 id; int x; int y; u16 obj_attr_0; u16 obj_attr_1; u16 obj_attr_2; } SeqSprite; #define SEQ_POS_X 20 #define SEQ_POS_Y 20 // SeqSprite seq_sprites[] = { // // 000-015: Step note names. // { // .x = SEQ_POS_X, // .y = SEQ_POS_Y, // .obj_attr_0 = OBJ_SHAPE_WIDE, // .obj_attr_1 = OBJ_SHAPE_SMALL, // .obj_attr_2 = 0, // }, // }; SeqSprite seq_sprites[16] = {0}; void init_sequencer_sprites() { // Load note sprites. note_sprites_id = load_packed_sprite_data(&sprite_note_names, 2, 73); for (size_t i = 0; i < 16; ++i) { int x = SEQ_POS_X + i * 16; int y = SEQ_POS_Y; if (i >= 8) { y += 32; x -= 8 * 16; } seq_sprites[i].x = x; seq_sprites[i].y = y; seq_sprites[i].id = obj_counter++; seq_sprites[i].obj_attr_0 = OBJ_SHAPE_WIDE | OBJ_Y_COORD(y); // TODO: They should start hidden until the update function changes that. // seq_sprites[i].obj_attr_0 = OBJ_SHAPE_WIDE | OBJ_HIDDEN; seq_sprites[i].obj_attr_1 = OBJ_SIZE_SMALL | OBJ_X_COORD(x); } } void update_sequencer_sprites() { // 000-015: Step note names. for (size_t i = 0; i < 16; ++i) { // Each note name is made of 2 8x8 tiles (16x8). size_t base_tile = sequence_synth[i].note * 2; // TODO: Show the note name if is within the duration, hide when trigger // is off or duration of previous note was cut short. if (!sequence_synth[i].trigger) { seq_sprites[i].obj_attr_0 |= OBJ_HIDDEN; } else { seq_sprites[i].obj_attr_0 &= ~OBJ_HIDDEN; } seq_sprites[i].obj_attr_2 = base_tile; } } void render_sequencer_sprites() { size_t len = sizeof(seq_sprites) / sizeof(SeqSprite); for (size_t i = 0; i < len; ++i) { size_t id = seq_sprites[i].id; OBJ_ATTR_0(id) = seq_sprites[i].obj_attr_0; OBJ_ATTR_1(id) = seq_sprites[i].obj_attr_1; OBJ_ATTR_2(id) = seq_sprites[i].obj_attr_2; } }