use std::io::Write;
use std::path::Path;
pub fn generate_wav(
path: &Path,
sample_rate: u32,
channels: u16,
duration_secs: f32,
bit_depth: u16,
) {
let bytes_per_sample = bit_depth / 8;
let block_align = channels * bytes_per_sample;
let byte_rate = sample_rate * block_align as u32;
let num_samples = (sample_rate as f32 * duration_secs) as u32;
let data_size = num_samples * block_align as u32;
let file_size = 4 + 24 + 8 + data_size;
let mut file = std::fs::File::create(path).expect("failed to create WAV file");
file.write_all(b"RIFF").unwrap();
file.write_all(&file_size.to_le_bytes()).unwrap();
file.write_all(b"WAVE").unwrap();
file.write_all(b"fmt ").unwrap();
file.write_all(&16u32.to_le_bytes()).unwrap(); file.write_all(&1u16.to_le_bytes()).unwrap(); file.write_all(&channels.to_le_bytes()).unwrap();
file.write_all(&sample_rate.to_le_bytes()).unwrap();
file.write_all(&byte_rate.to_le_bytes()).unwrap();
file.write_all(&block_align.to_le_bytes()).unwrap();
file.write_all(&bit_depth.to_le_bytes()).unwrap();
file.write_all(b"data").unwrap();
file.write_all(&data_size.to_le_bytes()).unwrap();
let zeros = [0u8; 4096];
let mut remaining = data_size as usize;
while remaining > 0 {
let chunk = remaining.min(zeros.len());
file.write_all(&zeros[..chunk]).unwrap();
remaining -= chunk;
}
file.flush().unwrap();
}
pub fn generate_wav_tone(path: &Path, sample_rate: u32, frequency_hz: f32, duration_secs: f32) {
let channels: u16 = 1;
let bit_depth: u16 = 16;
let bytes_per_sample = bit_depth / 8;
let block_align = channels * bytes_per_sample;
let byte_rate = sample_rate * block_align as u32;
let num_samples = (sample_rate as f32 * duration_secs) as u32;
let data_size = num_samples * block_align as u32;
let file_size = 4 + 24 + 8 + data_size;
let mut file = std::fs::File::create(path).expect("failed to create WAV file");
file.write_all(b"RIFF").unwrap();
file.write_all(&file_size.to_le_bytes()).unwrap();
file.write_all(b"WAVE").unwrap();
file.write_all(b"fmt ").unwrap();
file.write_all(&16u32.to_le_bytes()).unwrap();
file.write_all(&1u16.to_le_bytes()).unwrap(); file.write_all(&channels.to_le_bytes()).unwrap();
file.write_all(&sample_rate.to_le_bytes()).unwrap();
file.write_all(&byte_rate.to_le_bytes()).unwrap();
file.write_all(&block_align.to_le_bytes()).unwrap();
file.write_all(&bit_depth.to_le_bytes()).unwrap();
file.write_all(b"data").unwrap();
file.write_all(&data_size.to_le_bytes()).unwrap();
for i in 0..num_samples {
let t = i as f32 / sample_rate as f32;
let sample = (2.0 * std::f32::consts::PI * frequency_hz * t).sin();
let sample_i16 = (sample * i16::MAX as f32) as i16;
file.write_all(&sample_i16.to_le_bytes()).unwrap();
}
file.flush().unwrap();
}
pub fn generate_mp3_with_both_tags(path: &Path, id3v2_title: &str, id3v1_title: &str) {
let mut out: Vec<u8> = Vec::new();
let mut frames: Vec<u8> = Vec::new();
frames.extend_from_slice(b"TIT2");
let body_len = id3v2_title.len() as u32 + 1; frames.extend_from_slice(&body_len.to_be_bytes());
frames.extend_from_slice(&[0, 0]); frames.push(0); frames.extend_from_slice(id3v2_title.as_bytes());
frames.extend_from_slice(b"TRCK");
frames.extend_from_slice(&2u32.to_be_bytes());
frames.extend_from_slice(&[0, 0]);
frames.push(0);
frames.push(b'7');
out.extend_from_slice(b"ID3");
out.extend_from_slice(&[3, 0, 0]); let n = frames.len() as u32;
out.extend_from_slice(&[
((n >> 21) & 0x7f) as u8,
((n >> 14) & 0x7f) as u8,
((n >> 7) & 0x7f) as u8,
(n & 0x7f) as u8,
]);
out.extend_from_slice(&frames);
for _ in 0..40 {
out.extend_from_slice(&[0xFF, 0xFB, 0x90, 0x00]);
out.extend_from_slice(&[0u8; 413]);
}
let mut v1 = [0u8; 128];
v1[..3].copy_from_slice(b"TAG");
let title = id3v1_title.as_bytes();
let take = title.len().min(30);
v1[3..3 + take].copy_from_slice(&title[..take]);
out.extend_from_slice(&v1);
std::fs::write(path, out).expect("failed to write MP3 file");
}
pub fn generate_mp3_with_picture(
path: &Path,
version: u8,
title: &str,
artist: &str,
picture: &[u8],
) -> std::ops::Range<u64> {
fn synch(n: u32) -> u32 {
(n & 0x7F)
| ((n & (0x7F << 7)) << 1)
| ((n & (0x7F << 14)) << 2)
| ((n & (0x7F << 21)) << 3)
}
let frame = |out: &mut Vec<u8>, id_v2: &str, id_v3: &str, body: &[u8]| {
let len = body.len() as u32;
if version == 2 {
out.extend_from_slice(id_v2.as_bytes());
out.extend_from_slice(&len.to_be_bytes()[1..]);
} else {
out.extend_from_slice(id_v3.as_bytes());
let len = if version == 4 { synch(len) } else { len };
out.extend_from_slice(&len.to_be_bytes());
out.extend_from_slice(&[0, 0]); }
out.extend_from_slice(body);
};
let mut frames: Vec<u8> = Vec::new();
let mut text = vec![0u8]; text.extend_from_slice(title.as_bytes());
frame(&mut frames, "TT2", "TIT2", &text);
let mut art = vec![0u8]; if version == 2 {
art.extend_from_slice(b"JPG");
} else {
art.extend_from_slice(b"image/jpeg\0");
}
art.push(3); art.push(0); art.extend_from_slice(picture);
let picture_start = frames.len() + if version == 2 { 6 } else { 10 };
let picture_len = art.len();
frame(&mut frames, "PIC", "APIC", &art);
let mut text = vec![0u8];
text.extend_from_slice(artist.as_bytes());
frame(&mut frames, "TP1", "TPE1", &text);
let tag_len = frames.len() + 64;
let mut out: Vec<u8> = Vec::new();
out.extend_from_slice(b"ID3");
out.extend_from_slice(&[version, 0, 0]);
out.extend_from_slice(&synch(tag_len as u32).to_be_bytes());
out.extend_from_slice(&frames);
out.resize(10 + tag_len, 0);
for _ in 0..40 {
out.extend_from_slice(&[0xFF, 0xFB, 0x90, 0x00]);
out.extend_from_slice(&[0u8; 413]);
}
std::fs::write(path, out).expect("failed to write MP3 file");
let start = (10 + picture_start) as u64;
start..start + picture_len as u64
}