Skip to main content

rescale_core/wav/
reader.rs

1//! Binary Reader untuk mem-parsing file RIFF WAV ke f32 channel buffer.
2
3use super::header::{WavHeader, WavSampleFormat};
4use std::fs::File;
5use std::io::{self, BufReader, Read, Seek, SeekFrom};
6use std::path::Path;
7
8pub struct WavReader;
9
10impl WavReader {
11    /// Membaca file WAV dari path filesystem
12    pub fn read_from_file<P: AsRef<Path>>(path: P) -> io::Result<(WavHeader, Vec<Vec<f32>>)> {
13        let file = File::open(path)?;
14        let mut reader = BufReader::new(file);
15        Self::read_from_stream(&mut reader)
16    }
17
18    /// Mem-parsing stream WAV binary dari memory / file reader
19    pub fn read_from_stream<R: Read + Seek>(
20        reader: &mut R,
21    ) -> io::Result<(WavHeader, Vec<Vec<f32>>)> {
22        let mut fourcc = [0u8; 4];
23
24        // 1. Periksa header RIFF
25        reader.read_exact(&mut fourcc)?;
26        if &fourcc != b"RIFF" {
27            return Err(io::Error::new(
28                io::ErrorKind::InvalidData,
29                "Not a valid RIFF file",
30            ));
31        }
32
33        let mut riff_size_bytes = [0u8; 4];
34        reader.read_exact(&mut riff_size_bytes)?;
35
36        reader.read_exact(&mut fourcc)?;
37        if &fourcc != b"WAVE" {
38            return Err(io::Error::new(
39                io::ErrorKind::InvalidData,
40                "Not a WAVE format",
41            ));
42        }
43
44        let mut audio_format: u16 = 0;
45        let mut num_channels: u16 = 0;
46        let mut sample_rate: u32 = 0;
47        let mut bits_per_sample: u16 = 0;
48        let mut data_chunk_size: u32 = 0;
49        let mut fmt_found = false;
50
51        // Loop melalui chunks sampai menemukan "fmt " dan "data"
52        loop {
53            if reader.read_exact(&mut fourcc).is_err() {
54                break;
55            }
56
57            let mut chunk_size_bytes = [0u8; 4];
58            reader.read_exact(&mut chunk_size_bytes)?;
59            let chunk_size = u32::from_le_bytes(chunk_size_bytes);
60
61            match &fourcc {
62                b"fmt " => {
63                    let mut format_tag_bytes = [0u8; 2];
64                    let mut channels_bytes = [0u8; 2];
65                    let mut samplerate_bytes = [0u8; 4];
66                    let mut byterate_bytes = [0u8; 4];
67                    let mut blockalign_bytes = [0u8; 2];
68                    let mut bitspersample_bytes = [0u8; 2];
69
70                    reader.read_exact(&mut format_tag_bytes)?;
71                    reader.read_exact(&mut channels_bytes)?;
72                    reader.read_exact(&mut samplerate_bytes)?;
73                    reader.read_exact(&mut byterate_bytes)?;
74                    reader.read_exact(&mut blockalign_bytes)?;
75                    reader.read_exact(&mut bitspersample_bytes)?;
76
77                    audio_format = u16::from_le_bytes(format_tag_bytes);
78                    num_channels = u16::from_le_bytes(channels_bytes);
79                    sample_rate = u32::from_le_bytes(samplerate_bytes);
80                    bits_per_sample = u16::from_le_bytes(bitspersample_bytes);
81
82                    // Lewati byte ekstra jika chunk_size > 16
83                    if chunk_size > 16 {
84                        reader.seek(SeekFrom::Current((chunk_size - 16) as i64))?;
85                    }
86                    fmt_found = true;
87                }
88                b"data" => {
89                    data_chunk_size = chunk_size;
90                    break; // Mulai baca data audio
91                }
92                _ => {
93                    // Lewati chunk lain (misal "LIST", "JUNK", dll.)
94                    reader.seek(SeekFrom::Current(chunk_size as i64))?;
95                }
96            }
97        }
98
99        if !fmt_found {
100            return Err(io::Error::new(
101                io::ErrorKind::InvalidData,
102                "Missing 'fmt ' chunk in WAV file",
103            ));
104        }
105
106        let sample_format = match (audio_format, bits_per_sample) {
107            (1, 16) => WavSampleFormat::Pcm16Bit,
108            (1, 24) => WavSampleFormat::Pcm24Bit,
109            (3, 32) => WavSampleFormat::Pcm32BitFloat,
110            _ => {
111                return Err(io::Error::new(
112                    io::ErrorKind::Unsupported,
113                    format!(
114                        "Unsupported WAV format: tag={}, bits={}",
115                        audio_format, bits_per_sample
116                    ),
117                ));
118            }
119        };
120
121        let bytes_per_sample = (bits_per_sample / 8) as usize;
122        let total_samples = (data_chunk_size as usize) / bytes_per_sample;
123        let samples_per_channel = total_samples / (num_channels as usize);
124
125        let mut channels: Vec<Vec<f32>> =
126            vec![Vec::with_capacity(samples_per_channel); num_channels as usize];
127
128        // De-interleave data dari file binary
129        for _ in 0..samples_per_channel {
130            for channel in channels.iter_mut().take(num_channels as usize) {
131                let sample_val: f32 = match sample_format {
132                    WavSampleFormat::Pcm16Bit => {
133                        let mut b = [0u8; 2];
134                        reader.read_exact(&mut b)?;
135                        let i = i16::from_le_bytes(b);
136                        if i < 0 {
137                            i as f32 / 32768.0
138                        } else {
139                            i as f32 / 32767.0
140                        }
141                    }
142                    WavSampleFormat::Pcm24Bit => {
143                        let mut b = [0u8; 3];
144                        reader.read_exact(&mut b)?;
145                        // Sign extend 24-bit ke int 32-bit
146                        let sign = if b[2] & 0x80 != 0 { 0xFF } else { 0x00 };
147                        let i = i32::from_le_bytes([b[0], b[1], b[2], sign]);
148                        if i < 0 {
149                            i as f32 / 8388608.0
150                        } else {
151                            i as f32 / 8388607.0
152                        }
153                    }
154                    WavSampleFormat::Pcm32BitFloat => {
155                        let mut b = [0u8; 4];
156                        reader.read_exact(&mut b)?;
157                        f32::from_le_bytes(b)
158                    }
159                };
160                channel.push(sample_val);
161            }
162        }
163
164        let header = WavHeader::new(
165            num_channels,
166            sample_rate,
167            sample_format,
168            samples_per_channel,
169        );
170        Ok((header, channels))
171    }
172}