old school music tracker
at dev 202 lines 7.7 kB view raw
1use std::{io::Write, str::from_utf8}; 2 3use font8x8::UnicodeFonts; 4use torque_tracker_engine::{ 5 PlaybackSettings, audio_processing::playback::PlaybackPosition, project::pattern::Pattern, 6}; 7 8use crate::{ 9 coordinates::{CharPosition, CharRect}, 10 draw_buffer::DrawBuffer, 11}; 12 13#[derive(Debug, Clone)] 14pub enum HeaderEvent { 15 SetCursorRow(u16), 16 SetMaxCursorRow(u16), 17 SetPattern(u8), 18 SetMaxCursorPattern(u8), 19 SetOrder(u16), 20 SetOrderLen(u16), 21 SetSample(u8, Box<str>), 22 SetSpeed(usize), 23 SetTempo(usize), 24 SetPlayback(Option<PlaybackPosition>), 25} 26 27#[derive(Debug)] 28pub struct Header { 29 row: u16, 30 max_row: u16, 31 pattern: u8, 32 max_pattern: u8, 33 order: u16, 34 order_len: u16, 35 selected_sample: (u8, Box<str>), 36 playback: Option<PlaybackPosition>, 37} 38 39impl Default for Header { 40 fn default() -> Self { 41 Self { 42 row: 0, 43 max_row: Pattern::DEFAULT_ROWS, 44 pattern: 0, 45 max_pattern: 0, 46 order: 0, 47 order_len: 0, 48 selected_sample: (0, Box::from("")), 49 playback: None, 50 } 51 } 52} 53 54impl Header { 55 pub fn play_current_pattern(&self) -> PlaybackSettings { 56 PlaybackSettings::Pattern { 57 idx: self.pattern, 58 should_loop: true, 59 } 60 } 61 62 pub fn play_current_order(&self) -> PlaybackSettings { 63 PlaybackSettings::Order { 64 idx: self.order, 65 should_loop: true, 66 } 67 } 68 69 /// Header always needs a redraw after processing an event 70 pub fn process_event(&mut self, event: HeaderEvent) { 71 match event { 72 HeaderEvent::SetCursorRow(r) => self.row = r, 73 HeaderEvent::SetPattern(p) => self.pattern = p, 74 HeaderEvent::SetOrder(o) => self.order = o, 75 HeaderEvent::SetOrderLen(l) => self.order_len = l, 76 HeaderEvent::SetSample(i, n) => { 77 self.selected_sample.0 = i; 78 self.selected_sample.1 = n 79 } 80 HeaderEvent::SetSpeed(_) => todo!(), 81 HeaderEvent::SetTempo(_) => todo!(), 82 HeaderEvent::SetMaxCursorRow(r) => self.max_row = r, 83 HeaderEvent::SetMaxCursorPattern(p) => self.max_pattern = p, 84 HeaderEvent::SetPlayback(p) => self.playback = p, 85 } 86 } 87 88 pub fn draw(&self, draw_buffer: &mut DrawBuffer) { 89 let mut buf = [0; 3]; 90 // row 91 let mut curse: std::io::Cursor<&mut [u8]> = std::io::Cursor::new(&mut buf); 92 write!(&mut curse, "{:03}", self.row).unwrap(); 93 draw_buffer.draw_string(from_utf8(&buf).unwrap(), CharPosition::new(12, 7), 5, 0); 94 // row max 95 let mut curse: std::io::Cursor<&mut [u8]> = std::io::Cursor::new(&mut buf); 96 write!(&mut curse, "{:03}", self.max_row).unwrap(); 97 draw_buffer.draw_string(from_utf8(&buf).unwrap(), CharPosition::new(16, 7), 5, 0); 98 // pattern 99 let mut curse: std::io::Cursor<&mut [u8]> = std::io::Cursor::new(&mut buf); 100 write!(&mut curse, "{:03}", self.pattern).unwrap(); 101 draw_buffer.draw_string(from_utf8(&buf).unwrap(), CharPosition::new(12, 6), 5, 0); 102 // max pattern 103 let mut curse: std::io::Cursor<&mut [u8]> = std::io::Cursor::new(&mut buf); 104 write!(&mut curse, "{:03}", self.max_pattern).unwrap(); 105 draw_buffer.draw_string(from_utf8(&buf).unwrap(), CharPosition::new(16, 6), 5, 0); 106 107 // order 108 let mut curse: std::io::Cursor<&mut [u8]> = std::io::Cursor::new(&mut buf); 109 write!(&mut curse, "{:03}", self.order).unwrap(); 110 draw_buffer.draw_string(from_utf8(&buf).unwrap(), CharPosition::new(12, 5), 5, 0); 111 // order len 112 let mut curse: std::io::Cursor<&mut [u8]> = std::io::Cursor::new(&mut buf); 113 write!(&mut curse, "{:03}", self.order_len).unwrap(); 114 draw_buffer.draw_string(from_utf8(&buf).unwrap(), CharPosition::new(16, 5), 5, 0); 115 // sample 116 draw_buffer.draw_string_length(&self.selected_sample.1, CharPosition::new(53, 3), 24, 5, 0); 117 let mut curse: std::io::Cursor<&mut [u8]> = std::io::Cursor::new(&mut buf); 118 write!(&mut curse, "{:02}", self.selected_sample.0).unwrap(); 119 draw_buffer.draw_string( 120 from_utf8(&buf[..2]).unwrap(), 121 CharPosition::new(50, 3), 122 5, 123 0, 124 ); 125 // playback position 126 if let Some(position) = self.playback { 127 // Window width - 20 128 let mut buf = [b' '; 60]; 129 let mut curse: std::io::Cursor<&mut [u8]> = std::io::Cursor::new(&mut buf); 130 write!(&mut curse, "Playing, ").unwrap(); 131 if let Some(order) = position.order { 132 write!(&mut curse, "Order: {}/{}, ", order, self.order_len).unwrap(); 133 } 134 // TODO: figure out how to get the row count of the currently playing pattern in here 135 write!( 136 &mut curse, 137 "Pattern: {}, Row: {}", 138 position.pattern, position.row 139 ) 140 .unwrap(); 141 // TODO: add voice count 142 let string = from_utf8(&buf).unwrap(); 143 for (index, char) in string.char_indices() { 144 let char_color = if char.is_ascii_digit() { 3 } else { 0 }; 145 draw_buffer.draw_char( 146 font8x8::BASIC_FONTS.get(char).unwrap(), 147 CharPosition::new(2 + u8::try_from(index).unwrap(), 9), 148 char_color, 149 2, 150 ); 151 } 152 } else { 153 draw_buffer.draw_rect(2, CharRect::new(9, 9, 2, 62)); 154 } 155 } 156 157 pub fn draw_constant(&self, buffer: &mut DrawBuffer) { 158 buffer.draw_rect(2, CharRect::new(0, 11, 0, 79)); 159 buffer.draw_string("Torque Tracker", CharPosition::new(34, 1), 0, 2); 160 buffer.draw_string("Song Name", CharPosition::new(2, 3), 0, 2); 161 buffer.draw_string("File Name", CharPosition::new(2, 4), 0, 2); 162 buffer.draw_string("Order", CharPosition::new(6, 5), 0, 2); 163 buffer.draw_string("Pattern", CharPosition::new(4, 6), 0, 2); 164 buffer.draw_string("Row", CharPosition::new(8, 7), 0, 2); 165 buffer.draw_string("Speed/Tempo", CharPosition::new(38, 4), 0, 2); 166 buffer.draw_string("Octave", CharPosition::new(43, 5), 0, 2); 167 buffer.draw_string( 168 "F1...Help F9.....Load", 169 CharPosition::new(21, 6), 170 0, 171 2, 172 ); 173 buffer.draw_string( 174 "ESC..Main Menu F5/F8..Play / Stop", 175 CharPosition::new(21, 7), 176 0, 177 2, 178 ); 179 buffer.draw_string("Time", CharPosition::new(63, 9), 0, 2); 180 buffer.draw_string("/", CharPosition::new(15, 5), 1, 0); 181 buffer.draw_string("/", CharPosition::new(15, 6), 1, 0); 182 buffer.draw_string("/", CharPosition::new(15, 7), 1, 0); 183 buffer.draw_string("/", CharPosition::new(53, 4), 1, 0); 184 buffer.draw_string(":", CharPosition::new(52, 3), 7, 0); 185 // TODO: Not actually constant as it changes between Sample and Instrument mode 186 buffer.draw_string("Sample", CharPosition::new(43, 3), 0, 2); 187 } 188 189 /// Returns the ID of the header root. This should be added as a child of the outer node 190 #[cfg(feature = "accesskit")] 191 pub fn build_tree( 192 &self, 193 tree: &mut Vec<(accesskit::NodeId, accesskit::Node)>, 194 ) -> accesskit::NodeId { 195 use accesskit::{Node, NodeId, Role}; 196 const HEADER_ID: NodeId = NodeId(1); 197 let root = Node::new(Role::Header); 198 199 tree.push((HEADER_ID, root)); 200 HEADER_ID 201 } 202}