/* 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. */ // TODO: A list of features I would like to get to implement in the near future. // // UI tweaks. // - Notification support for feedback when doing some operations // (copying/pasting) // - Animations for cursor movement/current step highlight. (A fade out maybe?) // - Display played notes on all tonal channels when a trig or channel is not // selected. If a channel is selected show active note in that channel, if // a trig is selected behaved as usual. These could be highlighted in // different colors to make it easier on the eyes. If a pattern is selected, // show the notes it would play on that pattern? // - Theming support, with a number of pre-configured themes and custom colors. // // Quality of life improvements. // - Per channel sound adjustments that modify the sound in all trigs. // - Per-octave note adjustment with Select + L/R on a trig. // - Pattern chaining for more than 1 queue and/or song mode. // - Undo/Redo. // - Add a settings page to change some configuration parameters. // // Advanced // - Sync via MIDI via arduinoboy or something similar. // - Sync via CV by using the link cable. // // FIXME: Update readme and project pages with control changes. #include "gba/gba.h" #include "filesystem.c" #include "renderer_m0.c" #include "sequencer.c" #define PROF_ENABLE 0 #include "profiling.c" void render(void) { PROF(draw_rect(0, 0, SCREEN_WIDTH - 1, SCREEN_HEIGHT - 1, 1), clear_cycles); if (redraw_trigs) { PROF(draw_triggers(), draw_trigs_cycles); redraw_trigs = false; } if (redraw_channels) { PROF(draw_channels(), draw_btn_cycles); redraw_channels = false; } if (redraw_pattern_buttons) { PROF(draw_pattern_buttons(), draw_btn_cycles); redraw_pattern_buttons = false; } if (redraw_bank_buttons) { PROF(draw_bank_buttons(), draw_btn_cycles); redraw_bank_buttons = false; } if (redraw_bpm) { PROF(draw_bpm(), draw_btn_cycles); redraw_bpm = false; } if (redraw_play_pause) { PROF(draw_play(), draw_btn_cycles); PROF(draw_stop(), draw_btn_cycles); redraw_play_pause = false; } if (redraw_piano_note) { PROF(draw_piano(), draw_piano_cycles); 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) { // TODO: Show last/current played notes in all channels. } else { // Show currently selected trigger note. TriggerNote *trig = get_current_trig(); PROF(draw_note(trig->note, COL_NOTE_PRESSED), draw_piano_cycles); } redraw_piano_note = false; } if (redraw_params) { PROF(draw_parameters(), draw_param_cycles); redraw_params = false; } PROF(draw_cursors(), draw_cursor_cycles); } void update(void) { 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; } int main(void) { // Adjust system wait times. SYSTEM_WAIT = SYSTEM_WAIT_CARTRIDGE; // Initialize filesystem. fs_init(); // Initialize renderer. renderer_init(); // Register interrupts. irq_init(); irs_set(IRQ_VBLANK, irs_stub); // Initialize sequencer. sequencer_init(); txt_spacing(6); // Main loop. while (true) { poll_keys(); bios_vblank_wait(); PROF_SHOW(); FRAME_START(); PROF(flip_buffer(), flip_cycles); PROF(update(), update_cycles); PROF(handle_sequencer_input(), input_cycles); PROF(render(), render_cycles); FRAME_END(); } return 0; }