use crate::*; use tek_core::Direction; /// Phrase editor. pub struct Sequencer { pub name: Arc>, pub mode: bool, pub focused: bool, pub entered: bool, pub phrase: Option>>, pub transport: Option>>>, pub buffer: BigBuffer, pub keys: Buffer, /// Highlight input keys pub keys_in: [bool; 128], /// Highlight output keys pub keys_out: [bool; 128], pub now: usize, pub ppq: usize, pub note_axis: FixedAxis, pub time_axis: ScaledAxis, /// Play input through output. pub monitoring: bool, /// Write input to sequence. pub recording: bool, /// Overdub input to sequence. pub overdub: bool, /// Map: tick -> MIDI events at tick pub phrases: Vec>>, /// Phrase selector pub sequence: Option, /// Output from current sequence. pub midi_out: Option>, /// MIDI output buffer midi_out_buf: Vec>>, /// Send all notes off pub reset: bool, // TODO?: after Some(nframes) /// Highlight keys on piano roll. pub notes_in: [bool;128], /// Highlight keys on piano roll. pub notes_out: [bool;128], } impl Sequencer { pub fn new (name: &str) -> Self { Self { name: Arc::new(RwLock::new(name.into())), monitoring: false, recording: false, overdub: true, phrases: vec![], sequence: None, midi_out: None, midi_out_buf: vec![Vec::with_capacity(16);16384], reset: true, notes_in: [false;128], notes_out: [false;128], buffer: Default::default(), keys: keys_vert(), entered: false, focused: false, mode: false, keys_in: [false;128], keys_out: [false;128], phrase: None, now: 0, ppq: 96, transport: None, note_axis: FixedAxis { start: 12, point: Some(36) }, time_axis: ScaledAxis { start: 0, scale: 24, point: Some(0) }, } } pub fn toggle_monitor (&mut self) { self.monitoring = !self.monitoring; } pub fn toggle_record (&mut self) { self.recording = !self.recording; } pub fn toggle_overdub (&mut self) { self.overdub = !self.overdub; } pub fn process ( &mut self, input: Option, timebase: &Arc, playing: Option, started: Option<(usize, usize)>, quant: usize, reset: bool, scope: &ProcessScope, (frame0, frames): (usize, usize), (_usec0, _usecs): (usize, usize), period: f64, ) { if self.midi_out.is_some() { // Clear the section of the output buffer that we will be using for frame in &mut self.midi_out_buf[0..frames] { frame.clear(); } // Emit "all notes off" at start of buffer if requested if self.reset { all_notes_off(&mut self.midi_out_buf); self.reset = false; } else if reset { all_notes_off(&mut self.midi_out_buf); } } if let ( Some(TransportState::Rolling), Some((start_frame, _)), Some(phrase) ) = ( playing, started, self.sequence.and_then(|id|self.phrases.get_mut(id)) ) { phrase.read().map(|phrase|{ if self.midi_out.is_some() { phrase.process_out( &mut self.midi_out_buf, &mut self.notes_out, timebase, (frame0.saturating_sub(start_frame), frames, period) ); } }).unwrap(); let mut phrase = phrase.write().unwrap(); let length = phrase.length; // Monitor and record input if input.is_some() && (self.recording || self.monitoring) { // For highlighting keys and note repeat for (frame, event, bytes) in parse_midi_input(input.unwrap()) { match event { LiveEvent::Midi { message, .. } => { if self.monitoring { self.midi_out_buf[frame].push(bytes.to_vec()) } if self.recording { phrase.record_event({ let pulse = timebase.frame_to_pulse( (frame0 + frame - start_frame) as f64 ); let quantized = ( pulse / quant as f64 ).round() as usize * quant; let looped = quantized % length; looped }, message); } match message { MidiMessage::NoteOn { key, .. } => { self.notes_in[key.as_int() as usize] = true; } MidiMessage::NoteOff { key, .. } => { self.notes_in[key.as_int() as usize] = false; }, _ => {} } }, _ => {} } } } } else if input.is_some() && self.midi_out.is_some() && self.monitoring { for (frame, event, bytes) in parse_midi_input(input.unwrap()) { self.process_monitor_event(frame, &event, bytes) } } if let Some(out) = &mut self.midi_out { write_midi_output(&mut out.writer(scope), &self.midi_out_buf, frames); } } #[inline] fn process_monitor_event (&mut self, frame: usize, event: &LiveEvent, bytes: &[u8]) { match event { LiveEvent::Midi { message, .. } => { self.write_to_output_buffer(frame, bytes); self.process_monitor_message(&message); }, _ => {} } } #[inline] fn write_to_output_buffer (&mut self, frame: usize, bytes: &[u8]) { self.midi_out_buf[frame].push(bytes.to_vec()); } #[inline] fn process_monitor_message (&mut self, message: &MidiMessage) { match message { MidiMessage::NoteOn { key, .. } => { self.notes_in[key.as_int() as usize] = true; } MidiMessage::NoteOff { key, .. } => { self.notes_in[key.as_int() as usize] = false; }, _ => {} } } } impl Widget for Sequencer { type Engine = Tui; fn layout (&self, area: [u16;4]) -> Perhaps<[u16;4]> { todo!() } fn render (&self, to: &mut Tui) -> Perhaps<[u16;4]> { self.horizontal_draw(to)?; if self.focused && self.entered { Corners(Style::default().green().not_dim()).draw(to)?; } Ok(Some(to.area())) } } impl Sequencer { /// Select which pattern to display. This pre-renders it to the buffer at full resolution. pub fn show (&mut self, phrase: Option<&Arc>>) -> Usually<()> { self.phrase = phrase.map(Clone::clone); if let Some(ref phrase) = self.phrase { let width = usize::MAX.min(phrase.read().unwrap().length); let mut buffer = BigBuffer::new(width, 64); let phrase = phrase.read().unwrap(); fill_seq_bg(&mut buffer, phrase.length, self.ppq)?; fill_seq_fg(&mut buffer, &phrase)?; self.buffer = buffer; } else { self.buffer = Default::default(); } Ok(()) } pub(crate) fn style_focus (&self) -> Option