From bd9e239c773daab255f2e7a6f9b095944c1f5a08 Mon Sep 17 00:00:00 2001 From: brevalferrari Date: Tue, 10 Jun 2025 16:12:19 +0200 Subject: [PATCH] flac & raw export --- .gitignore | 1 + Cargo.toml | 3 +- src/cli/cli.rs | 11 +- src/cli/main.rs | 307 +++++++++++++++++++++++++++++++++++++++++++++++- 4 files changed, 314 insertions(+), 8 deletions(-) diff --git a/.gitignore b/.gitignore index bec1c58..d0deb17 100644 --- a/.gitignore +++ b/.gitignore @@ -34,6 +34,7 @@ out/ *.mp3 *.raw *.wav +*.flac # VSCode .vscode/settings.json diff --git a/Cargo.toml b/Cargo.toml index 2ae9e8e..0ba44b6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "bliplib" -version = "0.2.2" +version = "0.2.3" edition = "2024" authors = ["Breval Ferrari "] description = "The Bizarre Language for Intermodulation Programming (BLIP)" @@ -33,6 +33,7 @@ rodio = { version = "0.20", default-features = false, optional = true } dasp_sample = { version = "0", optional = true } log = "0" env_logger = { version = "0", optional = true } +fon = "0.5" [features] default = ["all-formats"] diff --git a/src/cli/cli.rs b/src/cli/cli.rs index 07ae387..42f7816 100644 --- a/src/cli/cli.rs +++ b/src/cli/cli.rs @@ -385,9 +385,7 @@ fn audio_format_parser(input: &str) -> Result { tag::<&'a str, LocatedSpan<&'a str>, nom::error::Error>>( AudioFormatDiscriminants::Flac.into(), ), - usize - .or(success(320000)) - .map(|bps| AudioFormat::Flac { bps }), + usize.or(success(16)).map(|bps| AudioFormat::Flac { bps }), ) } fn parser<'a>() -> impl P< @@ -401,8 +399,11 @@ fn audio_format_parser(input: &str) -> Result { flac::<'a>(), rest.map_res(|r: LocatedSpan<&'a str>| { Ok::>>(AudioFormat::Raw( - RawAudioFormat::try_from(*r) - .map_err(|_| nom::error::Error::new(r, ErrorKind::Verify))?, + (*r == "raw") + .then_some(Default::default()) + .ok_or(nom::error::Error::new(r, ErrorKind::Verify)) + .or(RawAudioFormat::try_from(*r) + .map_err(|_| nom::error::Error::new(r, ErrorKind::Verify)))?, )) }), )) diff --git a/src/cli/main.rs b/src/cli/main.rs index cb95932..9127291 100644 --- a/src/cli/main.rs +++ b/src/cli/main.rs @@ -1,5 +1,6 @@ //! See [the lib docs](https://docs.rs/bliplib) +#[macro_use] mod cli; use std::{ borrow::Cow, @@ -16,7 +17,8 @@ use bliplib::{ }; use clap::Parser as _; use cli::Cli; -use dasp_sample::Sample; +use dasp_sample::{I24, Sample}; +use flacenc::{component::BitRepr, error::Verify}; use hound::{SampleFormat, WavSpec, WavWriter}; use log::{debug, error, info, warn}; use mp3lame_encoder::{Builder as Mp3EncoderBuilder, FlushNoGap, MonoPcm}; @@ -134,6 +136,45 @@ fn main() -> anyhow::Result<()> { info!("writing samples to output"); writer.write_all(buff.get_ref())?; } + Flac { bps } => { + let config = flacenc::config::Encoder::default() + .into_verified() + .expect("Config data error."); + let source = flacenc::source::MemSource::from_samples( + samples + .into_iter() + .map(|sample| match bps { + 8 => sample.to_sample::() as i32, + 16 => sample.to_sample::() as i32, + 24 => sample.to_sample::().inner(), + _=> unimplemented!("sorry, the current implementation for the flac encoder doesn't support any other bitrate than 8, 16 or 24.") + }) + .collect::>() + .as_slice(), + 1, + bps, + SAMPLE_RATE.into(), + ); + let flac_stream = + flacenc::encode_with_fixed_block_size(&config, source, config.block_size) + .expect("Encode failed."); + + // `Stream` imlpements `BitRepr` so you can obtain the encoded stream via + // `ByteSink` struct that implements `BitSink`. + let mut sink = flacenc::bitsink::ByteSink::new(); + flac_stream + .write(&mut sink) + .context("Failed to write samples to FLAC byte sink")?; + + let mut writer: Box = output + .map(File::from) + .map(Box::new) + .map(|b| b as Box) + .unwrap_or(Box::new(stdout())); + writer + .write_all(sink.as_slice()) + .context("Failed to write samples to output")?; + } Mp3 { bitrate, quality } => { let buff = { let mut encoder = Mp3EncoderBuilder::new() @@ -181,7 +222,269 @@ fn main() -> anyhow::Result<()> { info!("writing samples to output"); writer.write_all(&buff)?; } - _ => todo!(), + // I don't know how to easily solve this copy paste with macros because of the variant name usage + Raw(format) => match format { + RawAudioFormat::ALaw => raw_audio::Encoder::new( + output + .map(File::from) + .map(Box::new) + .map(|b| b as Box) + .unwrap_or(Box::new(stdout())), + raw_audio::pcm::ALaw, + ) + .encode( + fon::Audio::::with_f64_buffer(SAMPLE_RATE, samples) + .drain(), + ) + .context("Failed to encode to raw audio")?, + RawAudioFormat::F32Be => raw_audio::Encoder::new( + output + .map(File::from) + .map(Box::new) + .map(|b| b as Box) + .unwrap_or(Box::new(stdout())), + raw_audio::pcm::F32Be, + ) + .encode( + fon::Audio::::with_f64_buffer(SAMPLE_RATE, samples) + .drain(), + ) + .context("Failed to encode to raw audio")?, + RawAudioFormat::F32Le => raw_audio::Encoder::new( + output + .map(File::from) + .map(Box::new) + .map(|b| b as Box) + .unwrap_or(Box::new(stdout())), + raw_audio::pcm::F32Le, + ) + .encode( + fon::Audio::::with_f64_buffer(SAMPLE_RATE, samples) + .drain(), + ) + .context("Failed to encode to raw audio")?, + RawAudioFormat::F64Be => raw_audio::Encoder::new( + output + .map(File::from) + .map(Box::new) + .map(|b| b as Box) + .unwrap_or(Box::new(stdout())), + raw_audio::pcm::F64Be, + ) + .encode( + fon::Audio::::with_f64_buffer(SAMPLE_RATE, samples) + .drain(), + ) + .context("Failed to encode to raw audio")?, + RawAudioFormat::F64Le => raw_audio::Encoder::new( + output + .map(File::from) + .map(Box::new) + .map(|b| b as Box) + .unwrap_or(Box::new(stdout())), + raw_audio::pcm::F64Le, + ) + .encode( + fon::Audio::::with_f64_buffer(SAMPLE_RATE, samples) + .drain(), + ) + .context("Failed to encode to raw audio")?, + RawAudioFormat::MuLaw => raw_audio::Encoder::new( + output + .map(File::from) + .map(Box::new) + .map(|b| b as Box) + .unwrap_or(Box::new(stdout())), + raw_audio::pcm::MuLaw, + ) + .encode( + fon::Audio::::with_f64_buffer(SAMPLE_RATE, samples) + .drain(), + ) + .context("Failed to encode to raw audio")?, + RawAudioFormat::S8 => raw_audio::Encoder::new( + output + .map(File::from) + .map(Box::new) + .map(|b| b as Box) + .unwrap_or(Box::new(stdout())), + raw_audio::pcm::S8, + ) + .encode( + fon::Audio::::with_f64_buffer(SAMPLE_RATE, samples) + .drain(), + ) + .context("Failed to encode to raw audio")?, + RawAudioFormat::S16Be => raw_audio::Encoder::new( + output + .map(File::from) + .map(Box::new) + .map(|b| b as Box) + .unwrap_or(Box::new(stdout())), + raw_audio::pcm::S16Be, + ) + .encode( + fon::Audio::::with_f64_buffer(SAMPLE_RATE, samples) + .drain(), + ) + .context("Failed to encode to raw audio")?, + RawAudioFormat::S16Le => raw_audio::Encoder::new( + output + .map(File::from) + .map(Box::new) + .map(|b| b as Box) + .unwrap_or(Box::new(stdout())), + raw_audio::pcm::S16Le, + ) + .encode( + fon::Audio::::with_f64_buffer(SAMPLE_RATE, samples) + .drain(), + ) + .context("Failed to encode to raw audio")?, + RawAudioFormat::S24Be => raw_audio::Encoder::new( + output + .map(File::from) + .map(Box::new) + .map(|b| b as Box) + .unwrap_or(Box::new(stdout())), + raw_audio::pcm::S24Be, + ) + .encode( + fon::Audio::::with_f64_buffer(SAMPLE_RATE, samples) + .drain(), + ) + .context("Failed to encode to raw audio")?, + RawAudioFormat::S24Le => raw_audio::Encoder::new( + output + .map(File::from) + .map(Box::new) + .map(|b| b as Box) + .unwrap_or(Box::new(stdout())), + raw_audio::pcm::S24Le, + ) + .encode( + fon::Audio::::with_f64_buffer(SAMPLE_RATE, samples) + .drain(), + ) + .context("Failed to encode to raw audio")?, + RawAudioFormat::S32Be => raw_audio::Encoder::new( + output + .map(File::from) + .map(Box::new) + .map(|b| b as Box) + .unwrap_or(Box::new(stdout())), + raw_audio::pcm::S32Be, + ) + .encode( + fon::Audio::::with_f64_buffer(SAMPLE_RATE, samples) + .drain(), + ) + .context("Failed to encode to raw audio")?, + RawAudioFormat::S32Le => raw_audio::Encoder::new( + output + .map(File::from) + .map(Box::new) + .map(|b| b as Box) + .unwrap_or(Box::new(stdout())), + raw_audio::pcm::S32Le, + ) + .encode( + fon::Audio::::with_f64_buffer(SAMPLE_RATE, samples) + .drain(), + ) + .context("Failed to encode to raw audio")?, + RawAudioFormat::U8 => raw_audio::Encoder::new( + output + .map(File::from) + .map(Box::new) + .map(|b| b as Box) + .unwrap_or(Box::new(stdout())), + raw_audio::pcm::U8, + ) + .encode( + fon::Audio::::with_f64_buffer(SAMPLE_RATE, samples) + .drain(), + ) + .context("Failed to encode to raw audio")?, + RawAudioFormat::U16Be => raw_audio::Encoder::new( + output + .map(File::from) + .map(Box::new) + .map(|b| b as Box) + .unwrap_or(Box::new(stdout())), + raw_audio::pcm::U16Be, + ) + .encode( + fon::Audio::::with_f64_buffer(SAMPLE_RATE, samples) + .drain(), + ) + .context("Failed to encode to raw audio")?, + RawAudioFormat::U16Le => raw_audio::Encoder::new( + output + .map(File::from) + .map(Box::new) + .map(|b| b as Box) + .unwrap_or(Box::new(stdout())), + raw_audio::pcm::U16Le, + ) + .encode( + fon::Audio::::with_f64_buffer(SAMPLE_RATE, samples) + .drain(), + ) + .context("Failed to encode to raw audio")?, + RawAudioFormat::U24Be => raw_audio::Encoder::new( + output + .map(File::from) + .map(Box::new) + .map(|b| b as Box) + .unwrap_or(Box::new(stdout())), + raw_audio::pcm::U24Be, + ) + .encode( + fon::Audio::::with_f64_buffer(SAMPLE_RATE, samples) + .drain(), + ) + .context("Failed to encode to raw audio")?, + RawAudioFormat::U24Le => raw_audio::Encoder::new( + output + .map(File::from) + .map(Box::new) + .map(|b| b as Box) + .unwrap_or(Box::new(stdout())), + raw_audio::pcm::U24Le, + ) + .encode( + fon::Audio::::with_f64_buffer(SAMPLE_RATE, samples) + .drain(), + ) + .context("Failed to encode to raw audio")?, + RawAudioFormat::U32Be => raw_audio::Encoder::new( + output + .map(File::from) + .map(Box::new) + .map(|b| b as Box) + .unwrap_or(Box::new(stdout())), + raw_audio::pcm::U32Be, + ) + .encode( + fon::Audio::::with_f64_buffer(SAMPLE_RATE, samples) + .drain(), + ) + .context("Failed to encode to raw audio")?, + RawAudioFormat::U32Le => raw_audio::Encoder::new( + output + .map(File::from) + .map(Box::new) + .map(|b| b as Box) + .unwrap_or(Box::new(stdout())), + raw_audio::pcm::U32Le, + ) + .encode( + fon::Audio::::with_f64_buffer(SAMPLE_RATE, samples) + .drain(), + ) + .context("Failed to encode to raw audio")?, + }, } } Memo(MemoKind::Syntax(s)) => println!(