From 535727268d70cdceb912e69b73dbe15a884add34 Mon Sep 17 00:00:00 2001 From: Nikolai Rodionov Date: Wed, 24 Dec 2025 13:30:15 +0100 Subject: [PATCH] Trying to get into jack --- Cargo.lock | 52 +++++++++++++++++++ Cargo.toml | 2 + src/main.rs | 145 +++++++++++++++++++--------------------------------- 3 files changed, 107 insertions(+), 92 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 1c6c1be..14b20a9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -242,6 +242,56 @@ dependencies = [ "windows", ] +[[package]] +name = "crossbeam" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1137cd7e7fc0fb5d3c5a8678be38ec56e819125d8d7907411fe24ccb943faca8" +dependencies = [ + "crossbeam-channel", + "crossbeam-deque", + "crossbeam-epoch", + "crossbeam-queue", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-channel" +version = "0.5.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82b8f8f868b36967f9606790d1903570de9ceaf870a7bf9fbbd3016d636a2cb2" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-deque" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51" +dependencies = [ + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-queue" +version = "0.3.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f58bbc28f91df819d0aa2a2c00cd19754769c2fad90579b3592b1c9ba7a3115" +dependencies = [ + "crossbeam-utils", +] + [[package]] name = "crossbeam-utils" version = "0.8.21" @@ -981,6 +1031,8 @@ dependencies = [ "clap", "coreaudio-rs", "cpal", + "crossbeam", + "crossbeam-channel", "jack", "pulseaudio", "ringbuf", diff --git a/Cargo.toml b/Cargo.toml index b4fff4f..2784adf 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,6 +12,8 @@ symphonia = { version = "0.5.5", features = ["mp3"] } coreaudio-rs = { version = "0.13.0", optional = true } cpal = { version = "0.16.0", optional = true } ringbuf = "0.4.8" +crossbeam = { version = "0.8.4", features = ["crossbeam-channel"] } +crossbeam-channel = "0.5.15" [features] pulseaudio = ["dep:pulseaudio"] diff --git a/src/main.rs b/src/main.rs index ac3a3ff..8d15c3f 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,101 +1,62 @@ -use std::{fs::File, path::PathBuf}; - -use clap::{Parser, Subcommand, ValueEnum}; -use jack::{AudioOut, Client, ClientOptions}; -use symphonia::core::{audio::{AudioBufferRef, SampleBuffer}, codecs::DecoderOptions, formats::FormatOptions, io::MediaSourceStream, meta::MetadataOptions, probe::Hint}; -use ringbuf::RingBuffer; - -#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, ValueEnum)] -enum Backend { - Jack, - Alsa, - Pulseaudio, - Coreaudio, - Cpal, -} -#[derive(Parser)] -#[command(version, about, long_about = None)] -struct Cli { - name: Option, - #[arg(short, long, value_enum)] - backend: Option, - #[arg(short, long)] - track: Option>, -} +use std::{io, str::FromStr}; +use ringbuf::{ + traits::{Consumer, Producer, Split}, + HeapRb, +}; fn main() { - let cli = Cli::parse(); - let (client, _status) = Client::new("rust_jack_simple", ClientOptions::default()).unwrap(); - let out_l = client.register_port("out_l", AudioOut::default()).unwrap(); - let out_r = client.register_port("out_r", AudioOut::default()).unwrap(); - let ring_l = RingBuffer::::new(48000 * 2); - let ring_r = RingBuffer::::new(48000 * 2); - let (prod_l, cons_l) = ring_l.split(); - let (prod_r, cons_r) = ring_r.split(); - - - if let Some(backend) = cli.backend { - match backend { - Backend::Jack => println!("jack"), - Backend::Alsa => println!("alsa"), - Backend::Pulseaudio => println!("pulse"), - Backend::Coreaudio => println!("coreaudio"), - Backend::Cpal => println!("cpal"), + /* + * Start the JACK backend and make it ready to play sounds + * + */ + let (client, _status) = + jack::Client::new("rust_jack_sine", jack::ClientOptions::default()).unwrap(); + let mut out_a = client + .register_port("sine_out", jack::AudioOut::default()) + .unwrap(); + let rb = HeapRb::::new(128); + let (mut prod, mut cons) = rb.split(); + let process_callback = move |_: &jack::Client, ps: &jack::ProcessScope| -> jack::Control { + let mut out_a_p = out_a.as_mut_slice(ps); + for v in cons.iter_mut() { + println!("{:?}", v); + out_a_p.fill(*v); } + jack::Control::Continue + }; + let process = jack::contrib::ClosureProcessHandler::new(process_callback); + + let active_client = client.activate_async((), process).unwrap(); + + active_client + .as_client() + .connect_ports_by_name("rust_jack_sine:sine_out", "system:playback_1") + .unwrap(); + active_client + .as_client() + .connect_ports_by_name("rust_jack_sine:sine_out", "system:playback_2") + .unwrap(); + // processing starts here + + // 5. wait or do some processing while your handler is running in real time. + println!("Enter an integer value to change the frequency of the sine wave."); + while let Some(f) = read_freq() { + prod.try_push(f); } + // 6. Optional deactivate. Not required since active_client will deactivate on + // drop, though explicit deactivate may help you identify errors in + // deactivate. + if let Err(err) = active_client.deactivate() { + eprintln!("JACK exited with error: {err}"); + }; } -fn spawn_mp3_decoder( - path: String, - mut prod_l: ringbuf::Producer, - mut prod_r: ringbuf::Producer, -) { - thread::spawn(move || { - let file = File::open(path).expect("failed to open mp3"); - let mss = MediaSourceStream::new(Box::new(file), Default::default()); - - let mut hint = Hint::new(); - hint.with_extension("mp3"); - - let probed = symphonia::default::get_probe() - .format( - &hint, - mss, - &FormatOptions::default(), - &MetadataOptions::default(), - ) - .expect("format probe failed"); - - let mut format = probed.format; - let track = format.default_track().unwrap(); - - let mut decoder = symphonia::default::get_codecs() - .make(&track.codec_params, &DecoderOptions::default()) - .unwrap(); - - let mut sample_buf: Option> = None; - - loop { - let packet = match format.next_packet() { - Ok(p) => p, - Err(_) => break, - }; - - let decoded = decoder.decode(&packet).unwrap(); - - let buf = sample_buf.get_or_insert_with(|| { - SampleBuffer::new(decoded.capacity() as u64, *decoded.spec()) - }); - - buf.copy_interleaved_ref(decoded); - - // Assume stereo - for frame in buf.samples().chunks(2) { - let _ = prod_l.push(frame[0]); - let _ = prod_r.push(frame[1]); - } - } - }); +fn read_freq() -> Option { + let mut user_input = String::new(); + match io::stdin().read_line(&mut user_input) { + Ok(_) => u16::from_str(user_input.trim()).ok().map(|n| n as f32), + Err(_) => None, + } }