mirror of
https://codeberg.org/unspeaker/tek.git
synced 2025-12-06 11:46:41 +01:00
add audio! macro
This commit is contained in:
parent
32eb1bf085
commit
9f97c44c84
8 changed files with 139 additions and 162 deletions
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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
|
||||
});
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue