Compare commits
2 Commits
main
...
monolith-a
| Author | SHA1 | Date | |
|---|---|---|---|
|
6bdb38bcee
|
|||
|
|
49e4f05b5e |
1308
Cargo.lock
generated
1308
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
14
Cargo.toml
14
Cargo.toml
@@ -4,3 +4,17 @@ version = "0.1.0"
|
||||
edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
clap = { version = "4.5.53", features = ["derive"] }
|
||||
jack = { version = "0.13.3", optional = true}
|
||||
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 }
|
||||
|
||||
[features]
|
||||
jack = ["dep:jack"]
|
||||
pulseaudio = ["dep:pulseaudio"]
|
||||
alsa = ["dep:alsa"]
|
||||
coreaudio = ["dep:coreaudio-rs"]
|
||||
cpal = ["dep:cpal"]
|
||||
|
||||
123
src/main.rs
123
src/main.rs
@@ -1,3 +1,122 @@
|
||||
fn main() {
|
||||
println!("Hello, world!");
|
||||
use std::{fs::File, path::PathBuf};
|
||||
|
||||
use clap::{Parser, Subcommand, ValueEnum};
|
||||
use symphonia::core::{audio::SampleBuffer, codecs::DecoderOptions, formats::FormatOptions, io::MediaSourceStream, meta::MetadataOptions, probe::Hint};
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, ValueEnum)]
|
||||
enum Backend {
|
||||
Jack,
|
||||
Alsa,
|
||||
Pulseaudio,
|
||||
Coreaudio,
|
||||
}
|
||||
#[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>>,
|
||||
}
|
||||
|
||||
|
||||
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
|
||||
if let Some(files) = cli.track {
|
||||
for file in files {
|
||||
read_audio_file(file);
|
||||
}
|
||||
}
|
||||
if let Some(backend) = cli.backend {
|
||||
match backend {
|
||||
Backend::Jack => println!("jack"),
|
||||
Backend::Alsa => println!("alsa"),
|
||||
Backend::Pulseaudio => println!("pulse"),
|
||||
Backend::Coreaudio => println!("coreaudio"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn read_audio_file(file: String) {
|
||||
let file = Box::new(File::open(file).unwrap());
|
||||
let mss = MediaSourceStream::new(file, Default::default());
|
||||
let mut hint = Hint::new();
|
||||
hint.with_extension("mp3");
|
||||
let format_opts: FormatOptions = Default::default();
|
||||
let metadata_opts: MetadataOptions = Default::default();
|
||||
let decoder_opts: DecoderOptions = Default::default();
|
||||
let probed =
|
||||
symphonia::default::get_probe().format(&hint, mss, &format_opts, &metadata_opts).unwrap();
|
||||
|
||||
// Get the format reader yielded by the probe operation.
|
||||
let mut format = probed.format;
|
||||
|
||||
// Get the default track.
|
||||
let track = format.default_track().unwrap();
|
||||
|
||||
// Create a decoder for the track.
|
||||
let mut decoder =
|
||||
symphonia::default::get_codecs().make(&track.codec_params, &decoder_opts).unwrap();
|
||||
|
||||
// Store the track identifier, we'll use it to filter packets.
|
||||
let track_id = track.id;
|
||||
|
||||
let mut sample_count = 0;
|
||||
let mut sample_buf = None;
|
||||
|
||||
loop {
|
||||
// Get the next packet from the format reader.
|
||||
let packet = format.next_packet().unwrap();
|
||||
|
||||
// If the packet does not belong to the selected track, skip it.
|
||||
if packet.track_id() != track_id {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Decode the packet into audio samples, ignoring any decode errors.
|
||||
match decoder.decode(&packet) {
|
||||
Ok(audio_buf) => {
|
||||
// The decoded audio samples may now be accessed via the audio buffer if per-channel
|
||||
// slices of samples in their native decoded format is desired. Use-cases where
|
||||
// the samples need to be accessed in an interleaved order or converted into
|
||||
// another sample format, or a byte buffer is required, are covered by copying the
|
||||
// audio buffer into a sample buffer or raw sample buffer, respectively. In the
|
||||
// example below, we will copy the audio buffer into a sample buffer in an
|
||||
// interleaved order while also converting to a f32 sample format.
|
||||
|
||||
// If this is the *first* decoded packet, create a sample buffer matching the
|
||||
// decoded audio buffer format.
|
||||
if sample_buf.is_none() {
|
||||
// Get the audio buffer specification.
|
||||
let spec = *audio_buf.spec();
|
||||
|
||||
// Get the capacity of the decoded buffer. Note: This is capacity, not length!
|
||||
let duration = audio_buf.capacity() as u64;
|
||||
|
||||
// Create the f32 sample buffer.
|
||||
sample_buf = Some(SampleBuffer::<f32>::new(duration, spec));
|
||||
}
|
||||
|
||||
// Copy the decoded audio buffer into the sample buffer in an interleaved format.
|
||||
if let Some(buf) = &mut sample_buf {
|
||||
buf.copy_interleaved_ref(audio_buf);
|
||||
|
||||
// The samples may now be access via the `samples()` function.
|
||||
sample_count += buf.samples().len();
|
||||
print!("\rDecoded {} samples", sample_count);
|
||||
}
|
||||
}
|
||||
Err(symphonia::core::errors::Error::DecodeError(_)) => (),
|
||||
Err(_) => break,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct DecodedAudio {
|
||||
pub samples: Vec<i16>, // interleaved
|
||||
pub sample_rate: u32,
|
||||
pub channels: u16,
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user