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> {
30 let encoding = wav.encoding.known();
31
32 if encoding != Some(WavEncoding::Pcm) {
33 return Err(PcmError::UnsupportedEncoding(encoding));
34 }
35
36 let channels = usize::from(wav.channels);
37 if channels == 0 {
38 return Ok(Vec::new());
39 }
40
41 let bits = wav.bits_per_sample;
42 let bytes_per_sample = usize::from(bits / 8);
43 let block_align = channels * bytes_per_sample;
44
45 if !wav.data.len().is_multiple_of(block_align) {
46 return Err(PcmError::MisalignedData(wav.data.len(), block_align));
47 }
48
49 let num_samples = wav.data.len() / bytes_per_sample;
50 let mut samples = Vec::with_capacity(num_samples);
51
52 match bits {
53 8 => {
54 for &byte in &wav.data {
57 let sample = (f32::from(byte) - 128.0) / 128.0;
58 samples.push(sample);
59 }
60 }
61 16 => {
62 for chunk in wav.data.chunks_exact(2) {
65 let sample_i16 = i16::from_le_bytes([chunk[0], chunk[1]]);
66 let sample = f32::from(sample_i16) / 32768.0;
67 samples.push(sample);
68 }
69 }
70 _ => return Err(PcmError::UnsupportedBitDepth(bits)),
71 }
72
73 Ok(samples)
74}
75
76#[cfg(test)]
77mod tests {
78 use super::*;
79 use crate::wav::{WavEncodingCode, WavType, WavWaveMetadata};
80
81 fn make_wav(bits: u16, data: Vec<u8>) -> Wav {
82 Wav::new_wave(
83 WavType::Vo,
84 WavWaveMetadata {
85 encoding: WavEncodingCode::from(WavEncoding::Pcm),
86 channels: 1,
87 sample_rate: 44100,
88 bytes_per_sec: 44100 * (u32::from(bits) / 8),
89 block_align: bits / 8,
90 bits_per_sample: bits,
91 },
92 data,
93 )
94 }
95
96 #[test]
97 fn test_decode_8bit_pcm() {
98 let data = vec![128, 0, 255];
100 let wav = make_wav(8, data);
101 let samples = decode_pcm_as_float(&wav).unwrap();
102
103 assert_eq!(samples.len(), 3);
104 assert!((samples[0] - 0.0).abs() < 1e-5);
105 assert!((samples[1] - -1.0).abs() < 1e-5);
106 assert!((samples[2] - 0.9921875).abs() < 1e-5);
107 }
108
109 #[test]
110 fn test_decode_16bit_pcm() {
111 let data = vec![
113 0x00, 0x00, 0x00, 0x80, 0xFF, 0x7F, ];
117 let wav = make_wav(16, data);
118 let samples = decode_pcm_as_float(&wav).unwrap();
119
120 assert_eq!(samples.len(), 3);
121 assert!((samples[0] - 0.0).abs() < 1e-5);
122 assert!((samples[1] - -1.0).abs() < 1e-5);
123 assert!((samples[2] - 0.9999695).abs() < 1e-5);
124 }
125
126 #[test]
127 fn test_unsupported_encoding() {
128 let mut wav = make_wav(16, vec![]);
129 wav.encoding = WavEncodingCode::from(WavEncoding::Mp3); let err = decode_pcm_as_float(&wav).unwrap_err();
131 assert!(matches!(err, PcmError::UnsupportedEncoding(_)));
132 }
133}