use super::grid;
use super::waveform::{self, Measured};
use crate::anlz::cues::{self, Kind};
use crate::anlz::section::{section, AnlzFile, Section};
use crate::collection::Track;
const HEADER_TAIL: [u8; 16] = [0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0];
pub fn pvbr(mp3_audio_frames: Option<u32>) -> Section {
let mut p = vec![0u8; 4 + 400 * 4 + 4];
if let Some(frames) = mp3_audio_frames {
p[1604..1608].copy_from_slice(&frames.wrapping_mul(1152).to_be_bytes());
}
section(b"PVBR", 0x10, &p)
}
fn file(sections: Vec<Section>) -> AnlzFile {
AnlzFile {
header_tail: HEADER_TAIL,
sections,
}
}
pub fn build_files(
track: &Track,
usb_path: &str,
audio: &Measured,
mp3_audio_frames: Option<u32>,
) -> [AnlzFile; 3] {
let waves = waveform::analyze(audio);
let bpm = track
.tempos
.first()
.map(|t| t.bpm)
.unwrap_or(track.average_bpm);
let mut dat = vec![
pvbr(mp3_audio_frames),
grid::pqtz(&track.tempos, audio.duration_ms()),
];
dat.extend(waves.dat_sections());
dat.extend(cues::sections(Kind::Dat, &track.cues, bpm));
let mut ext = vec![waves.pwv3_section()];
ext.extend(cues::sections(Kind::Ext, &track.cues, bpm));
ext.push(grid::pqt2_empty());
ext.push(waves.pwv5_section());
ext.push(waves.pwv4_section());
let two_ex = waves.two_ex_sections();
let mut files = [file(dat), file(ext), file(two_ex)];
for f in &mut files {
f.set_path(usb_path);
}
files
}
#[cfg(test)]
mod tests {
use super::*;
use crate::collection::{Cue, Tempo};
fn tags(f: &AnlzFile) -> Vec<&str> {
f.sections.iter().map(|s| s.tag_str()).collect()
}
#[test]
fn section_order_matches_rekordbox() {
let mut meter = waveform::Meter::new(44100, 2);
meter.push(&[0.1; 44100 * 2]);
let audio = meter.finish();
let track = Track {
tempos: vec![Tempo {
inizio: 0.1,
bpm: 120.0,
metro: "4/4".into(),
battito: 1,
}],
cues: vec![Cue {
start: 0.1,
num: 0,
rgb: Some((40, 226, 20)),
..Default::default()
}],
total_time: 1,
..Default::default()
};
let [dat, ext, two] = build_files(&track, "/Contents/A/B/c.mp3", &audio, Some(38));
assert_eq!(
tags(&dat),
["PPTH", "PVBR", "PQTZ", "PWAV", "PWV2", "PCOB", "PCOB"]
);
assert_eq!(
tags(&ext),
["PPTH", "PWV3", "PCOB", "PCOB", "PCO2", "PCO2", "PQT2", "PWV5", "PWV4"]
);
assert_eq!(tags(&two), ["PPTH", "PWV7", "PWV6", "PWVC"]);
let pvbr = dat.find(b"PVBR").unwrap();
assert_eq!(pvbr.bytes.len(), 1620);
assert_eq!(&pvbr.bytes[1616..], &(38u32 * 1152).to_be_bytes());
assert_eq!(&dat.to_bytes()[12..28], &HEADER_TAIL);
for f in [&dat, &ext, &two] {
assert_eq!(AnlzFile::parse(&f.to_bytes()).unwrap(), *f);
}
}
}