flac & raw export

This commit is contained in:
brevalferrari 2025-06-10 16:12:19 +02:00
parent b0444e13d4
commit bd9e239c77
4 changed files with 314 additions and 8 deletions

1
.gitignore vendored
View file

@ -34,6 +34,7 @@ out/
*.mp3
*.raw
*.wav
*.flac
# VSCode
.vscode/settings.json

View file

@ -1,6 +1,6 @@
[package]
name = "bliplib"
version = "0.2.2"
version = "0.2.3"
edition = "2024"
authors = ["Breval Ferrari <breval.ferrari@fish.golf>"]
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"]

View file

@ -385,9 +385,7 @@ fn audio_format_parser(input: &str) -> Result<AudioFormat, anyhow::Error> {
tag::<&'a str, LocatedSpan<&'a str>, nom::error::Error<LocatedSpan<&'a str>>>(
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<AudioFormat, anyhow::Error> {
flac::<'a>(),
rest.map_res(|r: LocatedSpan<&'a str>| {
Ok::<AudioFormat, nom::error::Error<LocatedSpan<&'a str>>>(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)))?,
))
}),
))

View file

@ -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::<i8>() as i32,
16 => sample.to_sample::<i16>() as i32,
24 => sample.to_sample::<I24>().inner(),
_=> unimplemented!("sorry, the current implementation for the flac encoder doesn't support any other bitrate than 8, 16 or 24.")
})
.collect::<Vec<i32>>()
.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<dyn Write> = output
.map(File::from)
.map(Box::new)
.map(|b| b as Box<dyn Write>)
.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<dyn Write>)
.unwrap_or(Box::new(stdout())),
raw_audio::pcm::ALaw,
)
.encode(
fon::Audio::<fon::mono::Mono64>::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<dyn Write>)
.unwrap_or(Box::new(stdout())),
raw_audio::pcm::F32Be,
)
.encode(
fon::Audio::<fon::mono::Mono64>::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<dyn Write>)
.unwrap_or(Box::new(stdout())),
raw_audio::pcm::F32Le,
)
.encode(
fon::Audio::<fon::mono::Mono64>::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<dyn Write>)
.unwrap_or(Box::new(stdout())),
raw_audio::pcm::F64Be,
)
.encode(
fon::Audio::<fon::mono::Mono64>::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<dyn Write>)
.unwrap_or(Box::new(stdout())),
raw_audio::pcm::F64Le,
)
.encode(
fon::Audio::<fon::mono::Mono64>::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<dyn Write>)
.unwrap_or(Box::new(stdout())),
raw_audio::pcm::MuLaw,
)
.encode(
fon::Audio::<fon::mono::Mono64>::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<dyn Write>)
.unwrap_or(Box::new(stdout())),
raw_audio::pcm::S8,
)
.encode(
fon::Audio::<fon::mono::Mono64>::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<dyn Write>)
.unwrap_or(Box::new(stdout())),
raw_audio::pcm::S16Be,
)
.encode(
fon::Audio::<fon::mono::Mono64>::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<dyn Write>)
.unwrap_or(Box::new(stdout())),
raw_audio::pcm::S16Le,
)
.encode(
fon::Audio::<fon::mono::Mono64>::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<dyn Write>)
.unwrap_or(Box::new(stdout())),
raw_audio::pcm::S24Be,
)
.encode(
fon::Audio::<fon::mono::Mono64>::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<dyn Write>)
.unwrap_or(Box::new(stdout())),
raw_audio::pcm::S24Le,
)
.encode(
fon::Audio::<fon::mono::Mono64>::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<dyn Write>)
.unwrap_or(Box::new(stdout())),
raw_audio::pcm::S32Be,
)
.encode(
fon::Audio::<fon::mono::Mono64>::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<dyn Write>)
.unwrap_or(Box::new(stdout())),
raw_audio::pcm::S32Le,
)
.encode(
fon::Audio::<fon::mono::Mono64>::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<dyn Write>)
.unwrap_or(Box::new(stdout())),
raw_audio::pcm::U8,
)
.encode(
fon::Audio::<fon::mono::Mono64>::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<dyn Write>)
.unwrap_or(Box::new(stdout())),
raw_audio::pcm::U16Be,
)
.encode(
fon::Audio::<fon::mono::Mono64>::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<dyn Write>)
.unwrap_or(Box::new(stdout())),
raw_audio::pcm::U16Le,
)
.encode(
fon::Audio::<fon::mono::Mono64>::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<dyn Write>)
.unwrap_or(Box::new(stdout())),
raw_audio::pcm::U24Be,
)
.encode(
fon::Audio::<fon::mono::Mono64>::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<dyn Write>)
.unwrap_or(Box::new(stdout())),
raw_audio::pcm::U24Le,
)
.encode(
fon::Audio::<fon::mono::Mono64>::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<dyn Write>)
.unwrap_or(Box::new(stdout())),
raw_audio::pcm::U32Be,
)
.encode(
fon::Audio::<fon::mono::Mono64>::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<dyn Write>)
.unwrap_or(Box::new(stdout())),
raw_audio::pcm::U32Le,
)
.encode(
fon::Audio::<fon::mono::Mono64>::with_f64_buffer(SAMPLE_RATE, samples)
.drain(),
)
.context("Failed to encode to raw audio")?,
},
}
}
Memo(MemoKind::Syntax(s)) => println!(