add audio! macro

This commit is contained in:
🪞👃🪞 2024-12-14 23:32:07 +01:00
parent 32eb1bf085
commit 9f97c44c84
8 changed files with 139 additions and 162 deletions

View file

@ -20,8 +20,4 @@ impl From<&Arc<RwLock<Mixer>>> for MixerAudio {
}
}
impl Audio for MixerAudio {
fn process (&mut self, _: &Client, _: &ProcessScope) -> Control {
Control::Continue
}
}
audio!(|self: MixerAudio, _, _|Control::Continue);

View file

@ -62,53 +62,51 @@ impl From<&Arc<RwLock<Plugin>>> 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
});

View file

@ -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<RwLock<Self>>, 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<Port<MidiIn>>;
fn midi_ins_mut (&mut self) -> &mut Vec<Port<MidiIn>>;
@ -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<RwLock<Self>>, 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<E: Engine>: Component<E> + Audio {
/// Perform type erasure for collecting heterogeneous devices.

View file

@ -96,15 +96,13 @@ impl From<&Arc<RwLock<Sampler>>> 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 {

View file

@ -323,53 +323,11 @@ impl TransportControl<ArrangerFocus> 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 {

View file

@ -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,

View file

@ -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<SequencerFocus>),
@ -175,6 +155,24 @@ pub fn to_sequencer_command (state: &SequencerTui, input: &TuiInput) -> Option<S
})
}
audio!(|self:SequencerTui,client,scope|{
// 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
});
render!(|self: SequencerTui|lay!([self.size, Tui::split_up(false, 1,
Tui::fill_xy(SequencerStatusBar::from(self)),
Tui::split_right(false, if self.size.w() > 60 {

View file

@ -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 {