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