use crate::*; #[derive(Debug)] pub struct MidiOutput { /// Handle to JACK client, for receiving reconnect events. jack: Jack<'static>, /// Port name name: Arc, /// Port handle. port: Port, /// List of ports to connect to. pub connections: Vec, /// List of currently held notes. held: Arc>, /// Buffer note_buffer: Vec, /// Buffer output_buffer: Vec>>, } impl HasJack<'static> for MidiOutput { fn jack (&self) -> &Jack<'static> { &self.jack } } impl JackPort for MidiOutput { type Port = MidiOut; type Pair = MidiIn; fn port_name (&self) -> &Arc { &self.name } fn port (&self) -> &Port { &self.port } fn port_mut (&mut self) -> &mut Port { &mut self.port } fn into_port (self) -> Port { self.port } fn connections (&self) -> &[Connect] { self.connections.as_slice() } fn new (jack: &Jack<'static>, name: &impl AsRef, connect: &[Connect]) -> Usually where Self: Sized { let port = Self::register(jack, name)?; let jack = jack.clone(); let name = name.as_ref().into(); let connections = connect.to_vec(); let port = Self { jack, port, name, connections, held: Arc::new([false;128].into()), note_buffer: vec![0;8], output_buffer: vec![vec![];65536], }; port.connect_to_matching()?; Ok(port) } } impl MidiOutput { /// Clear the section of the output buffer that we will be using, /// emitting "all notes off" at start of buffer if requested. pub fn buffer_clear (&mut self, scope: &ProcessScope, reset: bool) { let n_frames = (scope.n_frames() as usize).min(self.output_buffer.len()); for frame in &mut self.output_buffer[0..n_frames] { frame.clear(); } if reset { all_notes_off(&mut self.output_buffer); } } /// Write a note to the output buffer pub fn buffer_write <'a> ( &'a mut self, sample: usize, event: LiveEvent, ) { self.note_buffer.fill(0); event.write(&mut self.note_buffer).expect("failed to serialize MIDI event"); self.output_buffer[sample].push(self.note_buffer.clone()); // Update the list of currently held notes. if let LiveEvent::Midi { ref message, .. } = event { update_keys(&mut*self.held.write().unwrap(), message); } } /// Write a chunk of MIDI data from the output buffer to the output port. pub fn buffer_emit (&mut self, scope: &ProcessScope) { let samples = scope.n_frames() as usize; let mut writer = self.port.writer(scope); for (time, events) in self.output_buffer.iter().enumerate().take(samples) { for bytes in events.iter() { writer.write(&RawMidi { time: time as u32, bytes }).unwrap_or_else(|_|{ panic!("Failed to write MIDI data: {bytes:?}"); }); } } } } #[tengri_proc::command(MidiOutput)] impl MidiOutputCommand { fn _todo_ (_port: &mut MidiOutput) -> Perhaps { Ok(None) } } impl>> HasMidiOuts for T { fn midi_outs (&self) -> &Vec { self.get() } fn midi_outs_mut (&mut self) -> &mut Vec { self.get_mut() } } /// Trait for thing that may output MIDI. pub trait HasMidiOuts { fn midi_outs (&self) -> &Vec; fn midi_outs_mut (&mut self) -> &mut Vec; fn midi_outs_with_sizes <'a> (&'a self) -> impl Iterator, &[Connect], usize, usize)> + Send + Sync + 'a { let mut y = 0; self.midi_outs().iter().enumerate().map(move|(i, output)|{ let height = 1 + output.connections().len(); let data = (i, output.port_name(), output.connections(), y, y + height); y += height; data }) } fn midi_outs_emit (&mut self, scope: &ProcessScope) { for port in self.midi_outs_mut().iter_mut() { port.buffer_emit(scope) } } } /// Trail for thing that may gain new MIDI ports. impl> AddMidiOut for T { fn midi_out_add (&mut self) -> Usually<()> { let index = self.midi_outs().len(); let port = MidiOutput::new(self.jack(), &format!("{index}/M"), &[])?; self.midi_outs_mut().push(port); Ok(()) } } /// May create new MIDI output ports. pub trait AddMidiOut { fn midi_out_add (&mut self) -> Usually<()>; }