Trying to get into jack
This commit is contained in:
145
src/main.rs
145
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<String>,
|
||||
#[arg(short, long, value_enum)]
|
||||
backend: Option<Backend>,
|
||||
#[arg(short, long)]
|
||||
track: Option<Vec<String>>,
|
||||
}
|
||||
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::<f32>::new(48000 * 2);
|
||||
let ring_r = RingBuffer::<f32>::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::<f32>::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<f32>,
|
||||
mut prod_r: ringbuf::Producer<f32>,
|
||||
) {
|
||||
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<SampleBuffer<f32>> = 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<f32> {
|
||||
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,
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user