old school music tracker
at dev 665 lines 28 kB view raw
1use std::{io::Write, str::from_utf8}; 2 3use torque_tracker_engine::project::{ 4 event_command::NoteCommand, 5 note_event::{Note, NoteEvent, VolumeEffect}, 6 pattern::{InPatternPosition, Pattern, PatternOperation}, 7 song::{Song, SongOperation}, 8}; 9use winit::{ 10 event_loop::EventLoopProxy, 11 keyboard::{Key, NamedKey, SmolStr}, 12}; 13 14use crate::{ 15 EXECUTOR, EventQueue, GlobalEvent, SONG_MANAGER, 16 coordinates::{CharPosition, CharRect}, 17 header::HeaderEvent, 18 send_song_op, 19}; 20 21use super::{Page, PageResponse}; 22 23#[derive(Debug, Clone, Copy, PartialEq, Eq)] 24enum InEventPosition { 25 Note, 26 Octave, 27 Sample1, 28 Sample2, 29 VolPan1, 30 VolPan2, 31 Effect1, 32 Effect2, 33 Effect3, 34} 35 36impl InEventPosition { 37 fn to_right(self) -> Option<Self> { 38 match self { 39 InEventPosition::Note => Some(Self::Octave), 40 InEventPosition::Octave => Some(Self::Sample1), 41 InEventPosition::Sample1 => Some(Self::Sample2), 42 InEventPosition::Sample2 => Some(Self::VolPan1), 43 InEventPosition::VolPan1 => Some(Self::VolPan2), 44 InEventPosition::VolPan2 => Some(Self::Effect1), 45 InEventPosition::Effect1 => Some(Self::Effect2), 46 InEventPosition::Effect2 => Some(Self::Effect3), 47 InEventPosition::Effect3 => None, 48 } 49 } 50 51 fn to_left(self) -> Option<Self> { 52 match self { 53 InEventPosition::Note => None, 54 InEventPosition::Octave => Some(Self::Note), 55 InEventPosition::Sample1 => Some(Self::Octave), 56 InEventPosition::Sample2 => Some(Self::Sample1), 57 InEventPosition::VolPan1 => Some(Self::Sample2), 58 InEventPosition::VolPan2 => Some(Self::VolPan1), 59 InEventPosition::Effect1 => Some(Self::VolPan2), 60 InEventPosition::Effect2 => Some(Self::Effect1), 61 InEventPosition::Effect3 => Some(Self::Effect2), 62 } 63 } 64} 65 66fn get_note_from_key(key: &winit::keyboard::SmolStr, octave: u8) -> Option<Note> { 67 // TODO: keyboard layouts fuckk me. 68 #[allow(clippy::identity_op, clippy::zero_prefixed_literal)] 69 let note = match key.as_str() { 70 // lower octave 71 "z" => Some(Note::new(00 + 12 * (octave - 1))), // C-1 72 "s" => Some(Note::new(01 + 12 * (octave - 1))), 73 "x" => Some(Note::new(02 + 12 * (octave - 1))), 74 "d" => Some(Note::new(03 + 12 * (octave - 1))), 75 "c" => Some(Note::new(04 + 12 * (octave - 1))), 76 "v" => Some(Note::new(05 + 12 * (octave - 1))), 77 "g" => Some(Note::new(06 + 12 * (octave - 1))), 78 "b" => Some(Note::new(07 + 12 * (octave - 1))), 79 "h" => Some(Note::new(08 + 12 * (octave - 1))), 80 "n" => Some(Note::new(09 + 12 * (octave - 1))), 81 "j" => Some(Note::new(10 + 12 * (octave - 1))), 82 "m" => Some(Note::new(11 + 12 * (octave - 1))), 83 // base octave 84 "q" => Some(Note::new(00 + 12 * octave)), // C 85 "2" => Some(Note::new(01 + 12 * octave)), // Db / C# 86 "w" => Some(Note::new(02 + 12 * octave)), // D 87 "3" => Some(Note::new(03 + 12 * octave)), // Eb / D# 88 "e" => Some(Note::new(04 + 12 * octave)), // E 89 "r" => Some(Note::new(05 + 12 * octave)), // F 90 "5" => Some(Note::new(06 + 12 * octave)), // Gb / F# 91 "t" => Some(Note::new(07 + 12 * octave)), // G 92 "6" => Some(Note::new(08 + 12 * octave)), // Ab / G# 93 "y" => Some(Note::new(09 + 12 * octave)), // A 94 "7" => Some(Note::new(10 + 12 * octave)), // Bb / A# 95 "u" => Some(Note::new(11 + 12 * octave)), // B 96 // higher octave 97 "i" => Some(Note::new(0 + 12 * (octave + 1))), // C+1 98 "9" => Some(Note::new(1 + 12 * (octave + 1))), 99 "o" => Some(Note::new(2 + 12 * (octave + 1))), 100 "0" => Some(Note::new(3 + 12 * (octave + 1))), 101 "p" => Some(Note::new(4 + 12 * (octave + 1))), 102 _ => None, 103 }; 104 note.and_then(Result::ok) 105} 106 107#[derive(Debug, Clone)] 108pub enum PatternPageEvent { 109 Loaded(Pattern, u8), 110 SetSampleInstr(u8), 111 /// pattern, row 112 PlaybackPosition(Option<(u8, u16)>), 113} 114 115#[derive(Debug)] 116pub struct PatternPage { 117 pattern_index: u8, 118 pattern: Pattern, 119 cursor_position: (InPatternPosition, InEventPosition), 120 draw_position: InPatternPosition, 121 event_proxy: EventLoopProxy<GlobalEvent>, 122 /// Last used or last selected in the sample menu 123 selected_sample_instr: u8, 124 /// pattern, row 125 // storest the pattern index, because if i switch page i want to show the current position before i 126 // get the next event 127 playback: Option<(u8, u16)>, 128} 129 130impl PatternPage { 131 const MAX_PATTERN: usize = 199; 132 const DRAWN_ROWS: u16 = 32; 133 const DRAWN_CHANNELS: u8 = 5; 134 const MAX_CHANNELS: u8 = 64; 135 /// how many rows the cursor is moved when pressing pageup/down 136 // TODO: make configurable 137 const PAGE_AS_ROWS: u16 = 16; 138 const CHANNEL_WIDTH: u8 = 14; 139 140 // TODO: make configurable 141 const ROW_HIGHTLIGHT_MINOR: u16 = 4; 142 const ROW_HIGHTLIGHT_MAJOR: u16 = 16; 143 144 pub fn process_event( 145 &mut self, 146 event: PatternPageEvent, 147 events: &mut EventQueue<'_>, 148 ) -> PageResponse { 149 match event { 150 PatternPageEvent::Loaded(pattern, idx) => { 151 self.pattern = pattern; 152 self.pattern_index = idx; 153 events.push(GlobalEvent::Header(HeaderEvent::SetPattern(idx))); 154 events.push(GlobalEvent::Header(HeaderEvent::SetMaxCursorRow( 155 self.pattern.row_count(), 156 ))); 157 PageResponse::RequestRedraw 158 } 159 PatternPageEvent::SetSampleInstr(i) => { 160 self.selected_sample_instr = i; 161 PageResponse::None 162 } 163 PatternPageEvent::PlaybackPosition(p) => { 164 self.playback = p; 165 // TODO: only return this if the change is actually visible 166 PageResponse::RequestRedraw 167 } 168 } 169 } 170 171 pub fn new(proxy: EventLoopProxy<GlobalEvent>) -> Self { 172 Self { 173 pattern_index: 0, 174 pattern: Pattern::default(), 175 cursor_position: ( 176 InPatternPosition { row: 0, channel: 0 }, 177 InEventPosition::Note, 178 ), 179 draw_position: InPatternPosition { row: 0, channel: 0 }, 180 event_proxy: proxy, 181 selected_sample_instr: 0, 182 playback: None, 183 } 184 } 185 186 /// returns true if the position was changed 187 fn set_cursor(&mut self, mut pos: InPatternPosition, events: &mut EventQueue<'_>) -> bool { 188 if pos.row >= self.pattern.row_count() { 189 pos.row = self.pattern.row_count() - 1; 190 } 191 if pos.channel >= Self::MAX_CHANNELS { 192 pos.channel = Self::MAX_CHANNELS - 1; 193 } 194 195 if pos == self.cursor_position.0 { 196 return false; 197 } 198 199 if pos.row != self.cursor_position.0.row { 200 events.push(GlobalEvent::Header(HeaderEvent::SetCursorRow(pos.row))); 201 } 202 203 self.cursor_position.0 = pos; 204 205 // update draw position 206 if pos.channel >= self.draw_position.channel + Self::DRAWN_CHANNELS { 207 self.draw_position.channel = pos.channel - Self::DRAWN_CHANNELS + 1; 208 } else if pos.channel < self.draw_position.channel { 209 self.draw_position.channel = pos.channel 210 } 211 212 if pos.row <= (Self::DRAWN_ROWS / 2) { 213 self.draw_position.row = 0; 214 } else if pos.row >= self.pattern.row_count() - (Self::DRAWN_ROWS / 2) { 215 self.draw_position.row = self.pattern.row_count() - Self::DRAWN_ROWS 216 } else { 217 self.draw_position.row = pos.row - (Self::DRAWN_ROWS / 2); 218 } 219 220 true 221 } 222 223 /// returns true if the cursor was changed 224 fn cursor_next_row(&mut self, events: &mut EventQueue<'_>) -> bool { 225 let mut pos = self.cursor_position.0; 226 pos.row = pos.row.saturating_add(1); 227 self.set_cursor(pos, events) 228 } 229 230 fn load_pattern(&mut self, idx: u8) { 231 let proxy = self.event_proxy.clone(); 232 EXECUTOR 233 .spawn(async move { 234 let lock = SONG_MANAGER.lock().await; 235 let pattern = lock.get_song().patterns[usize::from(idx)].clone(); 236 drop(lock); 237 proxy 238 .send_event(GlobalEvent::Page(super::PageEvent::Pattern( 239 PatternPageEvent::Loaded(pattern, idx), 240 ))) 241 .unwrap(); 242 }) 243 .detach(); 244 } 245 246 fn set_event(&mut self, position: InPatternPosition, event: NoteEvent) { 247 self.pattern.set_event(position, event); 248 let op = SongOperation::PatternOperation( 249 self.pattern_index, 250 PatternOperation::SetEvent { position, event }, 251 ); 252 send_song_op(op); 253 } 254 255 fn remove_event(&mut self, position: InPatternPosition) { 256 self.pattern.remove_event(position); 257 let op = SongOperation::PatternOperation( 258 self.pattern_index, 259 PatternOperation::RemoveEvent { position }, 260 ); 261 send_song_op(op); 262 } 263 264 pub fn set_sample(&mut self, sample: u8, events: &mut EventQueue<'_>) { 265 self.selected_sample_instr = sample; 266 events.push(GlobalEvent::Page(super::PageEvent::SampleList( 267 super::sample_list::SampleListEvent::SelectSample(sample), 268 ))); 269 } 270} 271 272impl Page for PatternPage { 273 fn draw(&mut self, draw_buffer: &mut super::DrawBuffer) { 274 // helper fns 275 fn visible_channels(page: &PatternPage) -> impl Iterator<Item = (usize, u8)> { 276 (page.draw_position.channel..page.draw_position.channel + PatternPage::DRAWN_CHANNELS) 277 .enumerate() 278 } 279 280 fn visible_rows(page: &PatternPage) -> impl Iterator<Item = (usize, u16)> { 281 (page.draw_position.row..page.draw_position.row + PatternPage::DRAWN_ROWS).enumerate() 282 } 283 284 // draw row numbers 285 assert!(self.draw_position.row + Self::DRAWN_ROWS <= 999); 286 let mut buf = [0; 3]; 287 for (index, value) in visible_rows(self) { 288 const BASE_POS: CharPosition = CharPosition::new(1, 15); 289 let mut curse: std::io::Cursor<&mut [u8]> = std::io::Cursor::new(&mut buf); 290 write!(&mut curse, "{:03}", value).unwrap(); 291 let text_color = if self.playback == Some((self.pattern_index, value)) { 292 3 293 } else { 294 0 295 }; 296 draw_buffer.draw_string( 297 from_utf8(&buf).unwrap(), 298 BASE_POS + CharPosition::new(0, u8::try_from(index).unwrap()), 299 text_color, 300 2, 301 ); 302 } 303 304 // draw channel headings 305 assert!(self.draw_position.channel + Self::DRAWN_CHANNELS <= 99); 306 let mut buf: [u8; 2] = [0; 2]; 307 for (index, value) in visible_channels(self) { 308 const BASE_POS: CharPosition = CharPosition::new(14, 14); 309 310 let mut curse: std::io::Cursor<&mut [u8]> = std::io::Cursor::new(&mut buf); 311 write!(&mut curse, "{:02}", value).unwrap(); 312 313 draw_buffer.draw_string( 314 from_utf8(&buf).unwrap(), 315 BASE_POS + (u8::try_from(index).unwrap() * Self::CHANNEL_WIDTH, 0), 316 3, 317 1, 318 ); 319 } 320 321 // draw events 322 const BLOCK_CODE: [u8; 8] = [0b0, 0b0, 0b0, 0b11000, 0b11000, 0b0, 0b0, 0b0]; 323 struct EventView { 324 note1: [u8; 8], 325 note2: [u8; 8], 326 octave: [u8; 8], 327 sample1: [u8; 8], 328 sample2: [u8; 8], 329 vol_pan1: [u8; 8], 330 vol_pan2: [u8; 8], 331 effect1: [u8; 8], 332 effect2: [u8; 8], 333 effect3: [u8; 8], 334 } 335 336 impl Default for EventView { 337 fn default() -> Self { 338 Self { 339 note1: BLOCK_CODE, 340 note2: BLOCK_CODE, 341 octave: BLOCK_CODE, 342 sample1: BLOCK_CODE, 343 sample2: BLOCK_CODE, 344 vol_pan1: BLOCK_CODE, 345 vol_pan2: BLOCK_CODE, 346 effect1: font8x8::UnicodeFonts::get(&font8x8::BASIC_FONTS, '.').unwrap(), 347 effect2: font8x8::UnicodeFonts::get(&font8x8::BASIC_FONTS, '0').unwrap(), 348 effect3: font8x8::UnicodeFonts::get(&font8x8::BASIC_FONTS, '0').unwrap(), 349 } 350 } 351 } 352 353 impl From<NoteEvent> for EventView { 354 fn from(value: NoteEvent) -> Self { 355 fn get_bitmap_digit(value: u8) -> [u8; 8] { 356 let char = match value { 357 0 => '0', 358 1 => '1', 359 2 => '2', 360 3 => '3', 361 4 => '4', 362 5 => '5', 363 6 => '6', 364 7 => '7', 365 8 => '8', 366 9 => '9', 367 _ => panic!("not a digit. number too large"), 368 }; 369 font8x8::UnicodeFonts::get(&font8x8::BASIC_FONTS, char).unwrap() 370 } 371 372 let mut view = Self::default(); 373 // note 374 let mut note_chars = value.note.get_note_name().chars(); 375 let first = note_chars.next().unwrap(); 376 let second = note_chars.next().unwrap_or('-'); 377 view.note1 = font8x8::UnicodeFonts::get(&font8x8::BASIC_FONTS, first).unwrap(); 378 view.note2 = font8x8::UnicodeFonts::get(&font8x8::BASIC_FONTS, second).unwrap(); 379 380 view.octave = get_bitmap_digit(value.note.get_octave()); 381 // sample_instr number 382 view.sample1 = get_bitmap_digit(value.sample_instr / 10); 383 view.sample2 = get_bitmap_digit(value.sample_instr % 10); 384 // TODO: rest noch 385 view 386 } 387 } 388 389 const EVENT_BASE_POS: CharPosition = CharPosition::new(5, 15); 390 const BACKGROUND: u8 = 0; 391 const FOREGROUND: u8 = 6; 392 for (c_idx, c_val) in visible_channels(self) { 393 for (r_idx, r_val) in visible_rows(self) { 394 let background_color = match r_val { 395 val if val == self.cursor_position.0.row => 1, 396 val if val % Self::ROW_HIGHTLIGHT_MAJOR == 0 => 14, 397 val if val % Self::ROW_HIGHTLIGHT_MINOR == 0 => 15, 398 _ => BACKGROUND, 399 }; 400 let view: EventView = self 401 .pattern 402 .get_event(InPatternPosition { 403 row: r_val, 404 channel: c_val, 405 }) 406 .map(|e| (*e).into()) 407 .unwrap_or_default(); 408 let pos = EVENT_BASE_POS 409 + ( 410 u8::try_from(c_idx).unwrap() * Self::CHANNEL_WIDTH, 411 u8::try_from(r_idx).unwrap(), 412 ); 413 draw_buffer.draw_char(view.note1, pos, 6, background_color); 414 draw_buffer.draw_char(view.note2, pos + (1, 0), FOREGROUND, background_color); 415 draw_buffer.draw_char(view.octave, pos + (2, 0), FOREGROUND, background_color); 416 draw_buffer.draw_rect(background_color, (pos + (3, 0)).into()); 417 draw_buffer.draw_char(view.sample1, pos + (4, 0), FOREGROUND, background_color); 418 draw_buffer.draw_char(view.sample2, pos + (5, 0), FOREGROUND, background_color); 419 draw_buffer.draw_rect(background_color, (pos + (6, 0)).into()); 420 draw_buffer.draw_char(view.vol_pan1, pos + (7, 0), FOREGROUND, background_color); 421 draw_buffer.draw_char(view.vol_pan2, pos + (8, 0), FOREGROUND, background_color); 422 draw_buffer.draw_rect(background_color, (pos + (9, 0)).into()); 423 draw_buffer.draw_char(view.effect1, pos + (10, 0), FOREGROUND, background_color); 424 draw_buffer.draw_char(view.effect2, pos + (11, 0), FOREGROUND, background_color); 425 draw_buffer.draw_char(view.effect3, pos + (12, 0), FOREGROUND, background_color); 426 } 427 } 428 429 // draw cursor 430 let view: EventView = self 431 .pattern 432 .get_event(self.cursor_position.0) 433 .map(|e| (*e).into()) 434 .unwrap_or_default(); 435 assert!(self.cursor_position.0.channel >= self.draw_position.channel); 436 assert!(self.cursor_position.0.row >= self.draw_position.row); 437 let c_idx = self.cursor_position.0.channel - self.draw_position.channel; 438 let r_idx = u8::try_from(self.cursor_position.0.row - self.draw_position.row).unwrap(); 439 let pos = EVENT_BASE_POS + (c_idx * Self::CHANNEL_WIDTH, r_idx); 440 match self.cursor_position.1 { 441 InEventPosition::Note => draw_buffer.draw_char(view.note1, pos, 0, 3), 442 InEventPosition::Octave => draw_buffer.draw_char(view.octave, pos + (2, 0), 0, 3), 443 InEventPosition::Sample1 => draw_buffer.draw_char(view.sample1, pos + (4, 0), 0, 3), 444 InEventPosition::Sample2 => draw_buffer.draw_char(view.sample2, pos + (5, 0), 0, 3), 445 InEventPosition::VolPan1 => draw_buffer.draw_char(view.vol_pan1, pos + (7, 0), 0, 3), 446 InEventPosition::VolPan2 => draw_buffer.draw_char(view.vol_pan2, pos + (8, 0), 0, 3), 447 InEventPosition::Effect1 => draw_buffer.draw_char(view.effect1, pos + (10, 0), 0, 3), 448 InEventPosition::Effect2 => draw_buffer.draw_char(view.effect2, pos + (11, 0), 0, 3), 449 InEventPosition::Effect3 => draw_buffer.draw_char(view.effect3, pos + (12, 0), 0, 3), 450 } 451 } 452 453 fn draw_constant(&mut self, draw_buffer: &mut super::DrawBuffer) { 454 draw_buffer.draw_rect(2, CharRect::PAGE_AREA); 455 456 // draw channel headers const parts 457 for index in 0..Self::DRAWN_CHANNELS { 458 const BASE_POS: CharPosition = CharPosition::new(5, 14); 459 let pos = BASE_POS + (index * Self::CHANNEL_WIDTH, 0); 460 draw_buffer.draw_rect(1, (pos + (11, 0)).into()); 461 462 draw_buffer.draw_string(" Channel ", pos, 3, 1); 463 } 464 } 465 466 fn process_key_event( 467 &mut self, 468 modifiers: &winit::event::Modifiers, 469 key_event: &winit::event::KeyEvent, 470 events: &mut EventQueue<'_>, 471 ) -> PageResponse { 472 if !key_event.state.is_pressed() { 473 return PageResponse::None; 474 } 475 476 if key_event.logical_key == Key::Character(SmolStr::new_static("+")) { 477 if usize::from(self.pattern_index) != Self::MAX_PATTERN { 478 self.load_pattern(self.pattern_index + 1); 479 return PageResponse::RequestRedraw; 480 } 481 } else if key_event.logical_key == Key::Character(SmolStr::new_static("-")) { 482 if self.pattern_index != 0 { 483 self.load_pattern(self.pattern_index - 1); 484 return PageResponse::RequestRedraw; 485 } 486 } else if key_event.logical_key == Key::Named(NamedKey::ArrowDown) { 487 if self.cursor_next_row(events) { 488 return PageResponse::RequestRedraw; 489 } 490 } else if key_event.logical_key == Key::Named(NamedKey::ArrowUp) { 491 let mut pos = self.cursor_position.0; 492 pos.row = pos.row.saturating_sub(1); 493 if self.set_cursor(pos, events) { 494 return PageResponse::RequestRedraw; 495 } 496 } else if key_event.logical_key == Key::Named(NamedKey::ArrowRight) { 497 match self.cursor_position.1.to_right() { 498 Some(p) => { 499 self.cursor_position.1 = p; 500 return PageResponse::RequestRedraw; 501 } 502 None => { 503 let mut pos = self.cursor_position.0; 504 pos.channel = pos.channel.saturating_add(1); 505 if self.set_cursor(pos, events) { 506 self.cursor_position.1 = InEventPosition::Note; 507 return PageResponse::RequestRedraw; 508 } 509 } 510 } 511 } else if key_event.logical_key == Key::Named(NamedKey::ArrowLeft) { 512 match self.cursor_position.1.to_left() { 513 Some(p) => { 514 self.cursor_position.1 = p; 515 return PageResponse::RequestRedraw; 516 } 517 None => { 518 let mut pos = self.cursor_position.0; 519 pos.channel = pos.channel.saturating_sub(1); 520 if self.set_cursor(pos, events) { 521 self.cursor_position.1 = InEventPosition::Effect3; 522 return PageResponse::RequestRedraw; 523 } 524 } 525 } 526 } else if key_event.logical_key == Key::Named(NamedKey::Tab) { 527 // shift => move left 528 // not shift => move right 529 if modifiers.state().shift_key() { 530 if self.cursor_position.1 == InEventPosition::Note { 531 let mut pos = self.cursor_position.0; 532 pos.channel = pos.channel.saturating_sub(1); 533 if self.set_cursor(pos, events) { 534 return PageResponse::RequestRedraw; 535 } 536 } else { 537 self.cursor_position.1 = InEventPosition::Note; 538 return PageResponse::RequestRedraw; 539 } 540 } else { 541 let mut pos = self.cursor_position.0; 542 pos.channel = pos.channel.saturating_add(1); 543 self.set_cursor(pos, events); 544 self.cursor_position.1 = InEventPosition::Note; 545 return PageResponse::RequestRedraw; 546 } 547 } else if key_event.logical_key == Key::Named(NamedKey::PageDown) { 548 let mut pos = self.cursor_position.0; 549 pos.row = pos.row.saturating_add(Self::PAGE_AS_ROWS); 550 if self.set_cursor(pos, events) { 551 return PageResponse::RequestRedraw; 552 } 553 } else if key_event.logical_key == Key::Named(NamedKey::PageUp) { 554 // original has special behaviour on page up: 555 // when on last row it only goes up 15 rows 556 let mut pos = self.cursor_position.0; 557 pos.row = pos.row.saturating_sub(Self::PAGE_AS_ROWS); 558 if self.set_cursor(pos, events) { 559 return PageResponse::RequestRedraw; 560 } 561 } else if Key::Character(SmolStr::new_static(".")) == key_event.logical_key { 562 self.remove_event(self.cursor_position.0); 563 self.cursor_next_row(events); 564 return PageResponse::RequestRedraw; 565 } else if let Key::Character(char) = &key_event.logical_key { 566 // should be copied from the header, where this can already be set 567 const DEFAULT_OCTAVE: u8 = 5; 568 match self.cursor_position.1 { 569 InEventPosition::Note => { 570 // TODO: make octave configurable 571 if let Some(note) = get_note_from_key(char, DEFAULT_OCTAVE) { 572 let old_event = self.pattern.get_event(self.cursor_position.0); 573 let event = match old_event { 574 None => NoteEvent { 575 note, 576 sample_instr: self.selected_sample_instr, 577 vol: VolumeEffect::None, 578 command: NoteCommand::None, 579 }, 580 Some(&event) => NoteEvent { note, ..event }, 581 }; 582 self.set_event(self.cursor_position.0, event); 583 } 584 // move to next row even if 585 self.cursor_next_row(events); 586 // always redraw is incorrect. I only need to redraw if either the cursor moved, or the event changed 587 return PageResponse::RequestRedraw; 588 } 589 InEventPosition::Octave => { 590 if let Some(event) = self.pattern.get_event(self.cursor_position.0) { 591 let mut new_event = *event; 592 // set octave fn needed 593 let octave: Result<u8, _> = char.as_str().parse(); 594 if let Ok(octave) = octave { 595 new_event.note = 596 Note::new(event.note.get() % 12 + 12 * octave).unwrap(); 597 self.set_event(self.cursor_position.0, new_event); 598 } 599 } 600 self.cursor_next_row(events); 601 // always redraw is incorrect. 602 return PageResponse::RequestRedraw; 603 } 604 InEventPosition::Sample1 => { 605 let num: Result<u8, _> = char.as_str().parse(); 606 if let Ok(num) = num 607 && let Some(event) = self.pattern.get_event(self.cursor_position.0).copied() 608 { 609 let zeros = event.sample_instr % 10; 610 let sample_instr = zeros + num * 10; 611 if usize::from(sample_instr) < Song::MAX_SAMPLES_INSTR { 612 self.set_event( 613 self.cursor_position.0, 614 NoteEvent { 615 sample_instr, 616 ..event 617 }, 618 ); 619 self.set_sample(sample_instr, events); 620 } 621 } 622 // move cursor one step right 623 self.cursor_position.1 = InEventPosition::Sample2; 624 return PageResponse::RequestRedraw; 625 } 626 InEventPosition::Sample2 => { 627 let num: Result<u8, _> = char.as_str().parse(); 628 if let Ok(num) = num 629 && let Some(event) = self.pattern.get_event(self.cursor_position.0).copied() 630 { 631 let tens = event.sample_instr / 10; 632 let sample_instr = tens * 10 + num; 633 if usize::from(sample_instr) < Song::MAX_SAMPLES_INSTR { 634 self.set_event( 635 self.cursor_position.0, 636 NoteEvent { 637 sample_instr, 638 ..event 639 }, 640 ); 641 self.set_sample(sample_instr, events); 642 } 643 } 644 self.cursor_next_row(events); 645 return PageResponse::RequestRedraw; 646 } 647 InEventPosition::VolPan1 => eprintln!("not yet implemented"), 648 InEventPosition::VolPan2 => eprintln!("not yet implemented"), 649 InEventPosition::Effect1 => eprintln!("not yet implemented"), 650 InEventPosition::Effect2 => eprintln!("not yet implemented"), 651 InEventPosition::Effect3 => eprintln!("not yet implemented"), 652 } 653 } 654 655 PageResponse::None 656 } 657 658 #[cfg(feature = "accesskit")] 659 fn build_tree( 660 &self, 661 tree: &mut Vec<(accesskit::NodeId, accesskit::Node)>, 662 ) -> crate::AccessResponse { 663 todo!() 664 } 665}