use crate::core::*; use crate::model::*; pub struct Track { pub name: String, /// 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_out_buf: Vec>>, /// Device chain pub devices: Vec, /// Device selector pub device: usize, /// Send all notes off /// FIXME: Some(nframes)? pub reset: bool, /// Highlight keys on piano roll. pub notes_in: [bool;128], /// Highlight keys on piano roll. pub notes_out: [bool;128], } impl Track { pub fn new ( name: &str, phrases: Option>, devices: Option>, ) -> Usually { Ok(Self { name: name.to_string(), midi_out: None, midi_out_buf: vec![vec![];16384], notes_in: [false;128], notes_out: [false;128], monitoring: false, recording: false, overdub: true, sequence: None, phrases: phrases.unwrap_or_else(||Vec::with_capacity(16)), devices: devices.unwrap_or_else(||Vec::with_capacity(16)), device: 0, reset: true, }) } pub fn get_device (&self, i: usize) -> Option>> { self.devices.get(i).map(|d|d.state.read().unwrap()) } pub fn get_device_mut (&self, i: usize) -> Option>> { self.devices.get(i).map(|d|d.state.write().unwrap()) } pub fn device (&self) -> Option>> { self.get_device(self.device) } pub fn device_mut (&self) -> Option>> { self.get_device_mut(self.device) } pub fn first_device (&self) -> Option>> { self.get_device(0) } pub fn connect_first_device (&self) -> Usually<()> { if let (Some(port), Some(device)) = (&self.midi_out, self.devices.get(0)) { device.client.as_client().connect_ports(&port, &device.midi_ins()?[0])?; } Ok(()) } pub fn last_device (&self) -> Option>> { self.get_device(self.devices.len().saturating_sub(1)) } pub fn connect_last_device (&self, app: &App) -> Usually<()> { Ok(match self.devices.get(self.devices.len().saturating_sub(1)) { Some(device) => { app.audio_out(0).map(|left|device.connect_audio_out(0, &left)).transpose()?; app.audio_out(1).map(|right|device.connect_audio_out(0, &right)).transpose()?; () }, None => () }) } pub fn add_device (&mut self, device: JackDevice) -> Usually<&mut JackDevice> { self.devices.push(device); let index = self.devices.len() - 1; Ok(&mut self.devices[index]) } pub fn add_device_with_cb ( &mut self, mut device: JackDevice, init: impl Fn(&Self, &mut JackDevice)->Usually<()> ) -> Usually<&mut JackDevice> { init(&self, &mut device)?; self.devices.push(device); let index = self.devices.len() - 1; Ok(&mut self.devices[index]) } pub fn phrase (&self) -> Option<&Phrase> { if let Some(phrase) = self.sequence { return self.phrases.get(phrase) } else { None } } pub fn phrase_mut (&mut self) -> Option<&mut Phrase> { if let Some(phrase) = self.sequence { return self.phrases.get_mut(phrase) } else { None } } pub fn add_phrase ( &mut self, name: &str, length: usize, data: Option ) -> &mut Phrase { self.phrases.push(Phrase::new(name, length, data)); let index = self.phrases.len() - 1; &mut self.phrases[index] } 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, ) { // 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); } // For highlighting keys and note repeat // Currently playing phrase if let ( Some(TransportState::Rolling), Some((start_frame, _)), Some(phrase) ) = ( playing, started, self.sequence.and_then(|id|self.phrases.get_mut(id)) ) { phrase.process_out( &mut self.midi_out_buf, &mut self.notes_out, timebase, (frame0.saturating_sub(start_frame), frames, period) ); // 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 % phrase.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.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 App { pub fn new_track_name (&self) -> String { format!("Track {}", self.tracks.len() + 1) } pub fn add_track ( &mut self, name: Option<&str>, ) -> Usually<&mut Track> { let name = name.ok_or_else(||self.new_track_name())?; self.tracks.push(Track::new(&name, None, None)?); self.track_cursor = self.tracks.len(); Ok(&mut self.tracks[self.track_cursor - 1]) } pub fn add_track_with_cb ( &mut self, name: Option<&str>, init: impl FnOnce(&Client, &mut Track)->Usually<()>, ) -> Usually<&mut Track> { let name = name.ok_or_else(||self.new_track_name())?; let mut track = Track::new(&name, None, None)?; init(self.client(), &mut track)?; self.tracks.push(track); self.track_cursor = self.tracks.len(); Ok(&mut self.tracks[self.track_cursor - 1]) } pub fn track (&self) -> Option<(usize, &Track)> { match self.track_cursor { 0 => None, _ => { let id = self.track_cursor as usize - 1; self.tracks.get(id).map(|t|(id, t)) } } } pub fn track_mut (&mut self) -> Option<(usize, &mut Track)> { match self.track_cursor { 0 => None, _ => { let id = self.track_cursor as usize - 1; self.tracks.get_mut(id).map(|t|(id, t)) } } } }