mirror of
https://codeberg.org/unspeaker/tek.git
synced 2025-12-08 04:36:45 +01:00
wip: refactor pt.12, separate tek_snd
This commit is contained in:
parent
47c9cd2fe8
commit
2be7aee002
28 changed files with 955 additions and 766 deletions
8
crates/tek_snd/Cargo.toml
Normal file
8
crates/tek_snd/Cargo.toml
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
[package]
|
||||
name = "tek_snd"
|
||||
edition = "2021"
|
||||
version = "0.1.0"
|
||||
|
||||
[dependencies]
|
||||
tek_core = { path = "../tek_core" }
|
||||
tek_api = { path = "../tek_api" }
|
||||
9
crates/tek_snd/src/lib.rs
Normal file
9
crates/tek_snd/src/lib.rs
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
pub use tek_core::{*, jack::{*, Transport as JackTransport}};
|
||||
pub use tek_api::{*, Transport};
|
||||
pub(crate) use tek_core::midly::{*, live::LiveEvent, num::u7};
|
||||
|
||||
submod! {
|
||||
snd_arrange
|
||||
snd_mixer
|
||||
snd_sampler
|
||||
}
|
||||
22
crates/tek_snd/src/snd_arrange.rs
Normal file
22
crates/tek_snd/src/snd_arrange.rs
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
use crate::*;
|
||||
|
||||
pub struct ArrangementAudio {
|
||||
model: Arc<RwLock<Arrangement>>
|
||||
}
|
||||
|
||||
impl From<&Arc<RwLock<Arrangement>>> for ArrangementAudio {
|
||||
fn from (model: &Arc<RwLock<Arrangement>>) -> Self {
|
||||
Self { model: model.clone() }
|
||||
}
|
||||
}
|
||||
|
||||
impl Audio for ArrangementAudio {
|
||||
#[inline] fn process (&mut self, client: &Client, scope: &ProcessScope) -> Control {
|
||||
for track in self.model.write().unwrap().tracks.iter_mut() {
|
||||
if track.player.process(client, scope) == Control::Quit {
|
||||
return Control::Quit
|
||||
}
|
||||
}
|
||||
Control::Continue
|
||||
}
|
||||
}
|
||||
17
crates/tek_snd/src/snd_mixer.rs
Normal file
17
crates/tek_snd/src/snd_mixer.rs
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
use crate::*;
|
||||
|
||||
pub struct MixerAudio {
|
||||
model: Arc<RwLock<Mixer>>
|
||||
}
|
||||
|
||||
impl From<&Arc<RwLock<Mixer>>> for MixerAudio {
|
||||
fn from (model: &Arc<RwLock<Mixer>>) -> Self {
|
||||
Self { model: model.clone() }
|
||||
}
|
||||
}
|
||||
|
||||
impl Audio for MixerAudio {
|
||||
fn process (&mut self, _: &Client, _: &ProcessScope) -> Control {
|
||||
Control::Continue
|
||||
}
|
||||
}
|
||||
65
crates/tek_snd/src/snd_plugin.rs
Normal file
65
crates/tek_snd/src/snd_plugin.rs
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
use crate::*;
|
||||
|
||||
pub struct PluginAudio {
|
||||
model: Arc<RwLock<Plugin>>
|
||||
}
|
||||
|
||||
impl From<&Arc<RwLock<Plugin>>> for PluginAudio {
|
||||
fn from (model: &Arc<RwLock<Plugin>>) -> Self {
|
||||
Self { model: model.clone() }
|
||||
}
|
||||
}
|
||||
|
||||
impl Audio for PluginAudio {
|
||||
fn process (&mut self, _: &Client, scope: &ProcessScope) -> Control {
|
||||
match self.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 self.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 self.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(
|
||||
self.audio_ins.iter().map(|o|o.as_slice(scope))
|
||||
)
|
||||
.with_audio_outputs(
|
||||
self.audio_outs.iter_mut().map(|o|o.as_mut_slice(scope))
|
||||
);
|
||||
unsafe {
|
||||
instance.run(scope.n_frames() as usize, ports).unwrap()
|
||||
};
|
||||
},
|
||||
_ => {}
|
||||
}
|
||||
Control::Continue
|
||||
}
|
||||
}
|
||||
79
crates/tek_snd/src/snd_sampler.rs
Normal file
79
crates/tek_snd/src/snd_sampler.rs
Normal file
|
|
@ -0,0 +1,79 @@
|
|||
use crate::*;
|
||||
|
||||
pub struct SamplerAudio {
|
||||
model: Arc<RwLock<Sampler>>
|
||||
}
|
||||
|
||||
impl From<&Arc<RwLock<Sampler>>> for SamplerAudio {
|
||||
fn from (model: &Arc<RwLock<Sampler>>) -> Self {
|
||||
Self { model: model.clone() }
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
impl SamplerAudio {
|
||||
|
||||
/// Create [Voice]s from [Sample]s in response to MIDI input.
|
||||
pub fn process_midi_in (&mut self, scope: &ProcessScope) {
|
||||
let Sampler { midi_in, mapped, voices, .. } = &*self.model.read().unwrap();
|
||||
for RawMidi { time, bytes } in midi_in.iter(scope) {
|
||||
if let LiveEvent::Midi { message, .. } = LiveEvent::parse(bytes).unwrap() {
|
||||
if let MidiMessage::NoteOn { ref key, ref vel } = message {
|
||||
if let Some(sample) = mapped.get(key) {
|
||||
voices.write().unwrap().push(Sample::play(sample, time as usize, vel));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Zero the output buffer.
|
||||
pub fn clear_output_buffer (&mut self) {
|
||||
for buffer in self.model.write().unwrap().buffer.iter_mut() {
|
||||
buffer.fill(0.0);
|
||||
}
|
||||
}
|
||||
|
||||
/// Mix all currently playing samples into the output.
|
||||
pub fn process_audio_out (&mut self, scope: &ProcessScope) {
|
||||
let Sampler { ref mut buffer, voices, output_gain, .. } = &mut*self.model.write().unwrap();
|
||||
let channel_count = buffer.len();
|
||||
voices.write().unwrap().retain_mut(|voice|{
|
||||
for index in 0..scope.n_frames() as usize {
|
||||
if let Some(frame) = voice.next() {
|
||||
for (channel, sample) in frame.iter().enumerate() {
|
||||
// Averaging mixer:
|
||||
//self.buffer[channel % channel_count][index] = (
|
||||
//(self.buffer[channel % channel_count][index] + sample * self.output_gain) / 2.0
|
||||
//);
|
||||
buffer[channel % channel_count][index] += sample * *output_gain;
|
||||
}
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
});
|
||||
}
|
||||
|
||||
/// Write output buffer to output ports.
|
||||
pub fn write_output_buffer (&mut self, scope: &ProcessScope) {
|
||||
let Sampler { ref mut audio_outs, buffer, .. } = &mut*self.model.write().unwrap();
|
||||
for (i, port) in audio_outs.iter_mut().enumerate() {
|
||||
let buffer = &buffer[i];
|
||||
for (i, value) in port.as_mut_slice(scope).iter_mut().enumerate() {
|
||||
*value = *buffer.get(i).unwrap_or(&0.0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
37
crates/tek_snd/src/snd_sequencer.rs
Normal file
37
crates/tek_snd/src/snd_sequencer.rs
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
use crate::*;
|
||||
|
||||
pub struct MIDIPlayerAudio {
|
||||
model: Arc<RwLock<MIDIPlayer>>
|
||||
}
|
||||
|
||||
impl From<&Arc<RwLock<MIDIPlayer>>> for MIDIPlayerAudio {
|
||||
fn from (model: &Arc<RwLock<MIDIPlayer>>) -> Self {
|
||||
Self { model: model.clone() }
|
||||
}
|
||||
}
|
||||
|
||||
/// JACK process callback for a sequencer's phrase player/recorder.
|
||||
impl Audio for MIDIPlayer {
|
||||
fn process (&mut self, _: &Client, scope: &ProcessScope) -> Control {
|
||||
let has_midi_outputs = self.has_midi_outputs();
|
||||
let has_midi_inputs = self.has_midi_inputs();
|
||||
// Clear output buffer(s)
|
||||
self.clear(scope, false);
|
||||
// Write chunk of phrase to output, handle switchover
|
||||
if self.play(scope) {
|
||||
self.switchover(scope);
|
||||
}
|
||||
if has_midi_inputs {
|
||||
if self.recording || self.monitoring {
|
||||
// Record and/or monitor input
|
||||
self.record(scope)
|
||||
} else if has_midi_outputs && self.monitoring {
|
||||
// Monitor input to output
|
||||
self.monitor(scope)
|
||||
}
|
||||
}
|
||||
// Write to output port(s)
|
||||
self.write(scope);
|
||||
Control::Continue
|
||||
}
|
||||
}
|
||||
43
crates/tek_snd/src/snd_transport.rs
Normal file
43
crates/tek_snd/src/snd_transport.rs
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
use crate::*;
|
||||
|
||||
pub struct TransportAudio {
|
||||
model: Transport
|
||||
}
|
||||
|
||||
impl From<&Arc<RwLock<Transport>>> for TransportAudio {
|
||||
fn from (model: &Arc<RwLock<Transport>>) -> Self {
|
||||
Self { model: model.clone() }
|
||||
}
|
||||
}
|
||||
|
||||
impl Audio for Transport {
|
||||
fn process (&mut self, _: &Client, scope: &ProcessScope) -> Control {
|
||||
let times = scope.cycle_times().unwrap();
|
||||
let CycleTimes { current_frames, current_usecs, next_usecs: _, period_usecs: _ } = times;
|
||||
let _chunk_size = scope.n_frames() as usize;
|
||||
let transport = self.transport.query().unwrap();
|
||||
self.clock.current.sample.set(transport.pos.frame() as f64);
|
||||
let mut playing = self.clock.playing.write().unwrap();
|
||||
let mut started = self.clock.started.write().unwrap();
|
||||
if *playing != Some(transport.state) {
|
||||
match transport.state {
|
||||
TransportState::Rolling => {
|
||||
*started = Some((current_frames as usize, current_usecs as usize))
|
||||
},
|
||||
TransportState::Stopped => {
|
||||
*started = None
|
||||
},
|
||||
_ => {}
|
||||
}
|
||||
};
|
||||
*playing = Some(transport.state);
|
||||
if *playing == Some(TransportState::Stopped) {
|
||||
*started = None;
|
||||
}
|
||||
self.clock.current.update_from_usec(match *started {
|
||||
Some((_, usecs)) => current_usecs as f64 - usecs as f64,
|
||||
None => 0.
|
||||
});
|
||||
Control::Continue
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue