agb/agb-sound-converter/src/lib.rs

93 lines
2.5 KiB
Rust
Raw Normal View History

use hound;
use proc_macro::TokenStream;
use quote::quote;
use siphasher::sip::SipHasher;
use std::{
fs::File,
hash::{Hash, Hasher},
io::Write,
path::Path,
};
use syn::parse_macro_input;
#[proc_macro]
pub fn include_wav(input: TokenStream) -> TokenStream {
let input = parse_macro_input!(input as syn::LitStr);
let filename = input.value();
let root = std::env::var("CARGO_MANIFEST_DIR").expect("Failed to get cargo manifest dir");
let path = Path::new(&root).join(&*filename);
let include_path = path.to_string_lossy();
let wav_reader = hound::WavReader::open(&path)
.unwrap_or_else(|_| panic!("Failed to load file {}", include_path));
assert_eq!(
wav_reader.spec().sample_rate,
10512,
"agb currently only supports sample rate of 10512Hz"
);
let samples = samples_from_reader(wav_reader);
let out_file_path_include = {
let out_dir = std::env::var("OUT_DIR").expect("Expected OUT_DIR");
let out_filename = get_out_filename(&path);
let out_file_path = Path::new(&out_dir).with_file_name(&out_filename);
let mut out_file = File::create(&out_file_path).expect("Failed to open file for writing");
out_file
.write_all(&samples.collect::<Vec<_>>())
.expect("Failed to write to temporary file");
out_file_path
}
.clone()
.canonicalize()
.expect("Failed to canonicalize");
let out_file_path_include = out_file_path_include.to_string_lossy();
let result = quote! {
{
const _: &[u8] = include_bytes!(#include_path);
include_bytes!(#out_file_path_include)
}
};
TokenStream::from(result)
}
fn samples_from_reader<'a, R>(reader: hound::WavReader<R>) -> Box<dyn Iterator<Item = u8> + 'a>
where
R: std::io::Read + 'a,
{
let bitrate = reader.spec().bits_per_sample;
let reduction = bitrate - 8;
match reader.spec().sample_format {
hound::SampleFormat::Float => Box::new(
reader
.into_samples::<f32>()
.map(|sample| (sample.unwrap() * (i8::MAX as f32)) as u8),
),
hound::SampleFormat::Int => Box::new(
reader
.into_samples::<i32>()
.map(move |sample| (sample.unwrap() >> reduction) as u8),
),
}
}
fn get_out_filename(path: &Path) -> String {
let mut hasher = SipHasher::new();
path.hash(&mut hasher);
format!("{}.raw", hasher.finish())
}