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