flac & raw export
This commit is contained in:
parent
b0444e13d4
commit
bd9e239c77
4 changed files with 314 additions and 8 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -34,6 +34,7 @@ out/
|
||||||
*.mp3
|
*.mp3
|
||||||
*.raw
|
*.raw
|
||||||
*.wav
|
*.wav
|
||||||
|
*.flac
|
||||||
|
|
||||||
# VSCode
|
# VSCode
|
||||||
.vscode/settings.json
|
.vscode/settings.json
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "bliplib"
|
name = "bliplib"
|
||||||
version = "0.2.2"
|
version = "0.2.3"
|
||||||
edition = "2024"
|
edition = "2024"
|
||||||
authors = ["Breval Ferrari <breval.ferrari@fish.golf>"]
|
authors = ["Breval Ferrari <breval.ferrari@fish.golf>"]
|
||||||
description = "The Bizarre Language for Intermodulation Programming (BLIP)"
|
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 }
|
dasp_sample = { version = "0", optional = true }
|
||||||
log = "0"
|
log = "0"
|
||||||
env_logger = { version = "0", optional = true }
|
env_logger = { version = "0", optional = true }
|
||||||
|
fon = "0.5"
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["all-formats"]
|
default = ["all-formats"]
|
||||||
|
|
|
@ -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>>>(
|
tag::<&'a str, LocatedSpan<&'a str>, nom::error::Error<LocatedSpan<&'a str>>>(
|
||||||
AudioFormatDiscriminants::Flac.into(),
|
AudioFormatDiscriminants::Flac.into(),
|
||||||
),
|
),
|
||||||
usize
|
usize.or(success(16)).map(|bps| AudioFormat::Flac { bps }),
|
||||||
.or(success(320000))
|
|
||||||
.map(|bps| AudioFormat::Flac { bps }),
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
fn parser<'a>() -> impl P<
|
fn parser<'a>() -> impl P<
|
||||||
|
@ -401,8 +399,11 @@ fn audio_format_parser(input: &str) -> Result<AudioFormat, anyhow::Error> {
|
||||||
flac::<'a>(),
|
flac::<'a>(),
|
||||||
rest.map_res(|r: LocatedSpan<&'a str>| {
|
rest.map_res(|r: LocatedSpan<&'a str>| {
|
||||||
Ok::<AudioFormat, nom::error::Error<LocatedSpan<&'a str>>>(AudioFormat::Raw(
|
Ok::<AudioFormat, nom::error::Error<LocatedSpan<&'a str>>>(AudioFormat::Raw(
|
||||||
RawAudioFormat::try_from(*r)
|
(*r == "raw")
|
||||||
.map_err(|_| nom::error::Error::new(r, ErrorKind::Verify))?,
|
.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)))?,
|
||||||
))
|
))
|
||||||
}),
|
}),
|
||||||
))
|
))
|
||||||
|
|
307
src/cli/main.rs
307
src/cli/main.rs
|
@ -1,5 +1,6 @@
|
||||||
//! See [the lib docs](https://docs.rs/bliplib)
|
//! See [the lib docs](https://docs.rs/bliplib)
|
||||||
|
|
||||||
|
#[macro_use]
|
||||||
mod cli;
|
mod cli;
|
||||||
use std::{
|
use std::{
|
||||||
borrow::Cow,
|
borrow::Cow,
|
||||||
|
@ -16,7 +17,8 @@ use bliplib::{
|
||||||
};
|
};
|
||||||
use clap::Parser as _;
|
use clap::Parser as _;
|
||||||
use cli::Cli;
|
use cli::Cli;
|
||||||
use dasp_sample::Sample;
|
use dasp_sample::{I24, Sample};
|
||||||
|
use flacenc::{component::BitRepr, error::Verify};
|
||||||
use hound::{SampleFormat, WavSpec, WavWriter};
|
use hound::{SampleFormat, WavSpec, WavWriter};
|
||||||
use log::{debug, error, info, warn};
|
use log::{debug, error, info, warn};
|
||||||
use mp3lame_encoder::{Builder as Mp3EncoderBuilder, FlushNoGap, MonoPcm};
|
use mp3lame_encoder::{Builder as Mp3EncoderBuilder, FlushNoGap, MonoPcm};
|
||||||
|
@ -134,6 +136,45 @@ fn main() -> anyhow::Result<()> {
|
||||||
info!("writing samples to output");
|
info!("writing samples to output");
|
||||||
writer.write_all(buff.get_ref())?;
|
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 } => {
|
Mp3 { bitrate, quality } => {
|
||||||
let buff = {
|
let buff = {
|
||||||
let mut encoder = Mp3EncoderBuilder::new()
|
let mut encoder = Mp3EncoderBuilder::new()
|
||||||
|
@ -181,7 +222,269 @@ fn main() -> anyhow::Result<()> {
|
||||||
info!("writing samples to output");
|
info!("writing samples to output");
|
||||||
writer.write_all(&buff)?;
|
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!(
|
Memo(MemoKind::Syntax(s)) => println!(
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue