1use crate::core::{crc32, FloResult, Frame, FrameType};
2use crate::{ResidualEncoding, HEADER_SIZE, MAGIC, VERSION_MAJOR, VERSION_MINOR};
3
4pub struct Writer {
6 buffer: Vec<u8>,
7}
8
9impl Writer {
10 pub fn new() -> Self {
12 Writer { buffer: Vec::new() }
13 }
14
15 pub fn write(
17 self,
18 sample_rate: u32,
19 channels: u8,
20 bit_depth: u8,
21 compression_level: u8,
22 frames: &[Frame],
23 metadata: &[u8],
24 ) -> FloResult<Vec<u8>> {
25 self.write_ex(
26 sample_rate,
27 channels,
28 bit_depth,
29 compression_level,
30 false,
31 0,
32 frames,
33 metadata,
34 )
35 }
36
37 #[allow(clippy::too_many_arguments)]
39 pub fn write_ex(
40 mut self,
41 sample_rate: u32,
42 channels: u8,
43 bit_depth: u8,
44 compression_level: u8,
45 lossy: bool,
46 lossy_quality: u8,
47 frames: &[Frame],
48 metadata: &[u8],
49 ) -> FloResult<Vec<u8>> {
50 let toc_size = 4 + (frames.len() * 20) as u64;
52 let data_chunk = self.build_data_chunk(frames);
53 let data_size = data_chunk.len() as u64;
54 let extra_size = 0u64;
55 let meta_size = metadata.len() as u64;
56
57 let data_crc32 = crc32::compute(&data_chunk);
59
60 let toc_chunk = self.build_toc_chunk(frames);
62
63 let mut flags: u16 = 0;
65 if lossy {
66 flags |= 0x01; flags |= (lossy_quality as u16) << 8; }
69
70 self.write_header_ex(
72 sample_rate,
73 channels,
74 bit_depth,
75 compression_level,
76 frames.len() as u64,
77 data_crc32,
78 flags,
79 toc_size,
80 data_size,
81 extra_size,
82 meta_size,
83 );
84
85 self.buffer.extend_from_slice(&toc_chunk);
87
88 self.buffer.extend_from_slice(&data_chunk);
90
91 self.buffer.extend_from_slice(metadata);
95
96 Ok(self.buffer)
97 }
98
99 #[allow(dead_code, clippy::too_many_arguments)]
100 fn write_header(
101 &mut self,
102 sample_rate: u32,
103 channels: u8,
104 bit_depth: u8,
105 compression_level: u8,
106 total_frames: u64,
107 data_crc32: u32,
108 toc_size: u64,
109 data_size: u64,
110 extra_size: u64,
111 meta_size: u64,
112 ) {
113 self.write_header_ex(
114 sample_rate,
115 channels,
116 bit_depth,
117 compression_level,
118 total_frames,
119 data_crc32,
120 0,
121 toc_size,
122 data_size,
123 extra_size,
124 meta_size,
125 );
126 }
127
128 #[allow(clippy::too_many_arguments)]
129 fn write_header_ex(
130 &mut self,
131 sample_rate: u32,
132 channels: u8,
133 bit_depth: u8,
134 compression_level: u8,
135 total_frames: u64,
136 data_crc32: u32,
137 flags: u16,
138 toc_size: u64,
139 data_size: u64,
140 extra_size: u64,
141 meta_size: u64,
142 ) {
143 self.buffer.extend_from_slice(&MAGIC);
145
146 self.buffer.push(VERSION_MAJOR);
148 self.buffer.push(VERSION_MINOR);
149
150 self.buffer.extend_from_slice(&flags.to_le_bytes());
152
153 self.buffer.extend_from_slice(&sample_rate.to_le_bytes());
155
156 self.buffer.push(channels);
158
159 self.buffer.push(bit_depth);
161
162 self.buffer.extend_from_slice(&total_frames.to_le_bytes());
164
165 self.buffer.push(compression_level);
167
168 self.buffer.extend_from_slice(&[0, 0, 0]);
170
171 self.buffer.extend_from_slice(&data_crc32.to_le_bytes());
173
174 self.buffer.extend_from_slice(&HEADER_SIZE.to_le_bytes());
176
177 self.buffer.extend_from_slice(&toc_size.to_le_bytes());
179
180 self.buffer.extend_from_slice(&data_size.to_le_bytes());
182
183 self.buffer.extend_from_slice(&extra_size.to_le_bytes());
185
186 self.buffer.extend_from_slice(&meta_size.to_le_bytes());
188 }
189
190 fn build_toc_chunk(&self, frames: &[Frame]) -> Vec<u8> {
191 let mut toc = Vec::new();
192
193 toc.extend_from_slice(&(frames.len() as u32).to_le_bytes());
195
196 let mut byte_offset = 0u64;
197
198 for (i, frame) in frames.iter().enumerate() {
199 let frame_size = frame.byte_size() as u32;
200
201 toc.extend_from_slice(&(i as u32).to_le_bytes());
203
204 toc.extend_from_slice(&byte_offset.to_le_bytes());
206
207 toc.extend_from_slice(&frame_size.to_le_bytes());
209
210 let timestamp_ms = (i as u32) * 1000;
212 toc.extend_from_slice(×tamp_ms.to_le_bytes());
213
214 byte_offset += frame_size as u64;
215 }
216
217 toc
218 }
219
220 fn build_data_chunk(&self, frames: &[Frame]) -> Vec<u8> {
221 let mut data = Vec::new();
222
223 for frame in frames {
224 self.write_frame(&mut data, frame);
225 }
226
227 data
228 }
229
230 fn write_frame(&self, buffer: &mut Vec<u8>, frame: &Frame) {
231 let frame_type = FrameType::from(frame.frame_type);
232
233 buffer.push(frame.frame_type);
235 buffer.extend_from_slice(&frame.frame_samples.to_le_bytes());
236 buffer.push(frame.flags);
237
238 for ch_data in &frame.channels {
240 let mut ch_buffer = Vec::new();
242 self.write_channel_data(&mut ch_buffer, ch_data, frame_type);
243
244 buffer.extend_from_slice(&(ch_buffer.len() as u32).to_le_bytes());
246 buffer.extend_from_slice(&ch_buffer);
247 }
248 }
249
250 fn write_channel_data(
251 &self,
252 buffer: &mut Vec<u8>,
253 ch_data: &crate::ChannelData,
254 frame_type: FrameType,
255 ) {
256 match frame_type {
257 FrameType::Silence => {
258 }
260 FrameType::Raw => {
261 buffer.extend_from_slice(&ch_data.residuals);
263 }
264 FrameType::Transform => {
265 buffer.extend_from_slice(&ch_data.residuals);
267 }
268 _ if frame_type.is_alpc() => {
269 buffer.push(ch_data.predictor_coeffs.len() as u8);
271
272 for &coeff in &ch_data.predictor_coeffs {
274 buffer.extend_from_slice(&coeff.to_le_bytes());
275 }
276
277 buffer.push(ch_data.shift_bits);
279
280 buffer.push(ch_data.residual_encoding as u8);
282
283 if ch_data.residual_encoding == ResidualEncoding::Rice {
285 buffer.push(ch_data.rice_parameter);
286 }
287
288 buffer.extend_from_slice(&ch_data.residuals);
290 }
291 _ => {
292 }
294 }
295 }
296}
297
298impl Default for Writer {
299 fn default() -> Self {
300 Self::new()
301 }
302}