From 29181f9c767aea51b24aee585d9d329f39d995cf Mon Sep 17 00:00:00 2001 From: Nikolai Rodionov Date: Tue, 23 Dec 2025 19:26:36 +0100 Subject: [PATCH] Update plugins --- Cargo.lock | 33 +++++++++++++++ Cargo.toml | 4 +- src/main.rs | 116 +++++++++++++++++++++++----------------------------- 3 files changed, 87 insertions(+), 66 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 685b856..1c6c1be 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -242,6 +242,12 @@ dependencies = [ "windows", ] +[[package]] +name = "crossbeam-utils" +version = "0.8.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" + [[package]] name = "dasp_sample" version = "0.11.0" @@ -694,6 +700,21 @@ version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" +[[package]] +name = "portable-atomic" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f59e70c4aef1e55797c2e8fd94a4f2a973fc972cfde0e0b05f683667b0cd39dd" + +[[package]] +name = "portable-atomic-util" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8a2f0d8d040d7848a709caf78912debcc3f33ee4b3cac47d73d1e1069e83507" +dependencies = [ + "portable-atomic", +] + [[package]] name = "proc-macro-crate" version = "3.4.0" @@ -737,6 +758,17 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "ringbuf" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe47b720588c8702e34b5979cb3271a8b1842c7cb6f57408efa70c779363488c" +dependencies = [ + "crossbeam-utils", + "portable-atomic", + "portable-atomic-util", +] + [[package]] name = "rustversion" version = "1.0.22" @@ -951,6 +983,7 @@ dependencies = [ "cpal", "jack", "pulseaudio", + "ringbuf", "symphonia", ] diff --git a/Cargo.toml b/Cargo.toml index ef22a4c..b4fff4f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,15 +5,15 @@ edition = "2024" [dependencies] clap = { version = "4.5.53", features = ["derive"] } -jack = { version = "0.13.3", optional = true} +jack = { version = "0.13.3" } pulseaudio = { version = "0.3.1", optional = true} alsa = { version = "0.10.0", optional = true} 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" [features] -jack = ["dep:jack"] pulseaudio = ["dep:pulseaudio"] alsa = ["dep:alsa"] coreaudio = ["dep:coreaudio-rs"] diff --git a/src/main.rs b/src/main.rs index d71bf20..ac3a3ff 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,7 +1,9 @@ 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 { @@ -24,90 +26,76 @@ struct Cli { fn main() { let cli = Cli::parse(); - // You can check the value provided by positional arguments, or option arguments - // Read the track from the tracks into the buffer - let mut data: Vec; - if let Some(files) = cli.track { - for file in files { - data = read_audio_file(file); - } - } + 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 => play_with_jack(), + Backend::Jack => println!("jack"), Backend::Alsa => println!("alsa"), Backend::Pulseaudio => println!("pulse"), Backend::Coreaudio => println!("coreaudio"), Backend::Cpal => println!("cpal"), } } + } -#[cfg(feature = "jack")] -fn play_with_jack(data: Vec) { - unimplemented!() -} +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()); -#[cfg(not(feature = "jack"))] -fn play_with_jack(_: Vec) { - unimplemented!() -} + let mut hint = Hint::new(); + hint.with_extension("mp3"); -fn read_audio_file(path: String) -> Vec{ - // Open file - let file = File::open(path).expect("Failed to open file"); - let mss = MediaSourceStream::new(Box::new(file), Default::default()); + let probed = symphonia::default::get_probe() + .format( + &hint, + mss, + &FormatOptions::default(), + &MetadataOptions::default(), + ) + .expect("format probe failed"); - // Hint format by extension - let mut hint = Hint::new(); + let mut format = probed.format; + let track = format.default_track().unwrap(); - // Probe the file - let probed = symphonia::default::get_probe() - .format(&hint, mss, &FormatOptions::default(), &MetadataOptions::default()) - .expect("Unsupported format"); + let mut decoder = symphonia::default::get_codecs() + .make(&track.codec_params, &DecoderOptions::default()) + .unwrap(); - let mut format = probed.format; - let track = format.default_track().expect("No default track"); + let mut sample_buf: Option> = None; - let mut decoder = symphonia::default::get_codecs() - .make(&track.codec_params, &DecoderOptions::default()) - .expect("Failed to create decoder"); + loop { + let packet = match format.next_packet() { + Ok(p) => p, + Err(_) => break, + }; - let mut samples = Vec::new(); + let decoded = decoder.decode(&packet).unwrap(); - // Temporary sample buffer for decoding - let mut sample_buf = None; + let buf = sample_buf.get_or_insert_with(|| { + SampleBuffer::new(decoded.capacity() as u64, *decoded.spec()) + }); - while let Ok(packet) = format.next_packet() { - if packet.track_id() != track.id { - continue; - } + buf.copy_interleaved_ref(decoded); - // Decode packet - match decoder.decode(&packet) { - Ok(audio_buf) => { - // Create a f32 sample buffer if not already - if sample_buf.is_none() { - let spec = *audio_buf.spec(); - let capacity = audio_buf.capacity() as u64; - sample_buf = Some(SampleBuffer::::new(capacity, spec)); - } - - // Copy decoded data to f32 buffer - if let Some(buf) = &mut sample_buf { - buf.copy_interleaved_ref(audio_buf); - samples.extend(buf.samples()); - } + // Assume stereo + for frame in buf.samples().chunks(2) { + let _ = prod_l.push(frame[0]); + let _ = prod_r.push(frame[1]); } - Err(_) => continue, } - } - - samples -} - -pub struct DecodedAudio { - pub samples: Vec, // interleaved - pub sample_rate: u32, - pub channels: u16, + }); }