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: Port, midi_out_buf: Vec>>, /// Red keys on piano roll. pub notes_on: Vec, /// Device chain pub devices: Vec, /// Device selector pub device: usize, /// Send all notes off /// FIXME: Some(nframes)? pub reset: bool } ports!(Track { audio: { outs: |_t|Ok(vec![]), } midi: { ins: |_t|Ok(vec![]), outs: |_t|Ok(vec![]), } }); impl Track { pub fn new ( name: &str, jack: &Client, phrases: Option>, devices: Option>, ) -> Usually { Ok(Self { name: name.to_string(), midi_out: jack.register_port(name, MidiOut)?, midi_out_buf: vec![vec![];1024], notes_on: vec![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 device (&self) -> Option>> { self.get_device(self.device) } pub fn first_device (&self) -> Option>> { self.get_device(0) } pub fn last_device (&self) -> Option>> { self.get_device(self.devices.len().saturating_sub(1)) } pub fn get_device (&self, i: usize) -> Option>> { self.devices.get(i).map(|d|d.state.read().unwrap()) } 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: MidiIter, timebase: &Arc, playing: Option, started: Option<(usize, usize)>, quant: usize, reset: bool, scope: &ProcessScope, (frame0, frames): (usize, usize), (usec0, usecs): (usize, usize), period: f64, ) { // Need to be borrowed outside the conditionals? let recording = self.recording; let monitoring = self.monitoring; // 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); } // Play from phrase into output buffer let phrase = &mut self.sequence.map(|id|self.phrases.get_mut(id)).flatten(); if playing == Some(TransportState::Rolling) { if let Some((start_frame, start_usec)) = started { if let Some(phrase) = phrase { phrase.process_out( &mut self.midi_out_buf, &mut self.notes_on, timebase, (frame0.saturating_sub(start_frame), frames, period) ); } } } // Monitor and record input if self.recording || self.monitoring { // For highlighting keys let notes_on = &mut self.notes_on; for (time, event, bytes) in parse_midi_input(input) { match event { LiveEvent::Midi { message, .. } => { if monitoring { self.midi_out_buf[time].push(bytes.to_vec()) } if recording { if let Some(phrase) = phrase { let pulse = timebase.pulse_to_frame((frame0 + time) as f64) as usize; let pulse = pulse % phrase.length; let pulse = (pulse / quant) * quant; let contains = phrase.notes.contains_key(&pulse); if contains { phrase.notes.get_mut(&pulse).unwrap().push(message.clone()); } else { phrase.notes.insert(pulse, vec![message.clone()]); } }; } match message { MidiMessage::NoteOn { key, .. } => { notes_on[key.as_int() as usize] = true; } MidiMessage::NoteOff { key, .. } => { notes_on[key.as_int() as usize] = false; }, _ => {} } }, _ => {} } } } write_midi_output( &mut self.midi_out.writer(scope), &self.midi_out_buf, frames ); } }