mirror of
https://codeberg.org/unspeaker/tek.git
synced 2025-12-09 13:16:44 +01:00
271 lines
8.4 KiB
Rust
271 lines
8.4 KiB
Rust
use crate::core::*;
|
|
|
|
pub struct Jack {
|
|
pub client: Client,
|
|
pub ports: JackPorts,
|
|
}
|
|
|
|
pub struct JackDevice {
|
|
pub client: DynamicAsyncClient,
|
|
pub state: Arc<Mutex<Box<dyn Device>>>,
|
|
pub ports: UnownedJackPorts,
|
|
}
|
|
|
|
pub enum JackClient {
|
|
Active(DynamicAsyncClient),
|
|
Inactive(Client),
|
|
}
|
|
|
|
ports!(JackDevice {
|
|
audio: {
|
|
ins: |s|Ok(s.ports.audio_ins.values().collect()),
|
|
outs: |s|Ok(s.ports.audio_outs.values().collect()),
|
|
}
|
|
midi: {
|
|
ins: |s|Ok(s.ports.midi_ins.values().collect()),
|
|
outs: |s|Ok(s.ports.midi_outs.values().collect()),
|
|
}
|
|
});
|
|
|
|
impl Jack {
|
|
pub fn new (name: &str) -> Usually<Self> {
|
|
let (client, _) = Client::new(name, ClientOptions::NO_START_SERVER)?;
|
|
Ok(Self { client, ports: JackPorts::default() })
|
|
}
|
|
pub fn run <T: Device + Process + Sized + 'static> (
|
|
mut self, state: impl FnOnce(JackPorts)->Box<T>
|
|
)
|
|
-> Usually<JackDevice>
|
|
{
|
|
let mut owned_ports = JackPorts::default();
|
|
std::mem::swap(&mut self.ports, &mut owned_ports);
|
|
let unowned_ports = owned_ports.clone_unowned(&self.client);
|
|
let state = Arc::new(Mutex::new(state(owned_ports) as Box<dyn Device>));
|
|
let client = self.client.activate_async(
|
|
Notifications(Box::new({
|
|
let _state = state.clone();
|
|
move|_event|{
|
|
// FIXME: this deadlocks
|
|
//state.lock().unwrap().handle(&event).unwrap();
|
|
}
|
|
}) as Box<dyn Fn(AppEvent) + Send + Sync>),
|
|
ClosureProcessHandler::new(Box::new({
|
|
let state = state.clone();
|
|
move|c: &Client, s: &ProcessScope|{
|
|
state.lock().unwrap().process(c, s)
|
|
}
|
|
}) as BoxedProcessHandler)
|
|
)?;
|
|
Ok(JackDevice { client, state, ports: unowned_ports })
|
|
}
|
|
pub fn ports_from_lv2 (self, plugin: &::livi::Plugin) -> Usually<Self> {
|
|
let counts = plugin.port_counts();
|
|
let mut jack = self;
|
|
for i in 0..counts.atom_sequence_inputs {
|
|
jack = jack.register_midi_in(&format!("midi-in-{i}"))?
|
|
}
|
|
for i in 0..counts.atom_sequence_outputs {
|
|
jack = jack.register_midi_out(&format!("midi-out-{i}"))?;
|
|
}
|
|
for i in 0..counts.audio_inputs {
|
|
jack = jack.register_audio_in(&format!("audio-in-{i}"))?
|
|
}
|
|
for i in 0..counts.audio_outputs {
|
|
jack = jack.register_audio_out(&format!("audio-out-{i}"))?;
|
|
}
|
|
Ok(jack)
|
|
}
|
|
pub fn register_midi_out (mut self, name: &str) -> Usually<Self> {
|
|
let port = self.client.register_port(name, MidiOut::default())?;
|
|
self.ports.midi_outs.insert(name.to_string(), port);
|
|
Ok(self)
|
|
}
|
|
pub fn register_midi_in (mut self, name: &str) -> Usually<Self> {
|
|
let port = self.client.register_port(name, MidiIn::default())?;
|
|
self.ports.midi_ins.insert(name.to_string(), port);
|
|
Ok(self)
|
|
}
|
|
pub fn register_audio_out (mut self, name: &str) -> Usually<Self> {
|
|
let port = self.client.register_port(name, AudioOut::default())?;
|
|
self.ports.audio_outs.insert(name.to_string(), port);
|
|
Ok(self)
|
|
}
|
|
pub fn register_audio_in (mut self, name: &str) -> Usually<Self> {
|
|
let port = self.client.register_port(name, AudioIn::default())?;
|
|
self.ports.audio_ins.insert(name.to_string(), port);
|
|
Ok(self)
|
|
}
|
|
}
|
|
|
|
pub fn jack_run <T> (name: &str, app: &Arc<Mutex<T>>) -> Usually<DynamicAsyncClient>
|
|
where T: Handle + Process + Send + 'static
|
|
{
|
|
let options = ClientOptions::NO_START_SERVER;
|
|
let (client, _status) = Client::new(name, options)?;
|
|
Ok(client.activate_async(
|
|
Notifications(Box::new({
|
|
let _app = app.clone();
|
|
move|_event|{
|
|
// FIXME: this deadlocks
|
|
//app.lock().unwrap().handle(&event).unwrap();
|
|
}
|
|
}) as Box<dyn Fn(AppEvent) + Send + Sync>),
|
|
ClosureProcessHandler::new(Box::new({
|
|
let app = app.clone();
|
|
move|c: &Client, s: &ProcessScope|{
|
|
app.lock().unwrap().process(c, s)
|
|
//Control::Continue
|
|
}
|
|
}) as BoxedProcessHandler)
|
|
)?)
|
|
}
|
|
|
|
pub trait Process {
|
|
fn process (&mut self, _: &Client, _: &ProcessScope) -> Control {
|
|
Control::Continue
|
|
}
|
|
}
|
|
|
|
#[macro_export] macro_rules! process {
|
|
($T:ty) => {
|
|
impl Process for $T {}
|
|
};
|
|
($T:ty |$self:ident, $c:ident, $s:ident|$block:tt) => {
|
|
impl Process for $T {
|
|
fn process (&mut $self, $c: &Client, $s: &ProcessScope) -> Control {
|
|
$block
|
|
}
|
|
}
|
|
};
|
|
($T:ty = $process:path) => {
|
|
impl Process for $T {
|
|
fn process (&mut self, c: &Client, s: &ProcessScope) -> Control {
|
|
$process(self, c, s)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
pub type DynamicAsyncClient =
|
|
AsyncClient<DynamicNotifications, DynamicProcessHandler>;
|
|
|
|
pub type DynamicNotifications =
|
|
Notifications<Box<dyn Fn(AppEvent) + Send + Sync>>;
|
|
|
|
pub type DynamicProcessHandler =
|
|
ClosureProcessHandler<BoxedProcessHandler>;
|
|
|
|
pub type BoxedProcessHandler =
|
|
Box<dyn FnMut(&Client, &ProcessScope)-> Control + Send>;
|
|
|
|
pub use ::jack::{
|
|
AsyncClient,
|
|
AudioIn,
|
|
AudioOut,
|
|
Client,
|
|
ClientOptions,
|
|
ClientStatus,
|
|
ClosureProcessHandler,
|
|
Control,
|
|
Frames,
|
|
MidiIn,
|
|
MidiOut,
|
|
NotificationHandler,
|
|
Port,
|
|
PortFlags,
|
|
PortId,
|
|
PortSpec,
|
|
ProcessHandler,
|
|
ProcessScope,
|
|
RawMidi,
|
|
Transport,
|
|
TransportState,
|
|
TransportStatePosition,
|
|
Unowned
|
|
};
|
|
|
|
#[derive(Debug)]
|
|
pub enum JackEvent {
|
|
ThreadInit,
|
|
Shutdown(ClientStatus, String),
|
|
Freewheel(bool),
|
|
SampleRate(Frames),
|
|
ClientRegistration(String, bool),
|
|
PortRegistration(PortId, bool),
|
|
PortRename(PortId, String, String),
|
|
PortsConnected(PortId, PortId, bool),
|
|
GraphReorder,
|
|
XRun,
|
|
}
|
|
|
|
pub struct Notifications<T: Fn(AppEvent) + Send>(pub T);
|
|
|
|
impl<T: Fn(AppEvent) + Send> NotificationHandler for Notifications<T> {
|
|
fn thread_init (&self, _: &Client) {
|
|
self.0(AppEvent::Jack(JackEvent::ThreadInit));
|
|
}
|
|
|
|
fn shutdown (&mut self, status: ClientStatus, reason: &str) {
|
|
self.0(AppEvent::Jack(JackEvent::Shutdown(status, reason.into())));
|
|
}
|
|
|
|
fn freewheel (&mut self, _: &Client, enabled: bool) {
|
|
self.0(AppEvent::Jack(JackEvent::Freewheel(enabled)));
|
|
}
|
|
|
|
fn sample_rate (&mut self, _: &Client, frames: Frames) -> Control {
|
|
self.0(AppEvent::Jack(JackEvent::SampleRate(frames)));
|
|
Control::Quit
|
|
}
|
|
|
|
fn client_registration (&mut self, _: &Client, name: &str, reg: bool) {
|
|
self.0(AppEvent::Jack(JackEvent::ClientRegistration(name.into(), reg)));
|
|
}
|
|
|
|
fn port_registration (&mut self, _: &Client, id: PortId, reg: bool) {
|
|
self.0(AppEvent::Jack(JackEvent::PortRegistration(id, reg)));
|
|
}
|
|
|
|
fn port_rename (&mut self, _: &Client, id: PortId, old: &str, new: &str) -> Control {
|
|
self.0(AppEvent::Jack(JackEvent::PortRename(id, old.into(), new.into())));
|
|
Control::Continue
|
|
}
|
|
|
|
fn ports_connected (&mut self, _: &Client, a: PortId, b: PortId, are: bool) {
|
|
self.0(AppEvent::Jack(JackEvent::PortsConnected(a, b, are)));
|
|
}
|
|
|
|
fn graph_reorder (&mut self, _: &Client) -> Control {
|
|
self.0(AppEvent::Jack(JackEvent::GraphReorder));
|
|
Control::Continue
|
|
}
|
|
|
|
fn xrun (&mut self, _: &Client) -> Control {
|
|
self.0(AppEvent::Jack(JackEvent::XRun));
|
|
Control::Continue
|
|
}
|
|
}
|
|
|
|
/// Add "all notes off" to the start of a buffer.
|
|
pub fn all_notes_off (output: &mut MIDIChunk) {
|
|
output[0] = Some(vec![]);
|
|
if let Some(Some(frame)) = output.get_mut(0) {
|
|
let mut buf = vec![];
|
|
let msg = MidiMessage::Controller { controller: 123.into(), value: 0.into() };
|
|
let evt = LiveEvent::Midi { channel: 0.into(), message: msg };
|
|
evt.write(&mut buf).unwrap();
|
|
frame.push(buf);
|
|
}
|
|
}
|
|
|
|
/// Write to JACK port from output buffer (containing notes from sequence and/or monitor)
|
|
pub fn write_output (writer: &mut ::jack::MidiWriter, output: &mut MIDIChunk, frames: usize) {
|
|
for time in 0..frames {
|
|
if let Some(Some(frame)) = output.get_mut(time ) {
|
|
for event in frame.iter() {
|
|
writer.write(&::jack::RawMidi { time: time as u32, bytes: &event })
|
|
.expect(&format!("{event:?}"));
|
|
}
|
|
}
|
|
}
|
|
}
|