export mode
This commit is contained in:
parent
da38edbfd1
commit
c6cde8ffbf
3 changed files with 96 additions and 50 deletions
5
.gitignore
vendored
5
.gitignore
vendored
|
@ -29,3 +29,8 @@ out/
|
|||
|
||||
# Samply
|
||||
*.json.gz
|
||||
|
||||
# audio files
|
||||
*.mp3
|
||||
*.raw
|
||||
*.wav
|
|
@ -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>>,
|
||||
}
|
||||
|
||||
|
|
|
@ -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")
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue