mirror of
https://codeberg.org/unspeaker/tek.git
synced 2025-12-07 12:16:42 +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 {
|
audio!(|self: MixerAudio, _, _|Control::Continue);
|
||||||
fn process (&mut self, _: &Client, _: &ProcessScope) -> Control {
|
|
||||||
Control::Continue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -62,53 +62,51 @@ impl From<&Arc<RwLock<Plugin>>> for PluginAudio {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Audio for PluginAudio {
|
audio!(|self: PluginAudio, _, _|{
|
||||||
fn process (&mut self, _: &Client, scope: &ProcessScope) -> Control {
|
let state = &mut*self.0.write().unwrap();
|
||||||
let state = &mut*self.0.write().unwrap();
|
match state.plugin.as_mut() {
|
||||||
match state.plugin.as_mut() {
|
Some(PluginKind::LV2(LV2Plugin {
|
||||||
Some(PluginKind::LV2(LV2Plugin {
|
features,
|
||||||
features,
|
ref mut instance,
|
||||||
ref mut instance,
|
ref mut input_buffer,
|
||||||
ref mut input_buffer,
|
..
|
||||||
..
|
})) => {
|
||||||
})) => {
|
let urid = features.midi_urid();
|
||||||
let urid = features.midi_urid();
|
input_buffer.clear();
|
||||||
input_buffer.clear();
|
for port in state.midi_ins.iter() {
|
||||||
for port in state.midi_ins.iter() {
|
let mut atom = ::livi::event::LV2AtomSequence::new(
|
||||||
let mut atom = ::livi::event::LV2AtomSequence::new(
|
&features,
|
||||||
&features,
|
scope.n_frames() as usize
|
||||||
scope.n_frames() as usize
|
);
|
||||||
);
|
for event in port.iter(scope) {
|
||||||
for event in port.iter(scope) {
|
match event.bytes.len() {
|
||||||
match event.bytes.len() {
|
3 => atom.push_midi_event::<3>(
|
||||||
3 => atom.push_midi_event::<3>(
|
event.time as i64,
|
||||||
event.time as i64,
|
urid,
|
||||||
urid,
|
&event.bytes[0..3]
|
||||||
&event.bytes[0..3]
|
).unwrap(),
|
||||||
).unwrap(),
|
_ => {}
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
input_buffer.push(atom);
|
|
||||||
}
|
}
|
||||||
let mut outputs = vec![];
|
input_buffer.push(atom);
|
||||||
for _ in state.midi_outs.iter() {
|
}
|
||||||
outputs.push(::livi::event::LV2AtomSequence::new(
|
let mut outputs = vec![];
|
||||||
&features,
|
for _ in state.midi_outs.iter() {
|
||||||
scope.n_frames() as usize
|
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())
|
let ports = ::livi::EmptyPortConnections::new()
|
||||||
.with_audio_inputs(state.audio_ins.iter().map(|o|o.as_slice(scope)))
|
.with_atom_sequence_inputs(input_buffer.iter())
|
||||||
.with_audio_outputs(state.audio_outs.iter_mut().map(|o|o.as_mut_slice(scope)));
|
.with_atom_sequence_outputs(outputs.iter_mut())
|
||||||
unsafe {
|
.with_audio_inputs(state.audio_ins.iter().map(|o|o.as_slice(scope)))
|
||||||
instance.run(scope.n_frames() as usize, ports).unwrap()
|
.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
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
Control::Continue
|
||||||
|
});
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,29 @@
|
||||||
use crate::*;
|
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 {
|
pub trait HasMidiIns {
|
||||||
fn midi_ins (&self) -> &Vec<Port<MidiIn>>;
|
fn midi_ins (&self) -> &Vec<Port<MidiIn>>;
|
||||||
fn midi_ins_mut (&mut self) -> &mut 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.
|
/// A UI component that may be associated with a JACK client by the `Jack` factory.
|
||||||
pub trait AudioComponent<E: Engine>: Component<E> + Audio {
|
pub trait AudioComponent<E: Engine>: Component<E> + Audio {
|
||||||
/// Perform type erasure for collecting heterogeneous devices.
|
/// Perform type erasure for collecting heterogeneous devices.
|
||||||
|
|
|
||||||
|
|
@ -96,15 +96,13 @@ impl From<&Arc<RwLock<Sampler>>> for SamplerAudio {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Audio for SamplerAudio {
|
audio!(|self: SamplerAudio, _client, scope|{
|
||||||
#[inline] fn process (&mut self, _: &Client, scope: &ProcessScope) -> Control {
|
self.process_midi_in(scope);
|
||||||
self.process_midi_in(scope);
|
self.clear_output_buffer();
|
||||||
self.clear_output_buffer();
|
self.process_audio_out(scope);
|
||||||
self.process_audio_out(scope);
|
self.write_output_buffer(scope);
|
||||||
self.write_output_buffer(scope);
|
Control::Continue
|
||||||
Control::Continue
|
});
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl SamplerAudio {
|
impl SamplerAudio {
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -323,53 +323,11 @@ impl TransportControl<ArrangerFocus> for ArrangerTui {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
has_clock!(|self:ArrangerTui|&self.clock);
|
||||||
impl Audio for ArrangerTui {
|
has_clock!(|self:ArrangerTrack|self.player.clock());
|
||||||
#[inline] fn process (&mut self, client: &Client, scope: &ProcessScope) -> Control {
|
has_phrases!(|self:ArrangerTui|self.phrases.phrases);
|
||||||
// Start profiling cycle
|
has_editor!(|self:ArrangerTui|self.editor);
|
||||||
let t0 = self.perf.get_t0();
|
has_player!(|self:ArrangerTrack|self.player);
|
||||||
|
|
||||||
// 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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Layout for standalone arranger app.
|
// Layout for standalone arranger app.
|
||||||
render!(|self: ArrangerTui|{
|
render!(|self: ArrangerTui|{
|
||||||
let arranger_focused = self.arranger_focused();
|
let arranger_focused = self.arranger_focused();
|
||||||
|
|
@ -411,12 +369,43 @@ render!(|self: ArrangerTui|{
|
||||||
])
|
])
|
||||||
])
|
])
|
||||||
});
|
});
|
||||||
|
audio!(|self: ArrangerTui, client, scope|{
|
||||||
has_clock!(|self:ArrangerTui|&self.clock);
|
// Start profiling cycle
|
||||||
has_clock!(|self:ArrangerTrack|self.player.clock());
|
let t0 = self.perf.get_t0();
|
||||||
has_phrases!(|self:ArrangerTui|self.phrases.phrases);
|
// Update transport clock
|
||||||
has_editor!(|self:ArrangerTui|self.editor);
|
if ClockAudio(self).process(client, scope) == Control::Quit {
|
||||||
has_player!(|self:ArrangerTrack|self.player);
|
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 {
|
impl HasPhraseList for ArrangerTui {
|
||||||
fn phrases_focused (&self) -> bool {
|
fn phrases_focused (&self) -> bool {
|
||||||
|
|
|
||||||
|
|
@ -50,6 +50,7 @@ pub enum SamplerFocus {
|
||||||
_TODO
|
_TODO
|
||||||
}
|
}
|
||||||
|
|
||||||
|
audio!(|self: SamplerTui, _client, _scope|Control::Continue);
|
||||||
render!(|self: SamplerTui|render(|to|{
|
render!(|self: SamplerTui|render(|to|{
|
||||||
let [x, y, _, height] = to.area();
|
let [x, y, _, height] = to.area();
|
||||||
let style = Style::default().gray();
|
let style = Style::default().gray();
|
||||||
|
|
@ -93,11 +94,6 @@ impl SamplerTui {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl Audio for SamplerTui {
|
|
||||||
#[inline] fn process (&mut self, client: &Client, scope: &ProcessScope) -> Control {
|
|
||||||
Control::Continue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct AddSampleModal {
|
pub struct AddSampleModal {
|
||||||
exited: bool,
|
exited: bool,
|
||||||
|
|
|
||||||
|
|
@ -61,26 +61,6 @@ pub enum SequencerFocus {
|
||||||
PhraseEditor,
|
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)]
|
#[derive(Clone, Debug)]
|
||||||
pub enum SequencerCommand {
|
pub enum SequencerCommand {
|
||||||
Focus(FocusCommand<SequencerFocus>),
|
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,
|
render!(|self: SequencerTui|lay!([self.size, Tui::split_up(false, 1,
|
||||||
Tui::fill_xy(SequencerStatusBar::from(self)),
|
Tui::fill_xy(SequencerStatusBar::from(self)),
|
||||||
Tui::split_right(false, if self.size.w() > 60 {
|
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);
|
has_clock!(|self:TransportTui|&self.clock);
|
||||||
|
audio!(|self:TransportTui,client,scope|ClockAudio(self).process(client, scope));
|
||||||
impl Audio for TransportTui {
|
|
||||||
fn process (&mut self, client: &Client, scope: &ProcessScope) -> Control {
|
|
||||||
ClockAudio(self).process(client, scope)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
render!(|self: TransportTui|TransportView::from((self, None, true)));
|
render!(|self: TransportTui|TransportView::from((self, None, true)));
|
||||||
|
|
||||||
pub struct TransportView {
|
pub struct TransportView {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue