1use crate::core::{crc32, FloResult, Frame, FrameType};
2use crate::{ResidualEncoding, HEADER_SIZE, MAGIC, VERSION_MAJOR, VERSION_MINOR};
3use alloc::vec::Vec;
4
5pub struct Writer {
7 buffer: Vec<u8>,
8}
9
10impl Writer {
11 pub fn new() -> Self {
13 Writer { buffer: Vec::new() }
14 }
15
16 pub fn write(
18 self,
19 sample_rate: u32,
20 channels: u8,
21 bit_depth: u8,
22 compression_level: u8,
23 frames: &[Frame],
24 metadata: &[u8],
25 ) -> FloResult<Vec<u8>> {
26 self.write_ex(
27 sample_rate,
28 channels,
29 bit_depth,
30 compression_level,
31 false,
32 0,
33 frames,
34 metadata,
35 )
36 }
37
38 #[allow(clippy::too_many_arguments)]
40 pub fn write_ex(
41 mut self,
42 sample_rate: u32,
43 channels: u8,
44 bit_depth: u8,
45 compression_level: u8,
46 lossy: bool,
47 lossy_quality: u8,
48 frames: &[Frame],
49 metadata: &[u8],
50 ) -> FloResult<Vec<u8>> {
51 let toc_size = 4 + (frames.len() * 20) as u64;
53 let data_chunk = self.build_data_chunk(frames);
54 let data_size = data_chunk.len() as u64;
55 let extra_size = 0u64;
56 let meta_size = metadata.len() as u64;
57
58 let data_crc32 = crc32::compute(&data_chunk);
60
61 let toc_chunk = self.build_toc_chunk(frames, sample_rate);
63
64 let mut flags: u16 = 0;
66 if lossy {
67 flags |= 0x01; flags |= (lossy_quality as u16) << 8; }
70
71 let total_samples: u64 = frames.iter().map(|frame| frame.frame_samples as u64).sum();
73
74 self.write_header_ex(
76 sample_rate,
77 channels,
78 bit_depth,
79 compression_level,
80 total_samples,
81 data_crc32,
82 flags,
83 toc_size,
84 data_size,
85 extra_size,
86 meta_size,
87 );
88
89 self.buffer.extend_from_slice(&toc_chunk);
91
92 self.buffer.extend_from_slice(&data_chunk);
94
95 self.buffer.extend_from_slice(metadata);
99
100 Ok(self.buffer)
101 }
102
103 #[allow(dead_code, clippy::too_many_arguments)]
104 fn write_header(
105 &mut self,
106 sample_rate: u32,
107 channels: u8,
108 bit_depth: u8,
109 compression_level: u8,
110 total_samples: u64,
111 data_crc32: u32,
112 toc_size: u64,
113 data_size: u64,
114 extra_size: u64,
115 meta_size: u64,
116 ) {
117 self.write_header_ex(
118 sample_rate,
119 channels,
120 bit_depth,
121 compression_level,
122 total_samples,
123 data_crc32,
124 0,
125 toc_size,
126 data_size,
127 extra_size,
128 meta_size,
129 );
130 }
131
132 #[allow(clippy::too_many_arguments)]
133 fn write_header_ex(
134 &mut self,
135 sample_rate: u32,
136 channels: u8,
137 bit_depth: u8,
138 compression_level: u8,
139 total_samples: u64,
140 data_crc32: u32,
141 flags: u16,
142 toc_size: u64,
143 data_size: u64,
144 extra_size: u64,
145 meta_size: u64,
146 ) {
147 self.buffer.extend_from_slice(&MAGIC);
149
150 self.buffer.push(VERSION_MAJOR);
152 self.buffer.push(VERSION_MINOR);
153
154 self.buffer.extend_from_slice(&flags.to_le_bytes());
156
157 self.buffer.extend_from_slice(&sample_rate.to_le_bytes());
159
160 self.buffer.push(channels);
162
163 self.buffer.push(bit_depth);
165
166 self.buffer.extend_from_slice(&total_samples.to_le_bytes());
168
169 self.buffer.push(compression_level);
171
172 self.buffer.extend_from_slice(&[0, 0, 0]);
174
175 self.buffer.extend_from_slice(&data_crc32.to_le_bytes());
177
178 self.buffer.extend_from_slice(&HEADER_SIZE.to_le_bytes());
180
181 self.buffer.extend_from_slice(&toc_size.to_le_bytes());
183
184 self.buffer.extend_from_slice(&data_size.to_le_bytes());
186
187 self.buffer.extend_from_slice(&extra_size.to_le_bytes());
189
190 self.buffer.extend_from_slice(&meta_size.to_le_bytes());
192 }
193
194 fn build_toc_chunk(&self, frames: &[Frame], sample_rate: u32) -> Vec<u8> {
195 let mut toc = Vec::new();
196
197 toc.extend_from_slice(&(frames.len() as u32).to_le_bytes());
199
200 let mut byte_offset = 0u64;
201 let mut cumulative_samples = 0u64;
202
203 for (i, frame) in frames.iter().enumerate() {
204 let frame_size = frame.byte_size() as u32;
205
206 toc.extend_from_slice(&(i as u32).to_le_bytes());
208
209 toc.extend_from_slice(&byte_offset.to_le_bytes());
211
212 toc.extend_from_slice(&frame_size.to_le_bytes());
214
215 let timestamp_ms = (cumulative_samples * 1000 / sample_rate as u64) as u32;
218 toc.extend_from_slice(×tamp_ms.to_le_bytes());
219
220 byte_offset += frame_size as u64;
221 cumulative_samples += frame.frame_samples as u64;
222 }
223
224 toc
225 }
226
227 fn build_data_chunk(&self, frames: &[Frame]) -> Vec<u8> {
228 let mut data = Vec::new();
229
230 for frame in frames {
231 self.write_frame(&mut data, frame);
232 }
233
234 data
235 }
236
237 fn write_frame(&self, buffer: &mut Vec<u8>, frame: &Frame) {
238 let frame_type = FrameType::from(frame.frame_type);
239
240 buffer.push(frame.frame_type);
242 buffer.extend_from_slice(&frame.frame_samples.to_le_bytes());
243 buffer.push(frame.flags);
244
245 for ch_data in &frame.channels {
247 let mut ch_buffer = Vec::new();
249 self.write_channel_data(&mut ch_buffer, ch_data, frame_type);
250
251 buffer.extend_from_slice(&(ch_buffer.len() as u32).to_le_bytes());
253 buffer.extend_from_slice(&ch_buffer);
254 }
255 }
256
257 fn write_channel_data(
258 &self,
259 buffer: &mut Vec<u8>,
260 ch_data: &crate::ChannelData,
261 frame_type: FrameType,
262 ) {
263 match frame_type {
264 FrameType::Silence => {
265 }
267 FrameType::Raw => {
268 buffer.extend_from_slice(&ch_data.residuals);
270 }
271 FrameType::Transform => {
272 buffer.extend_from_slice(&ch_data.residuals);
274 }
275 _ if frame_type.is_alpc() => {
276 buffer.push(ch_data.predictor_coeffs.len() as u8);
278
279 for &coeff in &ch_data.predictor_coeffs {
281 buffer.extend_from_slice(&coeff.to_le_bytes());
282 }
283
284 buffer.push(ch_data.shift_bits);
286
287 buffer.push(ch_data.residual_encoding as u8);
289
290 if ch_data.residual_encoding == ResidualEncoding::Rice {
292 buffer.push(ch_data.rice_parameter);
293 }
294
295 buffer.extend_from_slice(&ch_data.residuals);
297 }
298 _ => {
299 }
301 }
302 }
303}
304
305impl Default for Writer {
306 fn default() -> Self {
307 Self::new()
308 }
309}