Skip to content

Commit 2923d0f

Browse files
authored
Move closure handler to contrib. (#211)
* Move closure handler to contrib. * Add documentation for closure API.
1 parent cb708b2 commit 2923d0f

File tree

16 files changed

+256
-44
lines changed

16 files changed

+256
-44
lines changed

.github/workflows/testing.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,5 +26,9 @@ jobs:
2626
run: cargo build --verbose --no-default-features
2727
- name: Build (metadata)
2828
run: cargo build --verbose --no-default-features --features metadata
29+
- name: Build (examples)
30+
run: cargo build --verbose --examples
2931
- name: Run Tests
3032
run: cargo nextest run --all-features
33+
- name: Run Doc Tests
34+
run: cargo doc && cargo test --doc

docs/contrib/closure_callbacks.md

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
---
2+
layout: page
3+
title: Closure Callbacks
4+
parent: Contrib
5+
permalink: /closure-callbacks
6+
nav_order: 1
7+
---
8+
9+
# Closure Callbacks
10+
11+
Closure callbacks allow you to define functionality inline.
12+
13+
## Process Closure
14+
15+
The typical use case for a process closure involves creating a closure that
16+
contains captures the required state and then activating it.
17+
18+
```rust
19+
// 1. Create the client.
20+
let (client, _status) =
21+
jack::Client::new("silence", jack::ClientOptions::NO_START_SERVER).unwrap();
22+
23+
// 2. Define the state.
24+
let mut output = client.register_port("out", jack::AudioOut::default());
25+
let silence_value = 0.0;
26+
27+
// 3. Define the closure. Use `move` to capture the required state.
28+
let process_callback = move |_: &jack::Client, ps: &jack::ProcessScope| -> jack::Control {
29+
output.as_mut_slice(ps).fill(silence_value);
30+
jack::Control::Continue
31+
};
32+
33+
// 4. Start processing.
34+
let process = jack::contrib::ClosureProcessHandler::new(process_callback);
35+
let active_client = client.activate_async((), process).unwrap();
36+
```
37+
38+
## State + Process Closure + Buffer Closure
39+
40+
`jack::contrib::ClosureProcessHandler` also allows defining a buffer size
41+
callback that can share state with the process callback. The buffer size
42+
callback is useful as it allows the handler to adapt to any changes in the
43+
buffer size.
44+
45+
```rust
46+
// 1. Create the client.
47+
let (client, _status) =
48+
jack::Client::new("silence", jack::ClientOptions::NO_START_SERVER).unwrap();
49+
50+
// 2. Define the state.
51+
struct State {
52+
silence: Vec<f32>,
53+
output: jack::Port<jack::AudioOut>,
54+
}
55+
let state = State {
56+
silence: Vec::new(),
57+
output: client
58+
.register_port("out", jack::AudioOut::default())
59+
.unwrap(),
60+
};
61+
62+
// 3. Define the state and closure.
63+
let process_callback = |state: &mut State, _: &jack::Client, ps: &jack::ProcessScope| {
64+
state
65+
.output
66+
.as_mut_slice(ps)
67+
.copy_from_slice(state.silence.as_slice());
68+
jack::Control::Continue
69+
};
70+
let buffer_callback = |state: &mut State, _: &jack::Client, len: jack::Frames| {
71+
state.silence = vec![0f32; len as usize];
72+
jack::Control::Continue
73+
};
74+
75+
// 4. Start processing.
76+
let process =
77+
jack::contrib::ClosureProcessHandler::with_state(state, process_callback, buffer_callback);
78+
let active_client = client.activate_async((), process).unwrap();
79+
```

docs/contrib/index.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
---
2+
layout: page
3+
title: Contrib
4+
permalink: /contrib
5+
nav_order: 3
6+
---
7+
8+
# Contrib
9+
10+
`jack::contrib` contains convenient but optional utilities.

docs/quickstart.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ fn main() {
8080
out_b_p.clone_from_slice(in_b_p);
8181
jack::Control::Continue
8282
};
83-
let process = jack::ClosureProcessHandler::new(process_callback);
83+
let process = jack::contrib::ClosureProcessHandler::new(process_callback);
8484

8585
// 3. Activate the client, which starts the processing.
8686
let active_client = client.activate_async((), process).unwrap();

examples/playback_capture.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ fn main() {
3131
out_b_p.clone_from_slice(in_b_p);
3232
jack::Control::Continue
3333
};
34-
let process = jack::ClosureProcessHandler::new(process_callback);
34+
let process = jack::contrib::ClosureProcessHandler::new(process_callback);
3535

3636
// Activate the client, which starts the processing.
3737
let active_client = client.activate_async(Notifications, process).unwrap();

examples/show_midi.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ fn main() {
8989

9090
// Activate
9191
let active_client = client
92-
.activate_async((), jack::ClosureProcessHandler::new(cback))
92+
.activate_async((), jack::contrib::ClosureProcessHandler::new(cback))
9393
.unwrap();
9494

9595
// Spawn a non-real-time thread that prints out the midi messages we get.

examples/sine.rs

Lines changed: 24 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -11,38 +11,49 @@ fn main() {
1111
jack::Client::new("rust_jack_sine", jack::ClientOptions::NO_START_SERVER).unwrap();
1212

1313
// 2. register port
14-
let mut out_port = client
14+
let out_port = client
1515
.register_port("sine_out", jack::AudioOut::default())
1616
.unwrap();
1717

1818
// 3. define process callback handler
19-
let mut frequency = 220.0;
20-
let sample_rate = client.sample_rate();
21-
let frame_t = 1.0 / sample_rate as f64;
22-
let mut time = 0.0;
2319
let (tx, rx) = bounded(1_000_000);
24-
let process = jack::ClosureProcessHandler::new(
25-
move |_: &jack::Client, ps: &jack::ProcessScope| -> jack::Control {
20+
struct State {
21+
out_port: jack::Port<jack::AudioOut>,
22+
rx: crossbeam_channel::Receiver<f64>,
23+
frequency: f64,
24+
frame_t: f64,
25+
time: f64,
26+
}
27+
let process = jack::contrib::ClosureProcessHandler::with_state(
28+
State {
29+
out_port,
30+
rx,
31+
frequency: 220.0,
32+
frame_t: 1.0 / client.sample_rate() as f64,
33+
time: 0.0,
34+
},
35+
|state, _, ps| -> jack::Control {
2636
// Get output buffer
27-
let out = out_port.as_mut_slice(ps);
37+
let out = state.out_port.as_mut_slice(ps);
2838

2939
// Check frequency requests
30-
while let Ok(f) = rx.try_recv() {
31-
time = 0.0;
32-
frequency = f;
40+
while let Ok(f) = state.rx.try_recv() {
41+
state.time = 0.0;
42+
state.frequency = f;
3343
}
3444

3545
// Write output
3646
for v in out.iter_mut() {
37-
let x = frequency * time * 2.0 * std::f64::consts::PI;
47+
let x = state.frequency * state.time * 2.0 * std::f64::consts::PI;
3848
let y = x.sin();
3949
*v = y as f32;
40-
time += frame_t;
50+
state.time += state.frame_t;
4151
}
4252

4353
// Continue as normal
4454
jack::Control::Continue
4555
},
56+
move |_, _, _| jack::Control::Continue,
4657
);
4758

4859
// 4. Activate the client. Also connect the ports to the system audio.

src/client/async_client.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ use crate::Error;
2121
/// // Create a client and a handler
2222
/// let (client, _status) =
2323
/// jack::Client::new("my_client", jack::ClientOptions::NO_START_SERVER).unwrap();
24-
/// let process_handler = jack::ClosureProcessHandler::new(
24+
/// let process_handler = jack::contrib::ClosureProcessHandler::new(
2525
/// move |_: &jack::Client, _: &jack::ProcessScope| jack::Control::Continue,
2626
/// );
2727
///

src/client/callbacks.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ pub trait NotificationHandler: Send {
2222
/// pipe so that the rest of the application knows that the JACK client thread has shut down.
2323
///
2424
/// # Safety
25-
/// See https://man7.org/linux/man-pages/man7/signal-safety.7.html for details about
25+
/// See <https://man7.org/linux/man-pages/man7/signal-safety.7.html> for details about
2626
/// what is legal in an async-signal-safe callback.
2727
unsafe fn shutdown(&mut self, _status: ClientStatus, _reason: &str) {}
2828

src/client/handler_impls.rs

Lines changed: 2 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -13,24 +13,5 @@ impl ProcessHandler for () {
1313

1414
/// Wrap a closure that can handle the `process` callback. This is called every time data from ports
1515
/// is available from JACK.
16-
pub struct ClosureProcessHandler<F: 'static + Send + FnMut(&Client, &ProcessScope) -> Control> {
17-
pub process_fn: F,
18-
}
19-
20-
impl<F> ClosureProcessHandler<F>
21-
where
22-
F: 'static + Send + FnMut(&Client, &ProcessScope) -> Control,
23-
{
24-
pub fn new(f: F) -> ClosureProcessHandler<F> {
25-
ClosureProcessHandler { process_fn: f }
26-
}
27-
}
28-
29-
impl<F> ProcessHandler for ClosureProcessHandler<F>
30-
where
31-
F: 'static + Send + FnMut(&Client, &ProcessScope) -> Control,
32-
{
33-
fn process(&mut self, c: &Client, ps: &ProcessScope) -> Control {
34-
(self.process_fn)(c, ps)
35-
}
36-
}
16+
#[deprecated = "Prefer using jack::contrib::ClosureProcessHandler directly."]
17+
pub type ClosureProcessHandler<F> = crate::contrib::ClosureProcessHandler<(), F>;

0 commit comments

Comments
 (0)