Monorepo for Aesthetic.Computer aesthetic.computer
at main 206 lines 5.2 kB view raw
1// BUILD_TARGET: CGB 2// Music box using sprites for colored bars 3#include <gb/gb.h> 4#include <gb/cgb.h> 5#include <stdint.h> 6 7#include "aesthetic_boot.h" 8 9// Note definitions 10enum notes { 11 C3 = 36, Cd3, D3, Dd3, E3, F3, Fd3, G3, Gd3, A3, Ad3, B3, 12 C4, Cd4, D4, Dd4, E4, F4, Fd4, G4, Gd4, A4, Ad4, B4, 13 C5, Cd5, D5, Dd5, E5, F5, Fd5, G5, Gd5, A5, Ad5, B5, 14 REST 15}; 16 17const uint16_t frequencies[] = { 18 1798, 1812, 1825, 1837, 1849, 1860, 1871, 1881, 1890, 1899, 1907, 1915, 19 1923, 1930, 1936, 1943, 1949, 1954, 1959, 1964, 1969, 1974, 1978, 1982, 20 1985, 1988, 1992, 1995, 1998, 2001, 2004, 2006, 2009, 2011, 2013, 2015, 21 0 22}; 23 24// Twinkle Twinkle melody 25const uint8_t melody[] = { 26 C4, C4, G4, G4, A4, A4, G4, REST, 27 F4, F4, E4, E4, D4, D4, C4, REST, 28 G4, G4, F4, F4, E4, E4, D4, REST, 29 G4, G4, F4, F4, E4, E4, D4, REST, 30 C4, C4, G4, G4, A4, A4, G4, REST, 31 F4, F4, E4, E4, D4, D4, C4, REST, 32 0xFF 33}; 34 35// Sprite tile: 8x8 filled square 36const uint8_t sprite_tile[] = { 37 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 38 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 39}; 40 41// OBJ palettes (7 colors + gray) 42const uint16_t obj_palettes[] = { 43 // Palette 0: Red (C) 44 RGB_BLACK, RGB(31, 0, 0), RGB(31, 0, 0), RGB(31, 0, 0), 45 // Palette 1: Orange (D) 46 RGB_BLACK, RGB(31, 15, 0), RGB(31, 15, 0), RGB(31, 15, 0), 47 // Palette 2: Yellow (E) 48 RGB_BLACK, RGB(31, 31, 0), RGB(31, 31, 0), RGB(31, 31, 0), 49 // Palette 3: Green (F) 50 RGB_BLACK, RGB(0, 31, 0), RGB(0, 31, 0), RGB(0, 31, 0), 51 // Palette 4: Cyan (G) 52 RGB_BLACK, RGB(0, 31, 31), RGB(0, 31, 31), RGB(0, 31, 31), 53 // Palette 5: Blue (A) 54 RGB_BLACK, RGB(0, 0, 31), RGB(0, 0, 31), RGB(0, 0, 31), 55 // Palette 6: Purple (B) 56 RGB_BLACK, RGB(31, 0, 31), RGB(31, 0, 31), RGB(31, 0, 31), 57 // Palette 7: Gray (played notes) 58 RGB_BLACK, RGB(10, 10, 10), RGB(10, 10, 10), RGB(10, 10, 10), 59}; 60 61#define FRAMES_PER_NOTE 24 62#define SPRITE_BASE_Y 144 // Bottom of screen 63 64uint8_t current_note_index = 0; 65uint8_t current_note = REST; 66uint8_t frame_counter = 0; 67uint8_t auto_play = 1; 68uint8_t sprite_count = 0; 69 70void play_note(uint8_t note) { 71 if(note == REST || note == 0xFF) { 72 NR12_REG = 0x00; 73 NR14_REG = 0x80; 74 return; 75 } 76 uint16_t freq = frequencies[note - C3]; 77 NR10_REG = 0x00; 78 NR11_REG = 0x81; 79 NR12_REG = 0xF3; 80 NR13_REG = (uint8_t)(freq & 0xFF); 81 NR14_REG = 0x80 | (uint8_t)((freq >> 8) & 0x07); 82} 83 84uint8_t get_palette_for_note(uint8_t note) { 85 if(note == REST || note == 0xFF) return 7; 86 uint8_t palette = ((note - C3) % 12); 87 if(palette > 6) palette = palette - 5; 88 return palette; 89} 90 91void draw_note_sprites() { 92 sprite_count = 0; 93 94 for(uint8_t i = 0; melody[i] != 0xFF && sprite_count < 40; i++) { 95 uint8_t note = melody[i]; 96 if(note == REST) continue; 97 98 // Calculate bar height (0-17 tiles = 0-136 pixels) 99 uint8_t height_tiles = ((note - C3) * 17) / 36; 100 if(height_tiles > 17) height_tiles = 17; 101 uint8_t height_px = height_tiles * 8; 102 103 // X position: each note gets 8 pixels wide 104 uint8_t x = 8 + (i * 8); 105 106 // Palette: color or gray 107 uint8_t palette = (i == current_note_index) ? get_palette_for_note(note) : 7; 108 109 // Draw sprites from bottom up 110 uint8_t y = SPRITE_BASE_Y - height_px; 111 for(uint8_t py = 0; py < height_px && sprite_count < 40; py += 8) { 112 set_sprite_tile(sprite_count, 0); 113 set_sprite_prop(sprite_count, palette); 114 move_sprite(sprite_count, x, y + py); 115 sprite_count++; 116 } 117 } 118 119 // Hide unused sprites 120 for(uint8_t i = sprite_count; i < 40; i++) { 121 move_sprite(i, 0, 0); 122 } 123} 124 125void init_graphics() { 126 DISPLAY_OFF; 127 128 // Load sprite tile 129 set_sprite_data(0, 1, sprite_tile); 130 131 // Set OBJ palettes 132 set_sprite_palette(0, 8, obj_palettes); 133 134 // Clear background to black 135 BGP_REG = 0xE4; 136 137 // Draw initial sprites 138 draw_note_sprites(); 139 140 SHOW_SPRITES; 141 DISPLAY_ON; 142} 143 144void main() { 145 show_aesthetic_boot(0); 146 147 // Sound setup 148 NR52_REG = 0x80; 149 NR51_REG = 0x11; 150 NR50_REG = 0x77; 151 152 init_graphics(); 153 154 // Start playing 155 current_note = melody[0]; 156 play_note(current_note); 157 158 while(1) { 159 vsync(); 160 frame_counter++; 161 162 uint8_t joy = joypad(); 163 164 // Manual control 165 if(joy & J_RIGHT) { 166 if(melody[current_note_index + 1] != 0xFF) { 167 current_note_index++; 168 current_note = melody[current_note_index]; 169 play_note(current_note); 170 draw_note_sprites(); 171 frame_counter = 0; 172 } 173 auto_play = 0; 174 } 175 176 if(joy & J_LEFT) { 177 if(current_note_index > 0) { 178 current_note_index--; 179 current_note = melody[current_note_index]; 180 play_note(current_note); 181 draw_note_sprites(); 182 frame_counter = 0; 183 } 184 auto_play = 0; 185 } 186 187 if(joy & J_A) { 188 auto_play = 1; 189 frame_counter = 0; 190 } 191 192 // Auto-play 193 if(auto_play && frame_counter >= FRAMES_PER_NOTE) { 194 if(melody[current_note_index + 1] != 0xFF) { 195 current_note_index++; 196 } else { 197 current_note_index = 0; 198 } 199 200 current_note = melody[current_note_index]; 201 play_note(current_note); 202 draw_note_sprites(); 203 frame_counter = 0; 204 } 205 } 206}