Skip to content

Commit 1dc0be6

Browse files
committed
Create rtrb
1 parent cdad744 commit 1dc0be6

3 files changed

Lines changed: 125 additions & 1 deletion

File tree

Cargo.toml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,14 @@ jack-sys = {version = "0.5", path = "./jack-sys"}
1616
lazy_static = "1.4"
1717
libc = "0.2"
1818
log = { version = "0.4", optional = true}
19+
rtrb = { version = "0.3.2", optional = true }
1920

2021
[dev-dependencies]
2122
approx = "0.5"
2223
crossbeam-channel = "0.5"
2324
ctor = "0.2"
2425

2526
[features]
26-
default = ["dynamic_loading", "log"]
27+
default = ["dynamic_loading", "log", "controller"]
2728
dynamic_loading = ["jack-sys/dynamic_loading"]
29+
controller = ["rtrb"]

src/contrib/controller.rs

Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
//! Utilities for building controllable JACK processors with lock-free communication.
2+
3+
use rtrb::{Consumer, Producer, RingBuffer};
4+
5+
use crate::{
6+
Client, Control, Frames, ProcessHandler, ProcessScope, TransportPosition, TransportState,
7+
};
8+
9+
/// Communication channels available to a processor in the real-time audio thread.
10+
pub struct ProcessorChannels<Command, Notification> {
11+
/// Send notifications from the processor to the controller.
12+
pub notifications: Producer<Notification>,
13+
/// Receive commands from the controller.
14+
pub commands: Consumer<Command>,
15+
}
16+
17+
/// Handle for controlling a processor from outside the real-time audio thread.
18+
pub struct ProcessorHandle<Command, Notification> {
19+
/// Receive notifications from the processor.
20+
pub notifications: Consumer<Notification>,
21+
/// Send commands to the processor.
22+
pub commands: Producer<Command>,
23+
}
24+
25+
/// A JACK processor that can be controlled via lock-free channels.
26+
///
27+
/// Implement this trait to create a processor that communicates with external
28+
/// code through commands and notifications while running in the real-time thread.
29+
pub trait ControlledProcessorTrait: Send + Sized {
30+
/// Commands sent from the controller to the processor.
31+
type Command: Send;
32+
/// Notifications sent from the processor to the controller.
33+
type Notification: Send;
34+
35+
/// See [`ProcessHandler::SLOW_SYNC`].
36+
const SLOW_SYNC: bool = false;
37+
38+
/// Called when the transport state changes. See [`ProcessHandler::sync`].
39+
fn sync(
40+
&mut self,
41+
_client: &Client,
42+
_state: TransportState,
43+
_pos: &TransportPosition,
44+
_channels: &mut ProcessorChannels<Self::Command, Self::Notification>,
45+
) -> bool {
46+
true
47+
}
48+
49+
/// Called when the buffer size changes. See [`ProcessHandler::buffer_size`].
50+
fn buffer_size(
51+
&mut self,
52+
client: &Client,
53+
size: Frames,
54+
channels: &mut ProcessorChannels<Self::Command, Self::Notification>,
55+
) -> Control;
56+
57+
/// Process audio. See [`ProcessHandler::process`].
58+
fn process(
59+
&mut self,
60+
client: &Client,
61+
scope: &ProcessScope,
62+
channels: &mut ProcessorChannels<Self::Command, Self::Notification>,
63+
) -> Control;
64+
65+
/// Create a processor instance and its control handle with the given channel capacities.
66+
fn instance(
67+
self,
68+
notification_channel_size: usize,
69+
command_channel_size: usize,
70+
) -> (
71+
ControlledProcessorInstance<Self>,
72+
ProcessorHandle<Self::Command, Self::Notification>,
73+
) {
74+
let (notifications, notifications_other) =
75+
RingBuffer::<Self::Notification>::new(notification_channel_size);
76+
let (commands_other, commands) = RingBuffer::<Self::Command>::new(command_channel_size);
77+
let handle = ProcessorHandle {
78+
notifications: notifications_other,
79+
commands: commands_other,
80+
};
81+
let processor = ControlledProcessorInstance {
82+
inner: self,
83+
channels: ProcessorChannels {
84+
notifications,
85+
commands,
86+
},
87+
};
88+
(processor, handle)
89+
}
90+
}
91+
92+
/// A [`ProcessHandler`] wrapper that provides channel-based communication.
93+
///
94+
/// Created via [`ControlledProcessorTrait::instance`].
95+
pub struct ControlledProcessorInstance<T: ControlledProcessorTrait> {
96+
inner: T,
97+
channels: ProcessorChannels<T::Command, T::Notification>,
98+
}
99+
100+
impl<T: ControlledProcessorTrait> ProcessHandler for ControlledProcessorInstance<T> {
101+
fn process(&mut self, client: &Client, scope: &ProcessScope) -> Control {
102+
self.inner.process(client, scope, &mut self.channels)
103+
}
104+
105+
const SLOW_SYNC: bool = T::SLOW_SYNC;
106+
107+
fn buffer_size(&mut self, client: &Client, size: Frames) -> Control {
108+
self.inner.buffer_size(client, size, &mut self.channels)
109+
}
110+
111+
fn sync(
112+
&mut self,
113+
client: &Client,
114+
state: crate::TransportState,
115+
pos: &crate::TransportPosition,
116+
) -> bool {
117+
self.inner.sync(client, state, pos, &mut self.channels)
118+
}
119+
}

src/lib.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,9 @@ pub mod contrib {
7272
mod closure;
7373

7474
pub use closure::ClosureProcessHandler;
75+
76+
#[cfg(feature = "controller")]
77+
pub mod controller;
7578
}
7679

7780
#[cfg(test)]

0 commit comments

Comments
 (0)