-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
30 changed files
with
764 additions
and
342 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,155 @@ | ||
use cpal::traits::{DeviceTrait, HostTrait, StreamTrait}; | ||
use libpd_rs::{ | ||
functions::{receive::on_float, send::send_list_to, util::calculate_ticks}, | ||
Pd, | ||
}; | ||
use sys_info::loadavg; | ||
|
||
fn main() -> Result<(), Box<dyn std::error::Error>> { | ||
// Initialize cpal | ||
// This could have been another cross platform audio library | ||
// basically anything which gets you the audio callback of the os. | ||
let host = cpal::default_host(); | ||
|
||
// Currently we're only going to output to the default device | ||
let device = host.default_output_device().unwrap(); | ||
|
||
// Using the default config | ||
let config = device.default_output_config()?; | ||
|
||
// Let's get the default configuration from the audio driver. | ||
let sample_rate = config.sample_rate().0 as i32; | ||
let output_channels = config.channels() as i32; | ||
|
||
// Initialize libpd with that configuration, | ||
// with no input channels since we're not going to use them. | ||
let mut pd = Pd::init_and_configure(0, output_channels, sample_rate)?; | ||
let ctx = pd.audio_context(); | ||
|
||
// Let's evaluate another pd patch. | ||
// We could have opened a `.pd` file also. | ||
pd.eval_patch( | ||
r#" | ||
#N canvas 832 310 625 448 12; | ||
#X obj 18 27 r cpu_load; | ||
#X obj 55 394 s response; | ||
#X obj 13 261 *~; | ||
#X obj 112 240 vline~; | ||
#X obj 118 62 bng 15 250 50 0 empty empty empty 17 7 0 10 -262144 -1 | ||
-1; | ||
#X obj 14 395 dac~; | ||
#X obj 50 299 sig~; | ||
#X floatatom 50 268 5 0 0 0 - - -; | ||
#X obj 13 228 phasor~ 120; | ||
#X obj 139 61 metro 2000; | ||
#X obj 139 38 tgl 15 0 empty empty empty 17 7 0 10 -262144 -1 -1 1 | ||
1; | ||
#X obj 18 52 unpack f f; | ||
#X obj 14 362 *~ 2; | ||
#X obj 14 336 vcf~ 12; | ||
#X obj 139 12 loadbang; | ||
#X msg 118 86 1 8 \, 0 0 10; | ||
#X obj 149 197 expr (480 + 80) * ($f1 - 8) / (4 - 16) + 480; | ||
#X obj 29 128 * 20; | ||
#X obj 167 273 expr (520 + 120) * ($f1 - 5) / (12 - 5) + 120; | ||
#X connect 0 0 11 0; | ||
#X connect 2 0 13 0; | ||
#X connect 3 0 2 1; | ||
#X connect 4 0 15 0; | ||
#X connect 6 0 13 1; | ||
#X connect 7 0 6 0; | ||
#X connect 8 0 2 0; | ||
#X connect 9 0 15 0; | ||
#X connect 10 0 9 0; | ||
#X connect 11 0 16 0; | ||
#X connect 11 0 18 0; | ||
#X connect 11 1 17 0; | ||
#X connect 12 0 5 0; | ||
#X connect 12 0 5 1; | ||
#X connect 13 0 12 0; | ||
#X connect 14 0 10 0; | ||
#X connect 15 0 3 0; | ||
#X connect 16 0 9 1; | ||
#X connect 17 0 1 0; | ||
#X connect 17 0 13 2; | ||
#X connect 18 0 7 0; | ||
"#, | ||
)?; | ||
|
||
// Here we are registering a listener (hook in libpd lingo) for | ||
// float values which are received from the pd patch. | ||
on_float(|source, value| { | ||
if source == "response" { | ||
print!("\r"); | ||
print!("Pd says that the q value of the vcf~ is: {value}"); | ||
} | ||
}); | ||
|
||
// Pd can send data to many different endpoints at a time. | ||
// This is why we need to declare our subscription to one or more first. | ||
// In this case we're subscribing to one, but it could have been many, | ||
pd.subscribe_to("response")?; | ||
|
||
// Build the audio stream. | ||
let output_stream = device.build_output_stream( | ||
&config.into(), | ||
move |data: &mut [f32], _: &cpal::OutputCallbackInfo| { | ||
// Provide the ticks to advance per iteration for the internal scheduler. | ||
let ticks = calculate_ticks(output_channels, data.len() as i32); | ||
|
||
// Here if we had an input buffer | ||
// we could have modified it to do pre-processing. | ||
|
||
// To receive messages from the pd patch we need to read the ring buffers | ||
// filled by the pd patch repeatedly to check if there are messages there. | ||
// Audio callback is a nice place to do that. | ||
ctx.receive_messages_from_pd(); | ||
|
||
// Process audio, advance internal scheduler. | ||
ctx.process_float(ticks, &[], data); | ||
|
||
// Here we could have done post processing after | ||
// pd processed our output buffer in place. | ||
}, | ||
|err| eprintln!("an error occurred on stream: {}", err), | ||
None, | ||
)?; | ||
|
||
// Turn audio processing on | ||
pd.activate_audio(true)?; | ||
|
||
// Run the stream | ||
output_stream.play()?; | ||
|
||
// This program does not terminate. | ||
// You would need to explicitly quit it. | ||
loop { | ||
// We sample in 2 hz. | ||
std::thread::sleep(std::time::Duration::from_millis(500)); | ||
|
||
// Read the average load of the cpu. | ||
let load = loadavg()?; | ||
|
||
let one_minute_cpu_load_average = load.one; | ||
let five_minutes_cpu_load_average = load.five; | ||
|
||
// Lists are one of the types we can send to pd. | ||
// Although pd allows for heterogeneous lists, | ||
// even if we're not using them heterogeneously in this example, | ||
// we still need to send it as a list of Atoms. | ||
|
||
// Atom is an encapsulating type in pd to unify | ||
// various types of data together under the same umbrella. | ||
// Check out `libpd_rs::types` module for more details. | ||
|
||
// Atoms have From trait implemented for them for | ||
// floats and strings. | ||
send_list_to( | ||
"cpu_load", | ||
&[ | ||
one_minute_cpu_load_average.into(), | ||
five_minutes_cpu_load_average.into(), | ||
], | ||
)?; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.