#include "bitmap.h" #include "rng.c" // Positioning parameters. #define SEQ_TRIG_POS_X 45 #define SEQ_TRIG_POS_Y 58 #define SEQ_TRIG_DIST_X 18 #define SEQ_TRIG_DIST_Y 28 #define SEQ_CHANNEL_POS_X SEQ_TRIG_POS_X - 32 #define SEQ_CHANNEL_POS_Y SEQ_TRIG_POS_Y #define SEQ_CHANNEL_DIST_Y 11 #define SEQ_ENV_POS_X 10 #define SEQ_ENV_POS_Y 18 #define SEQ_ENV_DIST 32 #define SEQ_DUTYCYCLE_POS_X SEQ_ENV_POS_X + SEQ_ENV_DIST * 3 #define SEQ_DUTYCYCLE_POS_Y SEQ_ENV_POS_Y - 8 #define SEQ_SWEEP_POS_X SEQ_DUTYCYCLE_POS_X + SEQ_ENV_DIST #define SEQ_SWEEP_POS_Y SEQ_ENV_POS_Y #define SEQ_CH3_PARAM_X SEQ_TRIG_POS_X #define SEQ_CH3_PARAM_Y SEQ_ENV_POS_Y - 12 #define SEQ_CH3_PARAM_SEL_X SEQ_CH3_PARAM_X - 5 #define SEQ_CH3_PARAM_SEL_Y SEQ_CH3_PARAM_Y + 22 #define SEQ_N_CHANNELS 3 // // Sprite data. // 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, 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, }; u32 sprite_trigger_button[] = { 0xfe020202, 0x02020202, 0xff808080, 0x80808080, 0x02020202, 0x02020202, 0x80808080, 0x80808080, 0x02020202, 0x020202fe, 0x80808080, 0x808080ff, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }; u32 sprite_trigger_active_indicator[] = { 0x00000000, 0x00f80000, }; u32 sprite_ch_3_parameter_selection[] = { 0x00000000, 0x00f80000, 0xfe000000, 0x00000000, 0x7f000000, 0x00000000, }; u32 sprite_trigger_selection[] = { 0x0f010101, 0x00000000, 0x80000000, 0x00000000, 0x07040404, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x0101010f, 0x00000000, 0x00000080, 0x00000000, 0x04040407, 0x00000000, 0x00000000, 0x00000000, }; u32 sprite_env_label_volume[] = { 0xa0a0a0a0, 0xc0000000, 0x262a2a2a, 0xee000000, 0xaaeaaaaa, 0xae000000, 0x0e020602, 0x0e000000, }; u32 sprite_env_sweep_label_time[] = { 0x00000000, 0x00000000, 0xee444444, 0xe4000000, 0xea2e6a2a, 0xea000000, 0x00000000, 0x00000000, }; u32 sprite_env_label_direction[] = { 0x00000000, 0x00000000, 0xcc949494, 0xcc000000, 0x1d140cd4, 0xd5000000, 0x00000000, 0x00000000, }; u32 sprite_env_label_env[] = { 0x6ea2a6a2, 0xae000000, 0xea2a6a2a, 0xec000000, 0x62a2a2a2, 0xee000000, 0xee2a6e22, 0xe2000000, }; u32 sprite_sweep_number_label[] = { 0x60a0a0a0, 0xa0000000, 0xaaeaaaaa, 0xae000000, 0xee2a662a, 0xee000000, 0x0e0a060a, 0x0a000000, }; u32 sprite_sweep_label[] = { 0x8040c000, 0xc0000000, 0xa5a4a5b5, 0xa8000000, 0xbb889988, 0xbb000000, 0x03020300, 0x00000000, }; u32 sprite_duty_cycle_label[] = { 0x00000000, 0x00000080, 0xa6aaaaaa, 0xe60000ab, 0xaea4e444, 0x4400008b, 0x00000000, 0x00000003, 0x80808080, 0x00000000, 0xa8b89093, 0x00000000, 0x888888bb, 0x00000000, 0x00010003, 0x00000000, }; u32 sprite_duty_cycle[] = { 0xb03030b0, 0xb0b0b000, 0x0f0c0c0f, 0x01616f00, 0x1f83831f, 0x18989f00, 0x00090d06, 0x030d0c00, 0x00000000, 0x00000000, 0xdfd8d8df, 0x0303df00, 0x07606087, 0xc6662700, 0x00020301, 0x00030300, 0x00000000, 0x00000000, 0xdf43435f, 0x5858df00, 0x07666686, 0xc6662700, 0x00020301, 0x00030300, 0x00000000, 0x00000000, 0xdfd8d8cc, 0x0603c300, 0x07606087, 0xc6662700, 0x00020301, 0x00030300, }; u32 sprite_env_volume[] = { 0x80808080, 0x80808000, 0x0f0c0c0c, 0x0c6c6f00, 0x1f999919, 0x19999f00, 0x00090d06, 0x030d0c00, 0x80808080, 0x80808000, 0x0f01010f, 0x09696f00, 0x1f83831f, 0x13939f00, 0x00090d06, 0x030d0c00, 0x60606060, 0x60606000, 0x1f18181e, 0x18d8df00, 0x3e30303c, 0x30303e00, 0x00131b0c, 0x061b1900, 0xf8c0c0f8, 0x1818f800, 0x3e06063e, 0x26a6be00, 0x7c0c0c7c, 0x4c4d7d00, 0x00263618, 0x0c363200, 0xf8c0c0f8, 0x1818f800, 0x3e323232, 0x32b2be00, 0x7c646464, 0x64657d00, 0x00263618, 0x0c363200, 0xf8c0c0f0, 0xc0c0f800, 0x3e30303c, 0x30b0be00, 0x7c606078, 0x60617d00, 0x00263618, 0x0c363200, 0xc0e0d0c8, 0xf8c0c000, 0x3e323232, 0x32b2be00, 0x7c646464, 0x64657d00, 0x00263618, 0x0c363200, 0xc0e0d0c8, 0xf8c0c000, 0x3e06063e, 0x26a6be00, 0x7c0c0c7c, 0x4c4d7d00, 0x00263618, 0x0c363200, 0xf81818f8, 0xc0c0f800, 0x3e30303c, 0x30b0be00, 0x7c606078, 0x60617d00, 0x00263618, 0x0c363200, 0xf81818f8, 0x9898f800, 0x3e323232, 0x32b2be00, 0x7c646464, 0x64657d00, 0x00263618, 0x0c363200, 0xf81818f8, 0x9898f800, 0x3e06063e, 0x26a6be00, 0x7c0c0c7c, 0x4c4d7d00, 0x00263618, 0x0c363200, 0xf8c0c060, 0x30181800, 0x3e30303c, 0x30b0be00, 0x7c606078, 0x60617d00, 0x00263618, 0x0c363200, 0xf8c8c8f8, 0xc8c8f800, 0x3e323232, 0x32b2be00, 0x7c646464, 0x64657d00, 0x00263618, 0x0c363200, 0xf8c8c8f8, 0xc8c8f800, 0x3e06063e, 0x26a6be00, 0x7c0c0c7c, 0x4c4d7d00, 0x00263618, 0x0c363200, 0xf8c8c8f8, 0xc0c0f800, 0x3e30303c, 0x30b0be00, 0x7c606078, 0x60617d00, 0x00263618, 0x0c363200, 0xec2c2c2c, 0x2c2cec00, 0xfbcbcbcb, 0xcbcbfb00, 0xf0909090, 0x9096f600, 0x0199d961, 0x31d9c900, }; u32 sprite_env_time[] = { 0x80808080, 0x80808000, 0x0f0c0c0c, 0x0c6c6f00, 0x1f999919, 0x19999f00, 0x00090d06, 0x030d0c00, 0x60606060, 0x60606000, 0x181c1a19, 0x1fd8d800, 0x3e30303c, 0x30303e00, 0x00131b0c, 0x061b1900, 0xf8c0c0f8, 0x1818f800, 0x3e32323e, 0x32b2be00, 0x7c0c0c7c, 0x4c4d7d00, 0x00263618, 0x0c363200, 0xc0e0d0c8, 0xf8c0c000, 0x3e30303e, 0x0686be00, 0x7c64647c, 0x60617d00, 0x00263618, 0x0c363200, 0xe06060e0, 0x0000e000, 0xfbc0c063, 0x331b1b00, 0x30303030, 0x30363600, 0x00131b0c, 0x061b1900, 0xe0000080, 0xc0606000, 0x1b1b1b19, 0x18d8d800, 0x30383432, 0x3e303000, 0x00131b0c, 0x061b1900, 0xf8c8c8f8, 0xc8c8f800, 0x3e06063e, 0x30b0be00, 0x7c606030, 0x180d0d00, 0x00263618, 0x0c363200, 0xec2c2c2c, 0x2c2cec00, 0xfbcbcbcb, 0xcbcbfb00, 0xf0909090, 0x9096f600, 0x0199d961, 0x31d9c900, }; u32 sprite_env_sweep_direction[] = { 0x00000000, 0x00000000, 0xc8c8c8c8, 0xc8c8f800, 0x3e32323e, 0x02020200, 0x00000000, 0x00000000, 0xe0202020, 0x2020e000, 0x71cbcbcb, 0xcbcb7100, 0xb2b2b2b2, 0xb2ba9400, 0x0d0f0e0c, 0x0c0c0c00, }; u32 sprite_sweep_time_number[] = { 0x7c646464, 0x64647c00, 0x18181818, 0x18181800, 0x7c60607c, 0x0c0c7c00, 0x7c606078, 0x60607c00, 0x60706864, 0x7c606000, 0x7c0c0c7c, 0x60607c00, 0x7c0c0c7c, 0x4c4c7c00, 0x7c606030, 0x180c0c00, }; u32 sprite_synth_param_selector[] = { 0x07010100, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xe0808000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000101, 0x07000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00008080, 0xe0000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }; u32 sprite_channels[] = { 0xc0404040, 0x404040c0, 0xff005c44, 0xc45c00ff, 0xff002121, 0x212500ff, 0x03020202, 0x02020203, 0xc0404040, 0x404040c0, 0xff00aea2, 0xe2ae00ff, 0xff005050, 0x505200ff, 0x03020202, 0x02020203, 0xc0404040, 0x404040c0, 0xff005751, 0x715700ff, 0xff00a8a8, 0xa8a900ff, 0x03020202, 0x02020203, 0xc0404040, 0x404040c0, 0xff005751, 0x715700ff, 0xff00a8a8, 0xa8c900ff, 0x03020202, 0x02020203, 0xc0404040, 0x404040c0, 0xff007010, 0x301000ff, 0xff000507, 0x050500ff, 0x03020202, 0x02020203, }; u32 sprite_channels_selector[] = { 0x70101000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x0e080800, 0x00000000, 0x00101070, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x0008080e, 0x00000000, }; u32 sprite_default_wave[] = { 0xff013149, 0x850101ff, 0x3f202028, 0x2423203f, 0xff016151, 0x49c501ff, 0x3f202c2a, 0x2928203f, 0xff017d45, 0x45c501ff, 0x3f202828, 0x282f203f, 0xff014911, 0x812501ff, 0x3f202128, 0x2420203f, }; // // Wave data. // u8 sine_wave[] = { 0x89, 0xBC, 0xDE, 0xEF, 0xFE, 0xED, 0xCB, 0x98, 0x76, 0x43, 0x21, 0x10, 0x01, 0x12, 0x34, 0x67, }; u8 saw_wave[16] = { 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, }; u8 square_wave[16] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }; // TODO: Should we split this up in individual trigger structs depending on the // channel? 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. { {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}}, {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}}, {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}}, {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}}, {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}}, {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_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}}, }, // Synth 3. { {true, NOTE_G_5, 8, 4, 0, 2, 0, 0, 0, 3, 0, {0}, {0}}, {false, NOTE_G_5, 8, 4, 0, 2, 0, 0, 0, 3, 0, {0}, {0}}, {false, NOTE_G_5, 8, 4, 0, 2, 0, 0, 0, 3, 0, {0}, {0}}, {false, 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}}, {false, NOTE_G_5, 8, 4, 0, 2, 0, 0, 0, 3, 0, {0}, {0}}, {false, NOTE_G_5, 8, 4, 0, 2, 0, 0, 0, 3, 0, {0}, {0}}, {false, NOTE_G_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}}, {false, NOTE_G_5, 8, 4, 0, 2, 0, 0, 0, 3, 0, {0}, {0}}, {false, NOTE_G_5, 8, 4, 0, 2, 0, 0, 0, 3, 0, {0}, {0}}, {false, NOTE_G_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}}, {false, NOTE_G_5, 8, 4, 0, 2, 0, 0, 0, 3, 0, {0}, {0}}, {false, NOTE_G_5, 8, 4, 0, 2, 0, 0, 0, 3, 0, {0}, {0}}, {false, NOTE_G_5, 8, 4, 0, 2, 0, 0, 0, 3, 0, {0}, {0}}, }, }; // TODO: Support for copy paste anything, contextual. If we are on trigger // selection mode, pressing the copy key (SELECT? SELECT+B?), all synth // parameters will be copied for that trigger. Pressing paste (SELECT+A?) will // update the params. This only works on compatible channels (Not on the wave // channel for example). If an entire channel is copied, all trigs in the // sequence will be copied. We can choose to copy only one of the parameters as // well or the note/duration. // TODO: Allow muting and unmuting channels. Show in grey when muted. // TODO: Show channel beat indicator if a note is being played. // TODO: Enable control on channel 4. // TODO: Research a way of having pattern chains. // TODO: Study how to save patterns and chains. static int bpm = 115; static int step_counter = 0; static Note active_note; void irq_timer_0(void) { { 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); memcpy(SOUND_WAVE_RAM, trig->wave_a, 16); SOUND_WAVE_MODE = SOUND_WAVE_BANK_SELECT(0); memcpy(SOUND_WAVE_RAM, trig->wave_b, 16); 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; } // // Sequencer sprite memory. // // Currently we load all sequencer sprite memory in the VRAM: // // - Note names: 73x2 tiles. // - Sprite trigger button: 8 tiles. // - Current step marker: 1 tile. // - Selected trigger marker: 16 tile. // - Env: Volume label: 4 tiles. // - Env: Volume text: 16x4 tiles // - Env: Time label: 4 tiles. // - Env: Time text: 8x4 tiles // - Env: Direction label: 4 tiles. // - Env: Direction text: 2x4 tiles // TODO: Update from sweep onwards. // // The order of OBJs correspond to: // // - 000-015 step note names. // - 015-031 step trigger steps. // - 032-032 current step marker. // - 033-033 trigger selection indicator. // - 034-034 envelope: volume label. // - 035-035 envelope: volume indicator. // - 036-036 envelope: time label. // - 037-037 envelope: time indicator. // - 038-038 envelope: direction label. // - 039-039 envelope: direction indicator. // - 040-040 envelope: envelope label. // - 041-041 duty-cycle label. // - 042-042 duty-cycle indicator. // - 043-043 sweep: number label. // - 044-044 sweep: number indicator. // - 045-045 sweep: time label. // - 046-046 sweep: time indicator. // - 047-047 sweep: direction label. // - 048-048 sweep: direction indicator. // - 049-049 sweep: sweep label. // - 050-050 synth parameter selector. // - 051-051 channel 1. // - 052-052 channel 2. // - 053-053 channel 3. // - 054-054 channel 4. // - 055-055 RESERVED: FM synth?. // - 056-056 channel selector. // - 057-057 ch3 selector. // - 058-066 ch3 default wave buttons. // size_t obj_counter = 0; typedef struct SeqSprite { u8 id; int x; int y; size_t base_tile; u16 obj_attr_0; u16 obj_attr_1; u16 obj_attr_2; } SeqSprite; typedef enum { SEQ_SELECT_TRIGGER, SEQ_SELECT_CHANNEL, SEQ_SELECT_PARAMETER, } SeqSelect; int trig_selection_loc = 0; int param_selection_loc = 64; int channel_selection_loc = 2; SeqSelect current_selection = SEQ_SELECT_TRIGGER; SeqSprite seq_sprites[66] = {0}; void draw_wave_pattern(u8 *pattern, int x, int y, u16 clr_idx) { for (size_t i = 0; i < 16; ++i) { u8 byte = pattern[i]; u8 first = (byte >> 4) & 0xF; u8 second = byte & 0xF; u8 a = x + i * 4; u8 b = y + 16; put_pixel_m4(a, b - first, clr_idx, backbuffer); put_pixel_m4(a + 1, b - first, clr_idx, backbuffer); put_pixel_m4(a + 2, b - second, clr_idx, backbuffer); put_pixel_m4(a + 3, b - second, clr_idx, backbuffer); } } void init_sequencer_sprites(void) { // Load palette. init_sprite_pal(0, COLOR_WHITE); init_sprite_pal(16, COLOR_CYAN); init_sprite_pal(32, COLOR_RED); init_sprite_pal(48, COLOR_GREY); init_sprites(512); // Sprite note names. size_t sprite_id = load_packed_sprite_data(&sprite_note_names, 2, 73); for (size_t i = 0; i < 16; ++i) { int x = SEQ_TRIG_POS_X + i * SEQ_TRIG_DIST_X; int y = SEQ_TRIG_POS_Y; int base_tile = sprites[sprite_id].tile_start; if (i >= 8) { y += SEQ_TRIG_DIST_Y; x -= 8 * SEQ_TRIG_DIST_X; } seq_sprites[i].x = x; seq_sprites[i].y = y; seq_sprites[i].id = obj_counter++; seq_sprites[i].base_tile = base_tile; seq_sprites[i].obj_attr_0 = OBJ_SHAPE_WIDE | OBJ_HIDDEN | OBJ_Y_COORD(y); seq_sprites[i].obj_attr_1 = OBJ_SIZE_SMALL | OBJ_X_COORD(x); seq_sprites[i].obj_attr_2 = base_tile; } // Trigger boxes. sprite_id = load_packed_sprite_data(&sprite_trigger_button, 8, 1); for (size_t i = 0; i < 16; ++i) { int x = SEQ_TRIG_POS_X + i * SEQ_TRIG_DIST_X; int y = SEQ_TRIG_POS_Y; int base_tile = sprites[sprite_id].tile_start; if (i >= 8) { y += 28; x -= 8 * SEQ_TRIG_DIST_X; } seq_sprites[i + 16].id = obj_counter++; seq_sprites[i + 16].base_tile = base_tile; seq_sprites[i + 16].obj_attr_0 = OBJ_SHAPE_TALL | OBJ_Y_COORD(y); seq_sprites[i + 16].obj_attr_1 = OBJ_SIZE_BIG | OBJ_X_COORD(x); seq_sprites[i + 16].obj_attr_2 = base_tile; } sprite_id = load_packed_sprite_data(&sprite_trigger_active_indicator, 1, 1); { int x = SEQ_TRIG_POS_X; int y = SEQ_TRIG_POS_Y + 15; int base_tile = sprites[sprite_id].tile_start; seq_sprites[32].id = obj_counter++; seq_sprites[32].base_tile = base_tile; seq_sprites[32].obj_attr_0 = OBJ_SHAPE_SQUARE | OBJ_Y_COORD(y) | OBJ_HIDDEN; seq_sprites[32].obj_attr_1 = OBJ_SIZE_SMALL | OBJ_X_COORD(x); seq_sprites[32].obj_attr_2 = base_tile | OBJ_PAL_BANK(1); } sprite_id = load_packed_sprite_data(&sprite_trigger_selection, 16, 1); { int x = SEQ_TRIG_POS_X - 1 + trig_selection_loc * SEQ_TRIG_DIST_X; int y = SEQ_TRIG_POS_Y - 2; int base_tile = sprites[sprite_id].tile_start; if (trig_selection_loc >= 8) { y += SEQ_TRIG_DIST_Y; x -= 8 * SEQ_TRIG_DIST_X; } seq_sprites[33].id = obj_counter++; seq_sprites[33].base_tile = base_tile; seq_sprites[33].obj_attr_0 = OBJ_SHAPE_SQUARE | OBJ_Y_COORD(y) | OBJ_HIDDEN; seq_sprites[33].obj_attr_1 = OBJ_SIZE_BIG | OBJ_X_COORD(x); seq_sprites[33].obj_attr_2 = base_tile | OBJ_PAL_BANK(2); } sprite_id = load_packed_sprite_data(&sprite_env_label_volume, 4, 1); { int x = SEQ_ENV_POS_X; int y = SEQ_ENV_POS_Y; int base_tile = sprites[sprite_id].tile_start; seq_sprites[34].id = obj_counter++; seq_sprites[34].base_tile = base_tile; seq_sprites[34].obj_attr_0 = OBJ_SHAPE_WIDE | OBJ_Y_COORD(y) | OBJ_HIDDEN; seq_sprites[34].obj_attr_1 = OBJ_SIZE_MID | OBJ_X_COORD(x); seq_sprites[34].obj_attr_2 = base_tile; } sprite_id = load_packed_sprite_data(&sprite_env_volume, 4, 16); { int x = SEQ_ENV_POS_X; int y = SEQ_ENV_POS_Y + 8; int base_tile = sprites[sprite_id].tile_start; seq_sprites[35].id = obj_counter++; seq_sprites[35].base_tile = base_tile; seq_sprites[35].obj_attr_0 = OBJ_SHAPE_WIDE | OBJ_Y_COORD(y) | OBJ_HIDDEN; seq_sprites[35].obj_attr_1 = OBJ_SIZE_MID | OBJ_X_COORD(x); seq_sprites[35].obj_attr_2 = base_tile; } sprite_id = load_packed_sprite_data(&sprite_env_sweep_label_time, 4, 1); { int x = SEQ_ENV_POS_X + SEQ_ENV_DIST; int y = SEQ_ENV_POS_Y; int base_tile = sprites[sprite_id].tile_start; seq_sprites[36].id = obj_counter++; seq_sprites[36].base_tile = base_tile; seq_sprites[36].obj_attr_0 = OBJ_SHAPE_WIDE | OBJ_Y_COORD(y) | OBJ_HIDDEN; seq_sprites[36].obj_attr_1 = OBJ_SIZE_MID | OBJ_X_COORD(x); seq_sprites[36].obj_attr_2 = base_tile; } { int x = SEQ_SWEEP_POS_X + SEQ_ENV_DIST; int y = SEQ_SWEEP_POS_Y; int base_tile = sprites[sprite_id].tile_start; seq_sprites[45].id = obj_counter++; seq_sprites[45].base_tile = base_tile; seq_sprites[45].obj_attr_0 = OBJ_SHAPE_WIDE | OBJ_Y_COORD(y) | OBJ_HIDDEN; seq_sprites[45].obj_attr_1 = OBJ_SIZE_MID | OBJ_X_COORD(x); seq_sprites[45].obj_attr_2 = base_tile; } sprite_id = load_packed_sprite_data(&sprite_env_time, 4, 8); { int x = SEQ_ENV_POS_X + SEQ_ENV_DIST; int y = SEQ_ENV_POS_Y + 8; int base_tile = sprites[sprite_id].tile_start; seq_sprites[37].id = obj_counter++; seq_sprites[37].base_tile = base_tile; seq_sprites[37].obj_attr_0 = OBJ_SHAPE_WIDE | OBJ_Y_COORD(y) | OBJ_HIDDEN; seq_sprites[37].obj_attr_1 = OBJ_SIZE_MID | OBJ_X_COORD(x); seq_sprites[37].obj_attr_2 = base_tile; } sprite_id = load_packed_sprite_data(&sprite_env_label_direction, 4, 1); { int x = SEQ_ENV_POS_X + SEQ_ENV_DIST * 2; int y = SEQ_ENV_POS_Y; int base_tile = sprites[sprite_id].tile_start; seq_sprites[38].id = obj_counter++; seq_sprites[38].base_tile = base_tile; seq_sprites[38].obj_attr_0 = OBJ_SHAPE_WIDE | OBJ_Y_COORD(y) | OBJ_HIDDEN; seq_sprites[38].obj_attr_1 = OBJ_SIZE_MID | OBJ_X_COORD(x); seq_sprites[38].obj_attr_2 = base_tile; } { int x = SEQ_SWEEP_POS_X + SEQ_ENV_DIST * 2; int y = SEQ_SWEEP_POS_Y; int base_tile = sprites[sprite_id].tile_start; seq_sprites[47].id = obj_counter++; seq_sprites[47].base_tile = base_tile; seq_sprites[47].obj_attr_0 = OBJ_SHAPE_WIDE | OBJ_Y_COORD(y) | OBJ_HIDDEN; seq_sprites[47].obj_attr_1 = OBJ_SIZE_MID | OBJ_X_COORD(x); seq_sprites[47].obj_attr_2 = base_tile; } sprite_id = load_packed_sprite_data(&sprite_env_sweep_direction, 4, 2); { int x = SEQ_ENV_POS_X + SEQ_ENV_DIST * 2; int y = SEQ_ENV_POS_Y + 8; int base_tile = sprites[sprite_id].tile_start; seq_sprites[39].id = obj_counter++; seq_sprites[39].base_tile = base_tile; seq_sprites[39].obj_attr_0 = OBJ_SHAPE_WIDE | OBJ_Y_COORD(y) | OBJ_HIDDEN; seq_sprites[39].obj_attr_1 = OBJ_SIZE_MID | OBJ_X_COORD(x); seq_sprites[39].obj_attr_2 = base_tile; } { int x = SEQ_SWEEP_POS_X + SEQ_ENV_DIST * 2; int y = SEQ_SWEEP_POS_Y + 8; int base_tile = sprites[sprite_id].tile_start; seq_sprites[48].id = obj_counter++; seq_sprites[48].base_tile = base_tile; seq_sprites[48].obj_attr_0 = OBJ_SHAPE_WIDE | OBJ_Y_COORD(y) | OBJ_HIDDEN; seq_sprites[48].obj_attr_1 = OBJ_SIZE_MID | OBJ_X_COORD(x); seq_sprites[48].obj_attr_2 = base_tile; } sprite_id = load_packed_sprite_data(&sprite_env_label_env, 4, 1); { int x = SEQ_ENV_POS_X + SEQ_ENV_DIST; int y = SEQ_ENV_POS_Y - 10; int base_tile = sprites[sprite_id].tile_start; seq_sprites[40].id = obj_counter++; seq_sprites[40].base_tile = base_tile; seq_sprites[40].obj_attr_0 = OBJ_SHAPE_WIDE | OBJ_Y_COORD(y) | OBJ_HIDDEN; seq_sprites[40].obj_attr_1 = OBJ_SIZE_MID | OBJ_X_COORD(x); seq_sprites[40].obj_attr_2 = base_tile; } sprite_id = load_packed_sprite_data(&sprite_duty_cycle_label, 8, 1); { int x = SEQ_DUTYCYCLE_POS_X; int y = SEQ_DUTYCYCLE_POS_Y; int base_tile = sprites[sprite_id].tile_start; seq_sprites[41].id = obj_counter++; seq_sprites[41].base_tile = base_tile; seq_sprites[41].obj_attr_0 = OBJ_SHAPE_WIDE | OBJ_Y_COORD(y) | OBJ_HIDDEN; seq_sprites[41].obj_attr_1 = OBJ_SIZE_BIG | OBJ_X_COORD(x); seq_sprites[41].obj_attr_2 = base_tile; } sprite_id = load_packed_sprite_data(&sprite_duty_cycle, 4, 4); { int x = SEQ_DUTYCYCLE_POS_X; int y = SEQ_DUTYCYCLE_POS_Y + 16; int base_tile = sprites[sprite_id].tile_start; seq_sprites[42].id = obj_counter++; seq_sprites[42].base_tile = base_tile; seq_sprites[42].obj_attr_0 = OBJ_SHAPE_WIDE | OBJ_Y_COORD(y) | OBJ_HIDDEN; seq_sprites[42].obj_attr_1 = OBJ_SIZE_MID | OBJ_X_COORD(x); seq_sprites[42].obj_attr_2 = base_tile; } sprite_id = load_packed_sprite_data(&sprite_sweep_number_label, 4, 1); { int x = SEQ_SWEEP_POS_X; int y = SEQ_SWEEP_POS_Y; int base_tile = sprites[sprite_id].tile_start; seq_sprites[43].id = obj_counter++; seq_sprites[43].base_tile = base_tile; seq_sprites[43].obj_attr_0 = OBJ_SHAPE_WIDE | OBJ_Y_COORD(y) | OBJ_HIDDEN; seq_sprites[43].obj_attr_1 = OBJ_SIZE_MID | OBJ_X_COORD(x); seq_sprites[43].obj_attr_2 = base_tile; } sprite_id = load_packed_sprite_data(&sprite_sweep_time_number, 1, 8); { int x = SEQ_SWEEP_POS_X + 12; int y = SEQ_SWEEP_POS_Y + 8; int base_tile = sprites[sprite_id].tile_start; seq_sprites[44].id = obj_counter++; seq_sprites[44].base_tile = base_tile; seq_sprites[44].obj_attr_0 = OBJ_SHAPE_SQUARE | OBJ_Y_COORD(y) | OBJ_HIDDEN; seq_sprites[44].obj_attr_1 = OBJ_SIZE_SMALL | OBJ_X_COORD(x); seq_sprites[44].obj_attr_2 = base_tile; } { int x = SEQ_SWEEP_POS_X + 12 + SEQ_ENV_DIST; int y = SEQ_SWEEP_POS_Y + 8; int base_tile = sprites[sprite_id].tile_start; seq_sprites[46].id = obj_counter++; seq_sprites[46].base_tile = base_tile; seq_sprites[46].obj_attr_0 = OBJ_SHAPE_SQUARE | OBJ_Y_COORD(y) | OBJ_HIDDEN; seq_sprites[46].obj_attr_1 = OBJ_SIZE_SMALL | OBJ_X_COORD(x); seq_sprites[46].obj_attr_2 = base_tile; } sprite_id = load_packed_sprite_data(&sprite_sweep_label, 4, 1); { int x = SEQ_SWEEP_POS_X + SEQ_ENV_DIST; int y = SEQ_SWEEP_POS_Y - 10; int base_tile = sprites[sprite_id].tile_start; seq_sprites[49].id = obj_counter++; seq_sprites[49].base_tile = base_tile; seq_sprites[49].obj_attr_0 = OBJ_SHAPE_WIDE | OBJ_Y_COORD(y) | OBJ_HIDDEN; seq_sprites[49].obj_attr_1 = OBJ_SIZE_MID | OBJ_X_COORD(x); seq_sprites[49].obj_attr_2 = base_tile; } sprite_id = load_packed_sprite_data(&sprite_synth_param_selector, 16, 1); { int x = SEQ_ENV_POS_X + 1; int y = SEQ_ENV_POS_Y - 3; int base_tile = sprites[sprite_id].tile_start; seq_sprites[50].id = obj_counter++; seq_sprites[50].base_tile = base_tile; seq_sprites[50].obj_attr_0 = OBJ_SHAPE_SQUARE | OBJ_Y_COORD(y) | OBJ_HIDDEN; seq_sprites[50].obj_attr_1 = OBJ_SIZE_BIG | OBJ_X_COORD(x); seq_sprites[50].obj_attr_2 = base_tile | OBJ_PAL_BANK(2); } sprite_id = load_packed_sprite_data(&sprite_channels, 4, 5); { int x = SEQ_CHANNEL_POS_X; int y = SEQ_CHANNEL_POS_Y; int base_tile = sprites[sprite_id].tile_start; seq_sprites[51].id = obj_counter++; seq_sprites[51].base_tile = base_tile; seq_sprites[51].obj_attr_0 = OBJ_SHAPE_WIDE | OBJ_Y_COORD(y); seq_sprites[51].obj_attr_1 = OBJ_SIZE_MID | OBJ_X_COORD(x); seq_sprites[51].obj_attr_2 = base_tile | OBJ_PAL_BANK(0); } { int x = SEQ_CHANNEL_POS_X; int y = SEQ_CHANNEL_POS_Y + SEQ_CHANNEL_DIST_Y; int base_tile = sprites[sprite_id].tile_start + 4; seq_sprites[52].id = obj_counter++; seq_sprites[52].base_tile = base_tile; seq_sprites[52].obj_attr_0 = OBJ_SHAPE_WIDE | OBJ_Y_COORD(y); seq_sprites[52].obj_attr_1 = OBJ_SIZE_MID | OBJ_X_COORD(x); seq_sprites[52].obj_attr_2 = base_tile | OBJ_PAL_BANK(0); } { int x = SEQ_CHANNEL_POS_X; int y = SEQ_CHANNEL_POS_Y + SEQ_CHANNEL_DIST_Y * 2; int base_tile = sprites[sprite_id].tile_start + 8; seq_sprites[53].id = obj_counter++; seq_sprites[53].base_tile = base_tile; seq_sprites[53].obj_attr_0 = OBJ_SHAPE_WIDE | OBJ_Y_COORD(y); seq_sprites[53].obj_attr_1 = OBJ_SIZE_MID | OBJ_X_COORD(x); seq_sprites[53].obj_attr_2 = base_tile | OBJ_PAL_BANK(0); } { int x = SEQ_CHANNEL_POS_X; int y = SEQ_CHANNEL_POS_Y + SEQ_CHANNEL_DIST_Y * 3; int base_tile = sprites[sprite_id].tile_start + 12; seq_sprites[54].id = obj_counter++; seq_sprites[54].base_tile = base_tile; seq_sprites[54].obj_attr_0 = OBJ_SHAPE_WIDE | OBJ_Y_COORD(y) | OBJ_HIDDEN; seq_sprites[54].obj_attr_1 = OBJ_SIZE_MID | OBJ_X_COORD(x); seq_sprites[54].obj_attr_2 = base_tile | OBJ_PAL_BANK(0); } { int x = SEQ_CHANNEL_POS_X; int y = SEQ_CHANNEL_POS_Y + SEQ_CHANNEL_DIST_Y * 4; int base_tile = sprites[sprite_id].tile_start + 16; seq_sprites[55].id = obj_counter++; seq_sprites[55].base_tile = base_tile; seq_sprites[55].obj_attr_0 = OBJ_SHAPE_WIDE | OBJ_Y_COORD(y) | OBJ_HIDDEN; seq_sprites[55].obj_attr_1 = OBJ_SIZE_MID | OBJ_X_COORD(x); seq_sprites[55].obj_attr_2 = base_tile | OBJ_PAL_BANK(0); } sprite_id = load_packed_sprite_data(&sprite_channels_selector, 8, 1); { int x = SEQ_CHANNEL_POS_X; int y = SEQ_CHANNEL_POS_Y - 2; int base_tile = sprites[sprite_id].tile_start; seq_sprites[56].id = obj_counter++; seq_sprites[56].base_tile = base_tile; seq_sprites[56].obj_attr_0 = OBJ_SHAPE_WIDE | OBJ_Y_COORD(y) | OBJ_HIDDEN; seq_sprites[56].obj_attr_1 = OBJ_SIZE_BIG | OBJ_X_COORD(x); seq_sprites[56].obj_attr_2 = base_tile | OBJ_PAL_BANK(2); } sprite_id = load_packed_sprite_data(&sprite_ch_3_parameter_selection, 3, 1); { int x = SEQ_CH3_PARAM_SEL_X; int y = SEQ_CH3_PARAM_SEL_Y; int base_tile = sprites[sprite_id].tile_start; seq_sprites[57].id = obj_counter++; seq_sprites[57].base_tile = base_tile; seq_sprites[57].obj_attr_0 = OBJ_SHAPE_SQUARE | OBJ_Y_COORD(y); seq_sprites[57].obj_attr_1 = OBJ_SIZE_SMALL | OBJ_X_COORD(x); seq_sprites[57].obj_attr_2 = base_tile | OBJ_PAL_BANK(2); } sprite_id = load_packed_sprite_data(&sprite_default_wave, 2, 4); // Wave A for (size_t i = 0; i < 4; ++i) { int x = SEQ_CH3_PARAM_X + 17 * i; int y = SEQ_CH3_PARAM_Y + 37; int base_tile = sprites[sprite_id].tile_start + i * 2 % 8; seq_sprites[58 + i].id = obj_counter++; seq_sprites[58 + i].base_tile = base_tile; seq_sprites[58 + i].obj_attr_0 = OBJ_SHAPE_WIDE | OBJ_Y_COORD(y); seq_sprites[58 + i].obj_attr_1 = OBJ_SIZE_SMALL | OBJ_X_COORD(x); seq_sprites[58 + i].obj_attr_2 = base_tile | OBJ_PAL_BANK(0); } // Wave B for (size_t i = 0; i < 4; ++i) { int x = SEQ_CH3_PARAM_X + 17 * i + 76; int y = SEQ_CH3_PARAM_Y + 37; int base_tile = sprites[sprite_id].tile_start + i * 2 % 8; seq_sprites[58 + 4 + i].id = obj_counter++; seq_sprites[58 + 4 + i].base_tile = base_tile; seq_sprites[58 + 4 + i].obj_attr_0 = OBJ_SHAPE_WIDE | OBJ_Y_COORD(y); seq_sprites[58 + 4 + i].obj_attr_1 = OBJ_SIZE_SMALL | OBJ_X_COORD(x); seq_sprites[58 + 4 + i].obj_attr_2 = base_tile | OBJ_PAL_BANK(0); } } bool update_params_screen = true; void update_sequencer_sprites(void) { // 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 = seq_sprites[i].base_tile + sequences[channel_selection_loc][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 not triggered // but note name appears, needs to be on a different palette bank. if (!sequences[channel_selection_loc][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; } // 33: Sequence indicator. { int x = SEQ_TRIG_POS_X + step_counter * SEQ_TRIG_DIST_X + 3; int y = SEQ_TRIG_POS_Y + 15; if (step_counter >= 8) { y += SEQ_TRIG_DIST_Y; x -= 8 * SEQ_TRIG_DIST_X; } seq_sprites[32].obj_attr_0 = OBJ_SHAPE_SQUARE | OBJ_Y_COORD(y); // TODO: Mask and update instead. seq_sprites[32].obj_attr_1 = OBJ_SIZE_SMALL | OBJ_X_COORD(x); // TODO: Mask and update instead. } // 34: Trigger selection. { int x = SEQ_TRIG_POS_X - 1 + trig_selection_loc * SEQ_TRIG_DIST_X; int y = SEQ_TRIG_POS_Y - 2; if (trig_selection_loc >= 8) { y += SEQ_TRIG_DIST_Y; x -= 8 * SEQ_TRIG_DIST_X; } if (current_selection == SEQ_SELECT_TRIGGER || current_selection == SEQ_SELECT_PARAMETER) { seq_sprites[33].obj_attr_0 = OBJ_SHAPE_SQUARE | OBJ_Y_COORD(y); } else { seq_sprites[33].obj_attr_0 = OBJ_SHAPE_SQUARE | OBJ_Y_COORD(y) | OBJ_HIDDEN; } seq_sprites[33].obj_attr_1 = OBJ_SIZE_BIG | OBJ_X_COORD(x); int base_tile = seq_sprites[33].base_tile; if (current_selection == SEQ_SELECT_TRIGGER) { seq_sprites[33].obj_attr_2 = base_tile | OBJ_PAL_BANK(2); } else { seq_sprites[33].obj_attr_2 = base_tile | OBJ_PAL_BANK(3); } } // 36: Envelope initial volume. { size_t tile_diff = sequences[channel_selection_loc][trig_selection_loc].env_volume * 4; size_t base_tile = seq_sprites[35].base_tile; size_t tile = base_tile + tile_diff; seq_sprites[35].obj_attr_2 = tile; } // 38: Envelope time. { size_t tile_diff = sequences[channel_selection_loc][trig_selection_loc].env_time * 4; size_t base_tile = seq_sprites[37].base_tile; size_t tile = base_tile + tile_diff; seq_sprites[37].obj_attr_2 = tile; } // 40: Envelope direction. { size_t tile_diff = 0; if (sequences[channel_selection_loc][trig_selection_loc].env_direction == 0) { tile_diff = 4; } size_t base_tile = seq_sprites[39].base_tile; size_t tile = base_tile + tile_diff; seq_sprites[39].obj_attr_2 = tile; } // 43: Duty cycle. { size_t tile_diff = sequences[channel_selection_loc][trig_selection_loc].duty_cycle * 4; size_t base_tile = seq_sprites[42].base_tile; size_t tile = base_tile + tile_diff; seq_sprites[42].obj_attr_2 = tile; } // 45: Sweep number. { size_t tile_diff = sequences[channel_selection_loc][trig_selection_loc].sweep_number; size_t base_tile = seq_sprites[44].base_tile; size_t tile = base_tile + tile_diff; seq_sprites[44].obj_attr_2 = tile; } // 47: Sweep time. { size_t tile_diff = sequences[channel_selection_loc][trig_selection_loc].sweep_time; size_t base_tile = seq_sprites[46].base_tile; size_t tile = base_tile + tile_diff; seq_sprites[46].obj_attr_2 = tile; } // 49: Sweep direction. { size_t tile_diff = sequences[channel_selection_loc][trig_selection_loc].sweep_direction * 4; size_t base_tile = seq_sprites[48].base_tile; size_t tile = base_tile + tile_diff; seq_sprites[48].obj_attr_2 = tile; } // 57: Channel selector. { int x = SEQ_CHANNEL_POS_X; int y = SEQ_CHANNEL_POS_Y - 2; y += (channel_selection_loc % SEQ_N_CHANNELS * SEQ_CHANNEL_DIST_Y); seq_sprites[56].obj_attr_0 = OBJ_SHAPE_WIDE | OBJ_Y_COORD(y); seq_sprites[56].obj_attr_1 = OBJ_SIZE_BIG | OBJ_X_COORD(x); int base_tile = seq_sprites[56].base_tile; if (current_selection == SEQ_SELECT_CHANNEL) { seq_sprites[56].obj_attr_2 = base_tile | OBJ_PAL_BANK(2); } else { seq_sprites[56].obj_attr_2 = base_tile | OBJ_PAL_BANK(3); } } if (channel_selection_loc == 2) { if (update_params_screen) { u8 *wave_a = sequences[channel_selection_loc][trig_selection_loc].wave_a; u8 *wave_b = sequences[channel_selection_loc][trig_selection_loc].wave_b; // Draw wave patterns for this trig. int x = SEQ_CH3_PARAM_X; int y = SEQ_CH3_PARAM_Y; // Wave A. draw_wave_pattern(wave_a, x, y, 2); txt_position(x - 1, y + 20); txt_printf("%02x%02x%02x%02x %02x%02x%02x%02x", wave_a[0], wave_a[1], wave_a[2], wave_a[3], wave_a[4], wave_a[5], wave_a[6], wave_a[7]); txt_position(x - 1, y + 20 + 8); txt_printf("%02x%02x%02x%02x %02x%02x%02x%02x", wave_a[8], wave_a[9], wave_a[10], wave_a[11], wave_a[12], wave_a[13], wave_a[14], wave_a[15]); // Wave B. x += 64 + 12; draw_wave_pattern(wave_b, x, y, 3); txt_position(x - 1, y + 20); txt_printf("%02x%02x%02x%02x %02x%02x%02x%02x", wave_b[0], wave_b[1], wave_b[2], wave_b[3], wave_b[4], wave_b[5], wave_b[6], wave_b[7]); txt_position(x - 1, y + 20 + 8); txt_printf("%02x%02x%02x%02x %02x%02x%02x%02x", wave_b[8], wave_b[9], wave_b[10], wave_b[11], wave_b[12], wave_b[13], wave_b[14], wave_b[15]); // Clear the backbuffer. flip_page(); clear_screen_m4(); } // Update parameter selection. { u8 x_positions[] = { // 32 half bytes (Wave A). 0, 4, 8, 12, 16, 20, 24, 28, 36, 40, 44, 48, 52, 56, 60, 64, 0, 4, 8, 12, 16, 20, 24, 28, 36, 40, 44, 48, 52, 56, 60, 64, // 32 half bytes (Wave B). 76, 80, 84, 88, 92, 96, 100, 104, 112, 116, 120, 124, 128, 132, 136, 140, 76, 80, 84, 88, 92, 96, 100, 104, 112, 116, 120, 124, 128, 132, 136, 140, // Default wave A. 4, 21, 38, 55, // Default wave B. 80, 97, 114, 131, }; u8 y_positions[] = { // 32 half bytes (Wave A) 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, // 32 half bytes (Wave B) 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, // Default wave A. 23, 23, 23, 23, // Default wave B. 23, 23, 23, 23, }; { int x = SEQ_CH3_PARAM_SEL_X + x_positions[param_selection_loc]; int y = SEQ_CH3_PARAM_SEL_Y + y_positions[param_selection_loc]; int base_tile = seq_sprites[57].base_tile; int shape = OBJ_SHAPE_SQUARE; int hidden = 0; if (current_selection != SEQ_SELECT_PARAMETER) { hidden = OBJ_HIDDEN; } if (param_selection_loc >= 64) { shape = OBJ_SHAPE_WIDE; base_tile += 1; } seq_sprites[57].obj_attr_0 = shape | OBJ_Y_COORD(y) | hidden; seq_sprites[57].obj_attr_1 = OBJ_SIZE_SMALL | OBJ_X_COORD(x); seq_sprites[57].obj_attr_2 = base_tile | OBJ_PAL_BANK(2); } // Wave A for (size_t i = 0; i < 4; ++i) { int x = SEQ_CH3_PARAM_X + 17 * i; int y = SEQ_CH3_PARAM_Y + 37; int base_tile = seq_sprites[58 + i].base_tile; int hidden = 0; if (current_selection == SEQ_SELECT_CHANNEL) { hidden = OBJ_HIDDEN; } seq_sprites[58 + i].obj_attr_0 = OBJ_SHAPE_WIDE | OBJ_Y_COORD(y) | hidden; seq_sprites[58 + i].obj_attr_1 = OBJ_SIZE_SMALL | OBJ_X_COORD(x); seq_sprites[58 + i].obj_attr_2 = base_tile | OBJ_PAL_BANK(0); } // Wave B for (size_t i = 0; i < 4; ++i) { int x = SEQ_CH3_PARAM_X + 17 * i + 76; int y = SEQ_CH3_PARAM_Y + 37; int base_tile = seq_sprites[58 + 4 + i].base_tile; int hidden = 0; if (current_selection == SEQ_SELECT_CHANNEL) { hidden = OBJ_HIDDEN; } seq_sprites[58 + 4 + i].obj_attr_0 = OBJ_SHAPE_WIDE | OBJ_Y_COORD(y) | hidden; seq_sprites[58 + 4 + i].obj_attr_1 = OBJ_SIZE_SMALL | OBJ_X_COORD(x); seq_sprites[58 + 4 + i].obj_attr_2 = base_tile | OBJ_PAL_BANK(0); } } } if (channel_selection_loc == 0) { if (current_selection == SEQ_SELECT_TRIGGER || current_selection == SEQ_SELECT_PARAMETER) { for (size_t i = 34; i < 50; ++i) { seq_sprites[i].obj_attr_0 &= ~OBJ_HIDDEN; } } } if (channel_selection_loc == 1) { if (current_selection == SEQ_SELECT_TRIGGER || current_selection == SEQ_SELECT_PARAMETER) { for (size_t i = 34; i <= 42; ++i) { seq_sprites[i].obj_attr_0 &= ~OBJ_HIDDEN; } } } if (channel_selection_loc == 0 || channel_selection_loc == 1) { // 51: Parameter selector. int x = SEQ_ENV_POS_X + 1 + (param_selection_loc % 7) * SEQ_ENV_DIST; int y = SEQ_ENV_POS_Y - 3; int hidden = 0; if (current_selection != SEQ_SELECT_PARAMETER) { hidden = OBJ_HIDDEN; } seq_sprites[50].obj_attr_0 = OBJ_SHAPE_SQUARE | OBJ_Y_COORD(y) | hidden; seq_sprites[50].obj_attr_1 = OBJ_SIZE_BIG | OBJ_X_COORD(x); } if (current_selection == SEQ_SELECT_CHANNEL && update_params_screen) { // TODO: Hide all parameter control sprites for now, we unhide it later. // Very inefficient but we may change the parameter rendering of all // modes to use bitmaps instead of sprites later. for (size_t i = 34; i <= 50; ++i) { seq_sprites[i].obj_attr_0 |= OBJ_HIDDEN; } for (size_t i = 58; i <= 66; ++i) { seq_sprites[i].obj_attr_0 |= OBJ_HIDDEN; } flip_page(); clear_screen_m4(); } update_params_screen = false; } void handle_sequencer_input(void) { SeqTrigger *trig = &sequences[channel_selection_loc][trig_selection_loc]; if (current_selection == SEQ_SELECT_TRIGGER) { if (key_pressed(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_pressed(KEY_RIGHT)) { if (trig_selection_loc != 7) { trig_selection_loc = MIN(trig_selection_loc + 1, 15); } } else if (key_pressed(KEY_UP) || key_pressed(KEY_DOWN)) { trig_selection_loc = (trig_selection_loc + 8) % 16; } else if (key_pressed(KEY_B)) { trig->trigger ^= 1; } else if (key_pressed(KEY_L)) { if (trig->trigger) { trig->note = MAX(trig->note - 1, NOTE_C_2); } } else if (key_pressed(KEY_R)) { if (trig->trigger) { trig->note = MIN( trig->note + 1, NOTE_C_8); } } else if (key_pressed(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_pressed(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_pressed(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_pressed(KEY_L) || key_pressed(KEY_R)) { int inc; if (key_pressed(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_pressed(KEY_LEFT) || key_pressed(KEY_RIGHT)) { int inc = 0; int loc = param_selection_loc; if (key_pressed(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_pressed(KEY_UP) || key_pressed(KEY_DOWN)) { int inc = 0; int loc = param_selection_loc; if (key_pressed(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_pressed(KEY_R) || key_pressed(KEY_L)) { int odd = param_selection_loc & 0x1; int inc; if (key_pressed(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){ memcpy(&trig->wave_a, &sine_wave, 16); } else if (param_selection_loc == 65){ memcpy(&trig->wave_a, &saw_wave, 16); } else if (param_selection_loc == 66){ memcpy(&trig->wave_a, &square_wave, 16); } else if (param_selection_loc == 67){ u32 rand_wave[4] = { rng32(), rng32(), rng32(), rng32(), }; memcpy(&trig->wave_a, &rand_wave, 16); } else if (param_selection_loc == 68){ memcpy(&trig->wave_b, &sine_wave, 16); } else if (param_selection_loc == 69){ memcpy(&trig->wave_b, &saw_wave, 16); } else if (param_selection_loc == 70){ memcpy(&trig->wave_b, &square_wave, 16); } else if (param_selection_loc == 71){ u32 rand_wave[4] = { rng32(), rng32(), rng32(), rng32(), }; memcpy(&trig->wave_b, &rand_wave, 16); } } } // Go back to trigger selection. if (key_pressed(KEY_A)) { current_selection = SEQ_SELECT_TRIGGER; } // Enable disable trigger. if (key_pressed(KEY_B)) { trig->trigger ^= 1; } } else if (current_selection == SEQ_SELECT_CHANNEL) { if (key_pressed(KEY_RIGHT)) { current_selection = SEQ_SELECT_TRIGGER; trig_selection_loc = 0; param_selection_loc = 0; } if (key_pressed(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_pressed(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_pressed(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_pressed(KEY_SELECT)) { TIMER_CTRL_0 ^= TIMER_CTRL_ENABLE; SOUND_SQUARE1_CTRL = 0; SOUND_SQUARE2_CTRL = 0; SOUND_WAVE_CTRL = 0; } if (key_pressed(KEY_LEFT) || key_pressed(KEY_RIGHT) || key_pressed(KEY_UP) || key_pressed(KEY_DOWN) || key_pressed(KEY_L) || key_pressed(KEY_R) ) { update_params_screen = true; } } void render_sequencer_sprites(void) { 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; } } void init_sequencer() { init_sequencer_sprites(); SOUND_STATUS = SOUND_ENABLE; SOUND_DMG_MASTER = sound_volume( SOUND_SQUARE1 | SOUND_SQUARE2 | SOUND_WAVE, 3); SOUND_DSOUND_MASTER = SOUND_DMG25; }