1#![allow(dead_code)]
7
8extern crate alloc;
9use alloc::string::String;
10
11use crate::kernel::audio::Audio;
12use crate::kernel::draw::{Color, Screen};
13use crate::kernel::window_mgr::App;
14
15struct PresetSong {
16 title: &'static str,
17 mml: &'static str,
18}
19
20static PRESET_SONGS: &[PresetSong] = &[
21 PresetSong {
22 title: "KiraKira Star",
23 mml: "T120 L4 O4 C C G G A A G2 F F E E D D C2 G G F F E E D2 G G F F E E D2 C C G G A A G2 F F E E D D C2",
24 },
25 PresetSong {
26 title: "Fur Elise",
27 mml: "T150 L8 O5 E D# E D# E O4 B O5 D C O4 A2 A C E A B2 B E G# B O5 C2 O5 E D# E D# E O4 B O5 D C O4 A2",
28 },
29 PresetSong {
30 title: "Atmos Symphony",
31 mml: "T140 L8 O4 E A O5 C2 O4 B A O5 C O4 B2 B O5 D F2 E D F E2 E A O5 C2 O4 B A O5 E O5 D2 O5 D C O4 B O5 C O4 A2",
32 },
33];
34
35pub struct MusicApp {
36 selected_song: usize,
37 last_win_w: u32,
38 last_win_h: u32,
39}
40
41impl Default for MusicApp {
42 fn default() -> Self {
43 Self::new()
44 }
45}
46
47impl MusicApp {
48 pub fn new() -> Self {
49 Self {
50 selected_song: 0,
51 last_win_w: 450,
52 last_win_h: 300,
53 }
54 }
55}
56
57impl App for MusicApp {
58 fn name(&self) -> &str {
59 "music"
60 }
61
62 fn draw(&mut self, screen: &Screen, win_x: u32, win_y: u32, win_w: u32, win_h: u32) {
63 self.last_win_w = win_w;
64 self.last_win_h = win_h;
65
66 let config = crate::kernel::config::get_config();
67
68 screen.boxfill(
70 win_x,
71 win_y,
72 win_x + win_w,
73 win_y + win_h,
74 Color(config.theme.terminal_bg),
75 );
76
77 screen.draw_string_vector(
79 win_x + 15,
80 win_y + 15,
81 "ATMOS SOUND SYSTEM",
82 Color(config.theme.success_fg),
83 16,
84 );
85 screen.boxfill(
86 win_x + 15,
87 win_y + 35,
88 win_x + win_w - 15,
89 win_y + 36,
90 Color(config.theme.tab_area_border),
91 );
92
93 screen.draw_string_vector(
95 win_x + 15,
96 win_y + 45,
97 "Select Song:",
98 Color(config.theme.fn_bar_dim_fg),
99 14,
100 );
101 let list_y = win_y + 65;
102 let line_h = 24u32;
103
104 for (i, song) in PRESET_SONGS.iter().enumerate() {
105 let y = list_y + i as u32 * line_h;
106 let is_selected = i == self.selected_song;
107
108 let bg_color = if is_selected {
109 Color(config.theme.button_active_bg)
110 } else {
111 Color(config.theme.ui_overlay_bg)
112 };
113 let fg_color = if is_selected {
114 Color(config.theme.button_active_fg)
115 } else {
116 Color(config.theme.terminal_fg)
117 };
118
119 screen.boxfill(win_x + 15, y, win_x + 240, y + line_h - 2, bg_color);
120 let label = alloc::format!("{}. {}", i + 1, song.title);
121 screen.draw_string_vector(win_x + 22, y + 4, &label, fg_color, 14);
122 }
123
124 let btn_y = win_y + 155;
126
127 screen.boxfill(
129 win_x + 15,
130 btn_y,
131 win_x + 120,
132 btn_y + 32,
133 Color(config.theme.success_fg),
134 );
135 screen.draw_string_vector(win_x + 48, btn_y + 8, "Play", Color(config.theme.browser_fg), 14);
136
137 screen.boxfill(
139 win_x + 135,
140 btn_y,
141 win_x + 240,
142 btn_y + 32,
143 Color(config.theme.error_fg),
144 );
145 screen.draw_string_vector(win_x + 168, btn_y + 8, "Stop", Color(config.theme.browser_fg), 14);
146
147 let eq_x0 = win_x + 265;
149 let eq_y0 = win_y + 55;
150 let eq_w = 150u32;
151 let eq_h = 135u32;
152
153 screen.boxfill(eq_x0, eq_y0, eq_x0 + eq_w, eq_y0 + eq_h, Color(config.theme.ui_overlay_bg));
155
156 let is_playing = Audio::is_playing();
157 let ticks = crate::kernel::timer::get_ticks();
158
159 let num_bars = 5;
161 let bar_width = 18u32;
162 let bar_gap = 8u32;
163
164 for bar in 0..num_bars {
165 let bx0 = eq_x0 + 15 + bar * (bar_width + bar_gap);
166 let bx1 = bx0 + bar_width;
167
168 let bar_height = if is_playing {
170 let seed = ticks.wrapping_add(bar as usize * 47);
171 let mut hash = seed ^ (seed >> 13);
172 hash = hash.wrapping_mul(0x85ebca6b);
173 hash ^= hash >> 15;
174 10 + (hash % 105) as u32
176 } else {
177 5
179 };
180
181 let by1 = eq_y0 + eq_h - 10;
182 let by0 = by1.saturating_sub(bar_height);
183
184 for row_y in by0..by1 {
186 let ratio = (by1 - row_y) as f32 / eq_h as f32;
188 let r = (ratio * 16.0) as u32;
189 let g = (185.0 - ratio * 80.0) as u32;
190 let b = (129.0 + ratio * 120.0) as u32;
191 let grad_color = Color(0xFF000000 | (r << 16) | (g << 8) | b);
192
193 screen.boxfill(bx0, row_y, bx1, row_y + 1, grad_color);
194 }
195 }
196
197 let status_y = win_y + win_h - 26;
199 screen.boxfill(
200 win_x,
201 status_y,
202 win_x + win_w,
203 win_y + win_h,
204 Color(config.theme.ui_overlay_bg),
205 );
206
207 let status_text = if is_playing {
208 alloc::format!("Now Playing: {}", PRESET_SONGS[self.selected_song].title)
209 } else {
210 String::from("Status: Stopped")
211 };
212 screen.draw_string_vector(
213 win_x + 15,
214 status_y + 6,
215 &status_text,
216 Color(config.theme.fn_bar_dim_fg),
217 13,
218 );
219 }
220
221 fn on_mouse(
222 &mut self,
223 local_x: i32,
224 local_y: i32,
225 btn_left: bool,
226 _btn_right: bool,
227 _wheel: i32,
228 ) {
229 if !btn_left {
230 return;
231 }
232
233 let list_y = 65;
235 let line_h = 24;
236 if (15..=240).contains(&local_x) && local_y >= list_y {
237 let row = (local_y - list_y) / line_h;
238 if row >= 0 && row < PRESET_SONGS.len() as i32 {
239 self.selected_song = row as usize;
240 return;
241 }
242 }
243
244 let btn_y = 155;
246 if (15..=120).contains(&local_x) && local_y >= btn_y && local_y <= btn_y + 32 {
248 Audio::stop_mml();
250 Audio::play_mml(PRESET_SONGS[self.selected_song].mml);
252 crate::info!(
253 "MusicPlayer: Started playing {}",
254 PRESET_SONGS[self.selected_song].title
255 );
256 return;
257 }
258
259 if (135..=240).contains(&local_x) && local_y >= btn_y && local_y <= btn_y + 32 {
261 Audio::stop_mml();
262 crate::info!("[AUD] MusicPlayer: Music stopped.");
263 }
264 }
265
266 fn on_key(&mut self, _keycode: u8, _pressed: bool, _ascii: Option<char>) {
267 }
269}