Skip to content

Commit 28b0a62

Browse files
committed
Adapt examples
1 parent 12edec6 commit 28b0a62

30 files changed

+767
-344
lines changed

Cargo.toml

Lines changed: 6 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "libpd-rs"
3-
version = "0.3.0"
3+
version = "0.2.0"
44
authors = ["alisomay <[email protected]>"]
55
edition = "2021"
66
license = "BSD-3-Clause"
@@ -11,11 +11,7 @@ repository = "https://github.com/alisomay/libpd-rs"
1111
documentation = "https://docs.rs/libpd-rs/latest/libpd_rs/#"
1212
keywords = ["puredata", "libpd", "audio", "midi", "bindings"]
1313
categories = ["multimedia"]
14-
exclude = [
15-
"tests/*",
16-
"assets/favicon/*",
17-
"assets/logo_*"
18-
]
14+
exclude = ["tests/*", "assets/favicon/*", "assets/logo_*"]
1915

2016
[lib]
2117
name = "libpd_rs"
@@ -28,18 +24,20 @@ edition = "2021"
2824
crate-type = ["lib"]
2925

3026
[dependencies]
31-
libpd-sys = "0.3"
27+
# libpd-sys = "0.3"
28+
libpd-sys = { git = "https://github.com/alisomay/libpd-sys.git", branch = "main" }
3229
thiserror = "2"
3330
libffi = "3.0.0"
3431
tempfile = "3.3.0"
3532
embed-doc-image = "0.1.4"
3633

3734
[dev-dependencies]
38-
cpal = "0.15.2"
35+
cpal = "0.15.3"
3936
sys-info = "0.9.1"
4037
nannou = "0.19"
4138
nannou_audio = "0.19"
4239
rand = "0.8.5"
40+
serial_test = "3"
4341

4442
# For local development,
4543
# [patch.crates-io]
@@ -48,7 +46,3 @@ rand = "0.8.5"
4846
# For local development,
4947
# [patch."https://github.com/alisomay/libpd-sys"]
5048
# libpd-sys = { path = "../libpd-sys" }
51-
52-
53-
54-

