/* Copyright (c) 2023 Bad Diode Permission to use, copy, modify, and distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE. */ #include "gba/gba.h" #define PROF_ENABLE 0 #include "profiling.c" #include "renderer_m0.c" #include "globals.c" #include "assets.c" #include "patterns.c" #include "settings.c" #include "dsound.c" #include "scale.c" #include "sequencer.c" static int frames = 0; void render_sequencer(void) { if (redraw_trigs) { draw_triggers(); redraw_trigs = false; } if (redraw_channels) { draw_channels(); redraw_channels = false; } if (redraw_pattern_buttons) { draw_pattern_buttons(); redraw_pattern_buttons = false; } if (redraw_bank_buttons) { draw_bank_buttons(); redraw_bank_buttons = false; } if (redraw_bpm) { draw_bpm(); redraw_bpm = false; } if (redraw_play_pause) { draw_play(); draw_stop(); draw_settings(); redraw_play_pause = false; } if (redraw_scale) { draw_scale(); redraw_scale = false; } if (redraw_params) { draw_parameters(); redraw_params = false; } if (input_handler == handle_pattern_selection || input_handler == handle_pattern_chain) { if (frames & 0x1) { draw_pattern_chain(); } } if (frames & 0x1) { draw_notif_bar(); } if (frames++ % 0x4 == 0) { draw_piano_notes(); } draw_cursors(); } void render_settings(void) { size_t x0 = 8; size_t x1 = x0 + 64; size_t y0 = 6; // Header. txt_drawf("SETTINGS", x0, y0, COL_FG) draw_wrench(SCREEN_WIDTH - 32, y0, COL_FG); y0 = 16; draw_filled_rect(x0, y0 - 1, SCREEN_WIDTH - 9, y0, COL_FG); y0 = 20; // Globals. txt_drawf("GLOBAL", x0, y0, COL_FG); draw_line(x0, y0 + 9, SCREEN_WIDTH - 9, y0 + 9, COL_FG); y0 += 17; draw_rect(x0, y0, x1, y0 + 10, COL_FG); txt_drawf("MUTE", x0 + 2, y0 + 1, COL_FG); txt_drawf("%s", x1 + 8, y0 + 1, COL_FG, toggle_settings_str[settings.global_mute]); y0 += 17; draw_rect(x0, y0, x1, y0 + 10, COL_FG); txt_drawf("BPM", x0 + 2, y0 + 1, COL_FG); txt_drawf("%s", x1 + 8, y0 + 1, COL_FG, toggle_settings_str[settings.global_bpm]); y0 += 17; txt_drawf("GENERAL", x0, y0, COL_FG); draw_line(x0, y0 + 9, SCREEN_WIDTH - 9, y0 + 9, COL_FG); y0 += 17; draw_rect(x0, y0, x1, y0 + 10, COL_FG); txt_drawf("AUTO-SAVE", x0 + 2, y0 + 1, COL_FG); txt_drawf("%s", x1 + 8, y0 + 1, COL_FG, toggle_settings_str[settings.auto_save]); y0 += 17; draw_rect(x0, y0, x1, y0 + 10, COL_FG); txt_drawf("SYNC", x0 + 2, y0 + 1, COL_FG); txt_drawf("%s", x1 + 8, y0 + 1, COL_FG, sync_setting_str[settings.sync]); y0 += 17; draw_rect(x0, y0, x1, y0 + 10, COL_FG); txt_drawf("THEME", x0 + 2, y0 + 1, COL_FG); txt_drawf("%s", x1 + 8, y0 + 1, COL_FG, theme_setting_str[settings.theme]); y0 += 17; draw_rect(x0, y0, x1, y0 + 10, COL_FG); txt_drawf("HELP", x0 + 2, y0 + 1, COL_FG); txt_drawf("%s", x1 + 8, y0 + 1, COL_FG, toggle_settings_str[settings.help]); draw_settings_cursor(); } void render(void) { if (clear_screen) { PROF(screen_fill(COL_BG), PROF_FILL); clear_screen = false; redraw_trigs = true; redraw_channels = true; redraw_pattern_buttons = true; redraw_bank_buttons = true; redraw_bpm = true; redraw_play_pause = true; redraw_params = true; redraw_scale = true; } switch (scene) { case SCENE_SETTINGS: { render_settings(); } break; case SCENE_SEQUENCER: { 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) * 6 + sizeof(chain) * 6); } void handle_input(void) { #if PROF_ENABLE > 0 if (key_hold(KEY_SELECT)) { if (key_tap(KEY_DOWN)) { PROF_SHOW(); clear_screen = true; redraw_trigs = true; redraw_channels = true; redraw_pattern_buttons = true; redraw_bank_buttons = true; redraw_bpm = true; redraw_play_pause = true; redraw_params = true; redraw_scale = true; return; } static int prof_detail = 0; if (key_tap(KEY_RIGHT)) { prof_detail = CLAMP(prof_detail + 1, 0, 3); PROF_DETAIL(prof_detail); clear_screen = true; redraw_trigs = true; redraw_channels = true; redraw_pattern_buttons = true; redraw_bank_buttons = true; redraw_bpm = true; redraw_play_pause = true; redraw_params = true; redraw_scale = true; return; } if (key_tap(KEY_LEFT)) { prof_detail = CLAMP(prof_detail - 1, 0, 3); PROF_DETAIL(prof_detail); clear_screen = true; redraw_trigs = true; redraw_channels = true; redraw_pattern_buttons = true; redraw_bank_buttons = true; redraw_bpm = true; redraw_play_pause = true; redraw_params = true; redraw_scale = true; return; } } #endif switch (scene) { case SCENE_SETTINGS: { handle_settings_input(); } break; case SCENE_SEQUENCER: { handle_sequencer_input(); } break; } } void update(void) { if (next_scene != scene) { scene = next_scene; clear_screen = true; redraw_trigs = true; redraw_channels = true; redraw_pattern_buttons = true; redraw_bank_buttons = true; redraw_bpm = true; redraw_play_pause = true; redraw_params = true; redraw_scale = true; } last_trig_loc = trig_selection_loc; last_channel_loc = channel_selection_loc; last_pattern_loc = pattern_selection_loc; last_right_col_loc = right_col_selection_loc; if (update_bpm) { if (settings.global_bpm) { set_time(settings.bpm); } else { set_time(patterns[current_pattern].bpm); } update_bpm = false; } if (audio_sync_click) { play_click(); } if (notif.time > 0) { notif.time--; } rng_state++; } int main(void) { // Adjust system wait times. SYSTEM_WAIT = SYSTEM_WAIT_CARTRIDGE; // Initialize renderer. renderer_init(); // Register interrupts. irq_init(); irs_set(IRQ_VBLANK, sound_vsync); // Initialize sequencer. sequencer_init(); txt_spacing(6); // Main loop. PROF_INIT(); while (true) { poll_keys(); bios_vblank_wait(); FRAME_START(); PROF(flip_buffer(), PROF_FLIP); PROF(update(), PROF_UPDATE); PROF(handle_input(), PROF_INPUT); PROF(render(), PROF_RENDER); FRAME_END(); } return 0; }