export mode

This commit is contained in:
brevalferrari 2025-06-05 17:47:28 +02:00
parent da38edbfd1
commit c6cde8ffbf
3 changed files with 96 additions and 50 deletions

5
.gitignore vendored
View file

@ -29,3 +29,8 @@ out/
# Samply
*.json.gz
# audio files
*.mp3
*.raw
*.wav

View file

@ -268,6 +268,7 @@ pub(super) struct ExportOpts {
#[arg(short, long, value_parser = audio_format_parser)]
pub(super) format: AudioFormat,
/// Output file [default: stdout]
#[arg(short, long)]
pub(super) output: Option<ClonableFile<true>>,
}

View file

@ -1,5 +1,10 @@
mod cli;
use std::{collections::HashMap, io::read_to_string, iter::once};
use std::{
collections::HashMap,
fs::File,
io::{Cursor, Write, read_to_string, stdout},
iter::once,
};
use anyhow::{Context as _, anyhow};
use bliplib::{
@ -9,9 +14,12 @@ use bliplib::{
use clap::Parser as _;
use cli::Cli;
use dasp_sample::Sample;
use hound::{SampleFormat, WavSpec, WavWriter};
use log::{debug, warn};
use rodio::{OutputStream, Sink, buffer::SamplesBuffer};
use crate::cli::{ExportOpts, PlayOpts};
fn main() -> anyhow::Result<()> {
env_logger::init();
let cli = Cli::parse();
@ -25,6 +33,55 @@ fn main() -> anyhow::Result<()> {
let sink = Sink::try_new(&stream_handle).context("Epic audio playback failure")?;
debug!("audio sink acquired");
let samples: Vec<f32> = parse_and_compile(&opts)?
.into_iter()
.map(Sample::to_sample)
.collect();
debug!("result: {} samples", samples.len());
if samples.is_empty() {
warn!("0 samples generated");
}
debug!("appending samples to sink");
sink.append(SamplesBuffer::new(1, SAMPLE_RATE as u32, samples));
debug!("sleeping until end of sink");
sink.sleep_until_end();
}
Export(ExportOpts {
playopts,
format: _format,
output,
}) => {
let samples = parse_and_compile(&playopts)?;
let mut buff = Cursor::new(Vec::with_capacity(samples.len() * 8));
{
let mut writer = WavWriter::new(
&mut buff,
WavSpec {
channels: 1,
sample_rate: SAMPLE_RATE as u32,
bits_per_sample: 32,
sample_format: SampleFormat::Float,
},
)
.context("Failed to create WAV writer")?;
for sample in samples {
writer.write_sample(sample.to_sample::<f32>())?;
}
}
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(buff.get_ref())?;
}
Memo(_opts) => todo!(),
}
Ok(())
}
fn parse_and_compile(opts: &PlayOpts) -> anyhow::Result<Vec<f64>> {
let default_variables = [
('l', 4f64),
('L', 0.0),
@ -69,24 +126,7 @@ fn main() -> anyhow::Result<()> {
.chain(once(('L'.to_string(), opts.length().clone()))),
));
debug!("compiling to samples");
let samples: Vec<f32> = compiler
compiler
.compile_all(tokens)
.context("Failed to process input tokens")?
.into_iter()
.map(Sample::to_sample)
.collect();
debug!("result: {} samples", samples.len());
if samples.is_empty() {
warn!("0 samples generated");
}
debug!("appending samples to sink");
sink.append(SamplesBuffer::new(1, SAMPLE_RATE as u32, samples));
debug!("sleeping until end of sink");
sink.sleep_until_end();
}
Export(_opts) => todo!(),
Memo(_opts) => todo!(),
}
Ok(())
.context("Failed to process input tokens")
}