examples/README.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,14 @@ This one is the exact same program which is provided in the main `README.md` fil
1414
cargo run --example simple
1515
```
1616

17+
## `communicate`
18+
19+
This one is the exact same program which is provided in the docs as the second example.
20+
21+
```sh
22+
cargo run --example communicate
23+
```
24+
1725
## `with_nannou`
1826

1927
[nannou](https://github.com/nannou-org/nannou) is a fantastic creative coding framework for Rust.

examples/communicate.rs

Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
use cpal::traits::{DeviceTrait, HostTrait, StreamTrait};
2+
use libpd_rs::{
3+
functions::{receive::on_float, send::send_list_to, util::calculate_ticks},
4+
Pd,
5+
};
6+
use sys_info::loadavg;
7+
8+
fn main() -> Result<(), Box<dyn std::error::Error>> {
9+
// Initialize cpal
10+
// This could have been another cross platform audio library
11+
// basically anything which gets you the audio callback of the os.
12+
let host = cpal::default_host();
13+
14+
// Currently we're only going to output to the default device
15+
let device = host.default_output_device().unwrap();
16+
17+
// Using the default config
18+
let config = device.default_output_config()?;
19+
20+
// Let's get the default configuration from the audio driver.
21+
let sample_rate = config.sample_rate().0 as i32;
22+
let output_channels = config.channels() as i32;
23+
24+
// Initialize libpd with that configuration,
25+
// with no input channels since we're not going to use them.
26+
let mut pd = Pd::init_and_configure(0, output_channels, sample_rate)?;
27+
let ctx = pd.audio_context();
28+
29+
// Let's evaluate another pd patch.
30+
// We could have opened a `.pd` file also.
31+
pd.eval_patch(
32+
r#"
33+
#N canvas 832 310 625 448 12;
34+
#X obj 18 27 r cpu_load;
35+
#X obj 55 394 s response;
36+
#X obj 13 261 *~;
37+
#X obj 112 240 vline~;
38+
#X obj 118 62 bng 15 250 50 0 empty empty empty 17 7 0 10 -262144 -1
39+
-1;
40+
#X obj 14 395 dac~;
41+
#X obj 50 299 sig~;
42+
#X floatatom 50 268 5 0 0 0 - - -;
43+
#X obj 13 228 phasor~ 120;
44+
#X obj 139 61 metro 2000;
45+
#X obj 139 38 tgl 15 0 empty empty empty 17 7 0 10 -262144 -1 -1 1
46+
1;
47+
#X obj 18 52 unpack f f;
48+
#X obj 14 362 *~ 2;
49+
#X obj 14 336 vcf~ 12;
50+
#X obj 139 12 loadbang;
51+
#X msg 118 86 1 8 \, 0 0 10;
52+
#X obj 149 197 expr (480 + 80) * ($f1 - 8) / (4 - 16) + 480;
53+
#X obj 29 128 * 20;
54+
#X obj 167 273 expr (520 + 120) * ($f1 - 5) / (12 - 5) + 120;
55+
#X connect 0 0 11 0;
56+
#X connect 2 0 13 0;
57+
#X connect 3 0 2 1;
58+
#X connect 4 0 15 0;
59+
#X connect 6 0 13 1;
60+
#X connect 7 0 6 0;
61+
#X connect 8 0 2 0;
62+
#X connect 9 0 15 0;
63+
#X connect 10 0 9 0;
64+
#X connect 11 0 16 0;
65+
#X connect 11 0 18 0;
66+
#X connect 11 1 17 0;
67+
#X connect 12 0 5 0;
68+
#X connect 12 0 5 1;
69+
#X connect 13 0 12 0;
70+
#X connect 14 0 10 0;
71+
#X connect 15 0 3 0;
72+
#X connect 16 0 9 1;
73+
#X connect 17 0 1 0;
74+
#X connect 17 0 13 2;
75+
#X connect 18 0 7 0;
76+
"#,
77+
)?;
78+
79+
// Here we are registering a listener (hook in libpd lingo) for
80+
// float values which are received from the pd patch.
81+
on_float(|source, value| {
82+
if source == "response" {
83+
print!("\r");
84+
print!("Pd says that the q value of the vcf~ is: {value}");
85+
}
86+
});
87+
88+
// Pd can send data to many different endpoints at a time.
89+
// This is why we need to declare our subscription to one or more first.
90+
// In this case we're subscribing to one, but it could have been many,
91+
pd.subscribe_to("response")?;
92+
93+
// Build the audio stream.
94+
let output_stream = device.build_output_stream(
95+
&config.into(),
96+
move |data: &mut [f32], _: &cpal::OutputCallbackInfo| {
97+
// Provide the ticks to advance per iteration for the internal scheduler.
98+
let ticks = calculate_ticks(output_channels, data.len() as i32);
99+
100+
// Here if we had an input buffer
101+
// we could have modified it to do pre-processing.
102+
103+
// To receive messages from the pd patch we need to read the ring buffers
104+
// filled by the pd patch repeatedly to check if there are messages there.
105+
// Audio callback is a nice place to do that.
106+
ctx.receive_messages_from_pd();
107+
108+
// Process audio, advance internal scheduler.
109+
ctx.process_float(ticks, &[], data);
110+
111+
// Here we could have done post processing after
112+
// pd processed our output buffer in place.
113+
},
114+
|err| eprintln!("an error occurred on stream: {}", err),
115+
None,
116+
)?;
117+
118+
// Turn audio processing on
119+
pd.activate_audio(true)?;
120+
121+
// Run the stream
122+
output_stream.play()?;
123+
124+
// This program does not terminate.
125+
// You would need to explicitly quit it.
126+
loop {
127+
// We sample in 2 hz.
128+
std::thread::sleep(std::time::Duration::from_millis(500));
129+
130+
// Read the average load of the cpu.
131+
let load = loadavg()?;
132+
133+
let one_minute_cpu_load_average = load.one;
134+
let five_minutes_cpu_load_average = load.five;
135+
136+
// Lists are one of the types we can send to pd.
137+
// Although pd allows for heterogeneous lists,
138+
// even if we're not using them heterogeneously in this example,
139+
// we still need to send it as a list of Atoms.
140+
141+
// Atom is an encapsulating type in pd to unify
142+
// various types of data together under the same umbrella.
143+
// Check out `libpd_rs::types` module for more details.
144+
145+
// Atoms have From trait implemented for them for
146+
// floats and strings.
147+
send_list_to(
148+
"cpu_load",
149+
&[
150+
one_minute_cpu_load_average.into(),
151+
five_minutes_cpu_load_average.into(),
152+
],
153+
)?;
154+
}
155+
}

examples/simple.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
2020
// Initialize libpd with that configuration,
2121
// with no input channels since we're not going to use them.
2222
let mut pd = Pd::init_and_configure(0, output_channels, sample_rate)?;
23+
let ctx = pd.audio_context();
2324

2425
// Let's evaluate a pd patch.
2526
// We could have opened a `.pd` file also.
@@ -49,7 +50,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
4950
// Here if we had an input buffer we could have modified it to do pre-processing.
5051

5152
// Process audio, advance internal scheduler.
52-
libpd_rs::functions::process::process_float(ticks, &[], data);
53+
ctx.process_float(ticks, &[], data);
5354

5455
// Here we could have done post processing after pd processed our output buffer in place.
5556
},

examples/with_nannou/bubble.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -140,7 +140,7 @@ impl Bubble {
140140
}
141141

142142
/// Transforms the voice message of the bubble to send to pure data.
143-
pub fn pack_message(&self) -> Vec<libpd_rs::types::Atom> {
143+
pub fn pack_message(&self) -> Vec<libpd_rs::atom::Atom> {
144144
self.state
145145
.message
146146
.into_iter()
@@ -212,6 +212,7 @@ impl Bubble {
212212

213213
// Collision with the floor!
214214
if distance_to_floor < self.properties.r * 2.0 {
215+
model.pd.set_as_current();
215216
// On collision we tell the right voice to play with the right parameters in pd.
216217
libpd_rs::functions::send::send_list_to("bubble_collision", &self.pack_message())
217218
.unwrap();

examples/with_nannou/main.rs

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
mod bubble;
33

44
use bubble::Bubble;
5+
use libpd_rs::PdAudioContext;
56
use nannou::prelude::*;
67
use nannou_audio as audio;
78
use nannou_audio::Buffer;
@@ -15,7 +16,7 @@ fn main() {
1516
// This data structure will be shared across nannou functions.
1617
pub struct Model {
1718
pd: libpd_rs::Pd,
18-
output_stream: audio::Stream<()>,
19+
output_stream: audio::Stream<PdAudioContext>,
1920
gravity: f32,
2021
bubbles: RefCell<Vec<Bubble>>,
2122
bubble_count: usize,
@@ -50,9 +51,13 @@ fn model(app: &App) -> Model {
5051
// .into_iter()
5152
// .find(|d| d.name().unwrap() == "BlackHole 16ch");
5253

54+
let pd = libpd_rs::Pd::init_and_configure(0, channels as i32, sample_rate as i32).unwrap();
55+
pd.set_as_current();
56+
let audio_ctx = pd.audio_context();
57+
5358
// Start the stream registering our audio callback.
5459
let output_stream = audio_host
55-
.new_output_stream(())
60+
.new_output_stream(audio_ctx)
5661
// Uncomment to pick another audio device.
5762
// .device(device.unwrap())
5863
.channels(channels)
@@ -69,7 +74,7 @@ fn model(app: &App) -> Model {
6974
// This data structure will be shared across nannou functions.
7075
let mut model = Model {
7176
// Initialize pd
72-
pd: libpd_rs::Pd::init_and_configure(0, channels as i32, sample_rate as i32).unwrap(),
77+
pd,
7378
output_stream,
7479
gravity: 0.8,
7580
bubbles: RefCell::new(vec![]),
@@ -138,15 +143,16 @@ impl Model {
138143

139144
// This is where we process audio.
140145
// We hand over all tasks to our pd patch!
141-
fn audio_callback(_: &mut (), buffer: &mut Buffer) {
146+
fn audio_callback(pd_audio_context: &mut PdAudioContext, buffer: &mut Buffer) {
142147
let ticks =
143148
libpd_rs::functions::util::calculate_ticks(buffer.channels() as i32, buffer.len() as i32);
144-
libpd_rs::functions::process::process_float(ticks, &[], buffer);
149+
pd_audio_context.process_float(ticks, &[], buffer);
145150
}
146151

147152
// This is where we draw repeatedly!
148153
fn view(app: &App, model: &Model, frame: Frame) {
149154
// Let's poll pd messages here, for every frame.
155+
model.pd.set_as_current();
150156
libpd_rs::functions::receive::receive_messages_from_pd();
151157

152158
let background_color = nannou::color::srgb8(238, 108, 77);

0 commit comments

Comments
 (0)