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, /// Red keys on piano roll. pub notes_on: Vec, /// Device chain pub devices: Vec, /// Device selector pub device: usize, } 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)?, notes_on: vec![], 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, }) } pub fn process ( &mut self, input: MidiIter, timebase: &Arc, playing: Option, scope: &ProcessScope, frame0: usize, frames: usize, panic: bool, ) { // Need to be borrowed outside the conditionals? let recording = self.recording; let monitoring = self.monitoring; // Output buffer let mut output: Vec>>> = vec![None;frames]; // For highlighting keys let mut notes_on = vec![false;128]; if panic { all_notes_off(&mut output); } // Play from phrase into output buffer if let Some(phrase) = self.phrase() { if playing == Some(TransportState::Rolling) { phrase.process_out( &mut output, &mut notes_on, timebase, frame0, frames ); } } // Monitor and record input if self.recording || self.monitoring { let phrase = &mut self.phrase_mut(); for (time, event, bytes) in parse_midi_input(input) { match event { LiveEvent::Midi { message, .. } => { if monitoring { if let Some(Some(frame)) = output.get_mut(time) { frame.push(bytes.into()) } else { output[time] = Some(vec![bytes.into()]); } } if recording { if let Some(phrase) = phrase { let pulse = timebase.frames_pulses((frame0 + time) as f64) as usize; let pulse = pulse % phrase.length; 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; }, _ => {} } }, _ => {} } } } self.notes_on = notes_on; write_midi_output(&mut self.midi_out.writer(scope), &output, frames); } 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 get_device (&self, i: usize) -> Option>> { self.devices.get(i).map(|d|d.state.lock().unwrap()) } 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 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; } }