diff --git a/crates/tek/src/api/_todo_api_mixer.rs b/crates/tek/src/api/_todo_api_mixer.rs index cd8df774..8e5f0135 100644 --- a/crates/tek/src/api/_todo_api_mixer.rs +++ b/crates/tek/src/api/_todo_api_mixer.rs @@ -20,8 +20,4 @@ impl From<&Arc>> for MixerAudio { } } -impl Audio for MixerAudio { - fn process (&mut self, _: &Client, _: &ProcessScope) -> Control { - Control::Continue - } -} +audio!(|self: MixerAudio, _, _|Control::Continue); diff --git a/crates/tek/src/api/_todo_api_plugin.rs b/crates/tek/src/api/_todo_api_plugin.rs index 3edbac42..161c59d7 100644 --- a/crates/tek/src/api/_todo_api_plugin.rs +++ b/crates/tek/src/api/_todo_api_plugin.rs @@ -62,53 +62,51 @@ impl From<&Arc>> for PluginAudio { } } -impl Audio for PluginAudio { - fn process (&mut self, _: &Client, scope: &ProcessScope) -> Control { - let state = &mut*self.0.write().unwrap(); - match state.plugin.as_mut() { - Some(PluginKind::LV2(LV2Plugin { - features, - ref mut instance, - ref mut input_buffer, - .. - })) => { - let urid = features.midi_urid(); - input_buffer.clear(); - for port in state.midi_ins.iter() { - let mut atom = ::livi::event::LV2AtomSequence::new( - &features, - scope.n_frames() as usize - ); - for event in port.iter(scope) { - match event.bytes.len() { - 3 => atom.push_midi_event::<3>( - event.time as i64, - urid, - &event.bytes[0..3] - ).unwrap(), - _ => {} - } +audio!(|self: PluginAudio, _, _|{ + let state = &mut*self.0.write().unwrap(); + match state.plugin.as_mut() { + Some(PluginKind::LV2(LV2Plugin { + features, + ref mut instance, + ref mut input_buffer, + .. + })) => { + let urid = features.midi_urid(); + input_buffer.clear(); + for port in state.midi_ins.iter() { + let mut atom = ::livi::event::LV2AtomSequence::new( + &features, + scope.n_frames() as usize + ); + for event in port.iter(scope) { + match event.bytes.len() { + 3 => atom.push_midi_event::<3>( + event.time as i64, + urid, + &event.bytes[0..3] + ).unwrap(), + _ => {} } - input_buffer.push(atom); } - let mut outputs = vec![]; - for _ in state.midi_outs.iter() { - outputs.push(::livi::event::LV2AtomSequence::new( - &features, - scope.n_frames() as usize - )); - } - let ports = ::livi::EmptyPortConnections::new() - .with_atom_sequence_inputs(input_buffer.iter()) - .with_atom_sequence_outputs(outputs.iter_mut()) - .with_audio_inputs(state.audio_ins.iter().map(|o|o.as_slice(scope))) - .with_audio_outputs(state.audio_outs.iter_mut().map(|o|o.as_mut_slice(scope))); - unsafe { - instance.run(scope.n_frames() as usize, ports).unwrap() - }; - }, - _ => {} - } - Control::Continue + input_buffer.push(atom); + } + let mut outputs = vec![]; + for _ in state.midi_outs.iter() { + outputs.push(::livi::event::LV2AtomSequence::new( + &features, + scope.n_frames() as usize + )); + } + let ports = ::livi::EmptyPortConnections::new() + .with_atom_sequence_inputs(input_buffer.iter()) + .with_atom_sequence_outputs(outputs.iter_mut()) + .with_audio_inputs(state.audio_ins.iter().map(|o|o.as_slice(scope))) + .with_audio_outputs(state.audio_outs.iter_mut().map(|o|o.as_mut_slice(scope))); + unsafe { + instance.run(scope.n_frames() as usize, ports).unwrap() + }; + }, + _ => {} } -} + Control::Continue +}); diff --git a/crates/tek/src/api/jack.rs b/crates/tek/src/api/jack.rs index d93c8e66..99b0edcf 100644 --- a/crates/tek/src/api/jack.rs +++ b/crates/tek/src/api/jack.rs @@ -1,5 +1,29 @@ use crate::*; +/// Trait for things that have a JACK process callback. +pub trait Audio: Send + Sync { + fn process (&mut self, _: &Client, _: &ProcessScope) -> Control { + Control::Continue + } + fn callback ( + state: &Arc>, client: &Client, scope: &ProcessScope + ) -> Control where Self: Sized { + if let Ok(mut state) = state.write() { + state.process(client, scope) + } else { + Control::Quit + } + } +} + +#[macro_export] macro_rules! audio { + (|$self:ident:$Struct:ident$(<$($L:lifetime),*$($T:ident$(:$U:path)?),*>)?,$c:ident,$s:ident|$cb:expr) => { + impl $(<$($L),*$($T $(: $U)?),*>)? Audio for $Struct $(<$($L),*$($T),*>)? { + #[inline] fn process (&mut $self, $c: &Client, $s: &ProcessScope) -> Control { $cb } + } + } +} + pub trait HasMidiIns { fn midi_ins (&self) -> &Vec>; fn midi_ins_mut (&mut self) -> &mut Vec>; @@ -54,22 +78,6 @@ impl JackActivate for JackClient { } } -/// Trait for things that have a JACK process callback. -pub trait Audio: Send + Sync { - fn process(&mut self, _: &Client, _: &ProcessScope) -> Control { - Control::Continue - } - fn callback( - state: &Arc>, client: &Client, scope: &ProcessScope - ) -> Control where Self: Sized { - if let Ok(mut state) = state.write() { - state.process(client, scope) - } else { - Control::Quit - } - } -} - /// A UI component that may be associated with a JACK client by the `Jack` factory. pub trait AudioComponent: Component + Audio { /// Perform type erasure for collecting heterogeneous devices. diff --git a/crates/tek/src/api/sampler.rs b/crates/tek/src/api/sampler.rs index 260fbf53..07855dbd 100644 --- a/crates/tek/src/api/sampler.rs +++ b/crates/tek/src/api/sampler.rs @@ -96,15 +96,13 @@ impl From<&Arc>> for SamplerAudio { } } -impl Audio for SamplerAudio { - #[inline] fn process (&mut self, _: &Client, scope: &ProcessScope) -> Control { - self.process_midi_in(scope); - self.clear_output_buffer(); - self.process_audio_out(scope); - self.write_output_buffer(scope); - Control::Continue - } -} +audio!(|self: SamplerAudio, _client, scope|{ + self.process_midi_in(scope); + self.clear_output_buffer(); + self.process_audio_out(scope); + self.write_output_buffer(scope); + Control::Continue +}); impl SamplerAudio { diff --git a/crates/tek/src/tui/app_arranger.rs b/crates/tek/src/tui/app_arranger.rs index 35da9a0b..884eb487 100644 --- a/crates/tek/src/tui/app_arranger.rs +++ b/crates/tek/src/tui/app_arranger.rs @@ -323,53 +323,11 @@ impl TransportControl for ArrangerTui { } } } - -impl Audio for ArrangerTui { - #[inline] fn process (&mut self, client: &Client, scope: &ProcessScope) -> Control { - // Start profiling cycle - let t0 = self.perf.get_t0(); - - // Update transport clock - if ClockAudio(self).process(client, scope) == Control::Quit { - return Control::Quit - } - - // Update MIDI sequencers - if TracksAudio( - &mut self.tracks, - &mut self.note_buf, - &mut self.midi_buf, - Default::default(), - ).process(client, scope) == Control::Quit { - return Control::Quit - } - - // FIXME: one of these per playing track - //self.now.set(0.); - //if let ArrangerSelection::Clip(t, s) = self.selected { - //let phrase = self.scenes().get(s).map(|scene|scene.clips.get(t)); - //if let Some(Some(Some(phrase))) = phrase { - //if let Some(track) = self.tracks().get(t) { - //if let Some((ref started_at, Some(ref playing))) = track.player.play_phrase { - //let phrase = phrase.read().unwrap(); - //if *playing.read().unwrap() == *phrase { - //let pulse = self.current().pulse.get(); - //let start = started_at.pulse.get(); - //let now = (pulse - start) % phrase.length as f64; - //self.now.set(now); - //} - //} - //} - //} - //} - - // End profiling cycle - self.perf.update(t0, scope); - - return Control::Continue - } -} - +has_clock!(|self:ArrangerTui|&self.clock); +has_clock!(|self:ArrangerTrack|self.player.clock()); +has_phrases!(|self:ArrangerTui|self.phrases.phrases); +has_editor!(|self:ArrangerTui|self.editor); +has_player!(|self:ArrangerTrack|self.player); // Layout for standalone arranger app. render!(|self: ArrangerTui|{ let arranger_focused = self.arranger_focused(); @@ -411,12 +369,43 @@ render!(|self: ArrangerTui|{ ]) ]) }); - -has_clock!(|self:ArrangerTui|&self.clock); -has_clock!(|self:ArrangerTrack|self.player.clock()); -has_phrases!(|self:ArrangerTui|self.phrases.phrases); -has_editor!(|self:ArrangerTui|self.editor); -has_player!(|self:ArrangerTrack|self.player); +audio!(|self: ArrangerTui, client, scope|{ + // Start profiling cycle + let t0 = self.perf.get_t0(); + // Update transport clock + if ClockAudio(self).process(client, scope) == Control::Quit { + return Control::Quit + } + // Update MIDI sequencers + let tracks = &mut self.tracks; + let note_buf = &mut self.note_buf; + let midi_buf = &mut self.midi_buf; + if TracksAudio(tracks, note_buf, midi_buf, Default::default()) + .process(client, scope) == Control::Quit { + return Control::Quit + } + // FIXME: one of these per playing track + //self.now.set(0.); + //if let ArrangerSelection::Clip(t, s) = self.selected { + //let phrase = self.scenes().get(s).map(|scene|scene.clips.get(t)); + //if let Some(Some(Some(phrase))) = phrase { + //if let Some(track) = self.tracks().get(t) { + //if let Some((ref started_at, Some(ref playing))) = track.player.play_phrase { + //let phrase = phrase.read().unwrap(); + //if *playing.read().unwrap() == *phrase { + //let pulse = self.current().pulse.get(); + //let start = started_at.pulse.get(); + //let now = (pulse - start) % phrase.length as f64; + //self.now.set(now); + //} + //} + //} + //} + //} + // End profiling cycle + self.perf.update(t0, scope); + return Control::Continue +}); impl HasPhraseList for ArrangerTui { fn phrases_focused (&self) -> bool { diff --git a/crates/tek/src/tui/app_sampler.rs b/crates/tek/src/tui/app_sampler.rs index 2fa6b9a2..795b5f58 100644 --- a/crates/tek/src/tui/app_sampler.rs +++ b/crates/tek/src/tui/app_sampler.rs @@ -50,6 +50,7 @@ pub enum SamplerFocus { _TODO } +audio!(|self: SamplerTui, _client, _scope|Control::Continue); render!(|self: SamplerTui|render(|to|{ let [x, y, _, height] = to.area(); let style = Style::default().gray(); @@ -93,11 +94,6 @@ impl SamplerTui { None } } -impl Audio for SamplerTui { - #[inline] fn process (&mut self, client: &Client, scope: &ProcessScope) -> Control { - Control::Continue - } -} pub struct AddSampleModal { exited: bool, diff --git a/crates/tek/src/tui/app_sequencer.rs b/crates/tek/src/tui/app_sequencer.rs index 3c2022dd..c8a70f20 100644 --- a/crates/tek/src/tui/app_sequencer.rs +++ b/crates/tek/src/tui/app_sequencer.rs @@ -61,26 +61,6 @@ pub enum SequencerFocus { PhraseEditor, } -impl Audio for SequencerTui { - fn process (&mut self, client: &Client, scope: &ProcessScope) -> Control { - // Start profiling cycle - let t0 = self.perf.get_t0(); - // Update transport clock - if Control::Quit == ClockAudio(self).process(client, scope) { - return Control::Quit - } - // Update MIDI sequencer - if Control::Quit == PlayerAudio( - &mut self.player, &mut self.note_buf, &mut self.midi_buf - ).process(client, scope) { - return Control::Quit - } - // End profiling cycle - self.perf.update(t0, scope); - Control::Continue - } -} - #[derive(Clone, Debug)] pub enum SequencerCommand { Focus(FocusCommand), @@ -175,6 +155,24 @@ pub fn to_sequencer_command (state: &SequencerTui, input: &TuiInput) -> Option 60 { diff --git a/crates/tek/src/tui/app_transport.rs b/crates/tek/src/tui/app_transport.rs index af48ff98..b4c3cd6b 100644 --- a/crates/tek/src/tui/app_transport.rs +++ b/crates/tek/src/tui/app_transport.rs @@ -38,13 +38,7 @@ impl std::fmt::Debug for TransportTui { } has_clock!(|self:TransportTui|&self.clock); - -impl Audio for TransportTui { - fn process (&mut self, client: &Client, scope: &ProcessScope) -> Control { - ClockAudio(self).process(client, scope) - } -} - +audio!(|self:TransportTui,client,scope|ClockAudio(self).process(client, scope)); render!(|self: TransportTui|TransportView::from((self, None, true))); pub struct TransportView {