Skip to main content

sound_to_patch/
audio.rs

1//! Stem intake: decoding, mono, resampling and the [`Stem`].
2//!
3//! A file decodes at its native rate. A consumer that needs another rate
4//! resamples. The [`Stem`] adds the beat grid, the band edge, the flat-top
5//! flag and the stereo screen.
6//!
7//! Samples are `f64` from the decoder on. Every channel is kept, because the
8//! stereo statistics need both. Mono is the mean of the first two channels,
9//! the pair the stereo statistics read, with no gain normalisation. Only a consumer with a fixed input rate, such as the
10//! transcription model, resamples; analysis stays at the native rate.
11//!
12//! ```no_run
13//! use sound_to_patch::audio::{self, SampleRate};
14//!
15//! let stem = audio::decode("bass.wav".as_ref())?.into_audio();
16//! let mono = stem.mono();
17//! let model_rate = SampleRate::new(22_050)?;
18//! let resampled = audio::resample(&mono, stem.sample_rate(), model_rate)?;
19//! # Ok::<(), Box<dyn std::error::Error>>(())
20//! ```
21
22mod decode;
23mod resample;
24mod stem;
25mod stereo;
26mod tempo;
27
28pub use decode::{
29    DecodeError, Decoded, MAX_SAMPLE_RATE, MAX_SKIPPED_PACKETS, MIN_SAMPLE_RATE, SkippedPacket,
30    decode,
31};
32pub(crate) use decode::{decode_bytes, read_stem};
33pub use resample::{ResampleError, resample};
34#[doc(inline)]
35pub use sound_to_patch_core::audio::{
36    Audio, AudioError, SampleRate, mono_mean, write_wav, write_wav_to,
37};
38pub use stem::{
39    BAND_EDGE_CONVENTION, BAND_EDGE_MARGIN_DB, BAND_EDGE_PLATEAU_DB, BAND_EDGE_STEEP_DROP_DB,
40    BeatGrid, CEILING_TOLERANCE, CLIP_APPROACH, Division, FLAT_TOP_MIN_RUN_S, FLAT_TOP_MIN_SHARE,
41    Feel, HARD_LIMITED_CONVENTION, LONG_FRAME_BELOW_HZ, NoteValue, Stem, band_edge, is_flat_topped,
42};
43pub use stereo::{
44    Band, HIGH_EDGE_HZ, SCREEN_FRAME_LEN, SCREEN_HARMONICS, STEREO_CONVENTION, SUB_EDGE_HZ,
45    StereoScreen, StereoStats, stereo_screen,
46};
47pub use tempo::{
48    BPM_STEP, MAX_BEAT_LAGS, MIN_BEAT_LAGS, OVERLAP_BEATS, PEAK_RATIO_MIN, PERIODICITY_MIN,
49    SEARCH_SHARE, SHARPNESS_MIN, SPARSE_ONSETS_MAX, SPARSE_PERIODICITY_MIN, TEMPO_CONVENTION,
50    Tempo,
51};
52
53#[cfg(test)]
54mod tests {
55    use super::*;
56
57    #[test]
58    fn the_decoder_reads_a_written_file_back() {
59        let dir = tempfile::tempdir().unwrap();
60        let path = dir.path().join("two.wav");
61        let left = vec![0.0, 0.5, -0.25, 1.0];
62        let right = vec![-1.0, 0.125, 0.0, 0.75];
63        let audio = Audio::new(SampleRate::new(44_100).unwrap(), vec![left, right]).unwrap();
64        write_wav(&path, &audio).unwrap();
65        assert_eq!(decode(&path).unwrap().into_audio(), audio);
66    }
67}