rakata_formats/wav/
pcm.rs1use super::{Wav, WavEncoding};
7use thiserror::Error;
8
9#[derive(Debug, Error)]
11pub enum PcmError {
12 #[error("unsupported encoding: {0:?}")]
14 UnsupportedEncoding(Option<WavEncoding>),
15
16 #[error("unsupported bit depth: {0}")]
18 UnsupportedBitDepth(u16),
19
20 #[error("data length {0} is not a multiple of block alignment {1}")]
22 MisalignedData(usize, usize),
23}
24
25pub fn decode_pcm_as_float(wav: &Wav) -> Result<Vec<f32>, PcmError> {
37 let encoding = wav.encoding.known();
38
39 if encoding != Some(WavEncoding::Pcm) {
40 return Err(PcmError::UnsupportedEncoding(encoding));
41 }
42
43 let channels = usize::from(wav.channels);
44 if channels == 0 {
45 return Ok(Vec::new());
46 }
47
48 let bits = wav.bits_per_sample;
49 let bytes_per_sample = usize::from(bits / 8);
50 let block_align = channels * bytes_per_sample;
51
52 if !wav.data.len().is_multiple_of(block_align) {
53 return Err(PcmError::MisalignedData(wav.data.len(), block_align));
54 }
55
56 let num_samples = wav.data.len() / bytes_per_sample;
57 let mut samples = Vec::with_capacity(num_samples);
58
59 match bits {
60 8 => {
61 for &byte in &wav.data {
64 let sample = (f32::from(byte) - 128.0) / 128.0;
65 samples.push(sample);
66 }
67 }
68 16 => {
69 for chunk in wav.data.chunks_exact(2) {
72 let sample_i16 = i16::from_le_bytes([chunk[0], chunk[1]]);
73 let sample = f32::from(sample_i16) / 32768.0;
74 samples.push(sample);
75 }
76 }
77 _ => return Err(PcmError::UnsupportedBitDepth(bits)),
78 }
79
80 Ok(samples)
81}
82
83#[cfg(test)]
84mod tests {
85 use super::*;
86 use crate::wav::{WavEncodingCode, WavType, WavWaveMetadata};
87
88 fn make_wav(bits: u16, data: Vec<u8>) -> Wav {
89 Wav::new_wave(
90 WavType::Vo,
91 WavWaveMetadata {
92 encoding: WavEncodingCode::from(WavEncoding::Pcm),
93 channels: 1,
94 sample_rate: 44100,
95 bytes_per_sec: 44100 * (u32::from(bits) / 8),
96 block_align: bits / 8,
97 bits_per_sample: bits,
98 },
99 data,
100 )
101 }
102
103 #[test]
104 fn test_decode_8bit_pcm() {
105 let data = vec![128, 0, 255];
107 let wav = make_wav(8, data);
108 let samples = decode_pcm_as_float(&wav).unwrap();
109
110 assert_eq!(samples.len(), 3);
111 assert!((samples[0] - 0.0).abs() < 1e-5);
112 assert!((samples[1] - -1.0).abs() < 1e-5);
113 assert!((samples[2] - 0.9921875).abs() < 1e-5);
114 }
115
116 #[test]
117 fn test_decode_16bit_pcm() {
118 let data = vec![
120 0x00, 0x00, 0x00, 0x80, 0xFF, 0x7F, ];
124 let wav = make_wav(16, data);
125 let samples = decode_pcm_as_float(&wav).unwrap();
126
127 assert_eq!(samples.len(), 3);
128 assert!((samples[0] - 0.0).abs() < 1e-5);
129 assert!((samples[1] - -1.0).abs() < 1e-5);
130 assert!((samples[2] - 0.9999695).abs() < 1e-5);
131 }
132
133 #[test]
134 fn test_unsupported_encoding() {
135 let mut wav = make_wav(16, vec![]);
136 wav.encoding = WavEncodingCode::from(WavEncoding::Mp3); let err = decode_pcm_as_float(&wav).unwrap_err();
138 assert!(matches!(err, PcmError::UnsupportedEncoding(_)));
139 }
140}