Skip to content

Commit bb0f93f

Browse files
author
Kevin Brothaler
committed
Add code to sync the header after writing samples.
1 parent deff6d8 commit bb0f93f

File tree

1 file changed

+91
-9
lines changed

1 file changed

+91
-9
lines changed

src/writer.rs

Lines changed: 91 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ use std::cmp;
1414
use std::error;
1515
use std::fmt;
1616
use std::io;
17-
use std::io::{Seek, Write};
17+
use std::io::{Seek, SeekFrom, Write};
1818
use std::result;
1919

2020
use byteorder::{LittleEndian, WriteBytesExt};
@@ -150,15 +150,15 @@ trait WriteWaveExt: Write + Seek {
150150
try!(self.write_u32_l(0)); // Size of data subchunk.
151151

152152
Ok(())
153-
}
153+
}
154154

155155
fn write_u16_l(&mut self, n: u16) -> io::Result<()> {
156156
self.write_u16::<LittleEndian>(n)
157157
}
158158

159159
fn write_u32_l(&mut self, n: u32) -> io::Result<()> {
160160
self.write_u32::<LittleEndian>(n)
161-
}
161+
}
162162
}
163163

164164
impl<T> WriteWaveExt for T where T: Seek + Write {}
@@ -176,6 +176,9 @@ pub struct WaveWriter<T>
176176
/// Represents the PCM format for this wave file.
177177
pub pcm_format: PcmFormat,
178178

179+
// How many samples have been written.
180+
written_samples: u32,
181+
179182
// The underlying writer that we'll use to read data.
180183
writer: T,
181184
}
@@ -200,20 +203,20 @@ impl<T> WaveWriter<T>
200203

201204
Ok(WaveWriter {
202205
pcm_format: pcm_format,
206+
written_samples: 0,
203207
writer: writer,
204208
})
205209
}
206210

207-
208211
/// Writes a single sample as an unsigned 8-bit value.
209212
pub fn write_sample_u8(&mut self, sample: u8) -> io::Result<()> {
210213
self.write_sample(sample, |writer, sample| writer.write_u8(sample))
211214
}
212215

213216
/// Writes a single sample as a signed 16-bit value.
214217
pub fn write_sample_i16(&mut self, sample: i16) -> io::Result<()> {
215-
self.write_sample(sample,
216-
|writer, sample| writer.write_i16::<LittleEndian>(sample))
218+
self.write_sample(sample, |writer, sample|
219+
writer.write_i16::<LittleEndian>(sample))
217220
}
218221

219222
/// Writes a single sample as a signed 24-bit value. The value will be truncated
@@ -227,14 +230,35 @@ impl<T> WaveWriter<T>
227230

228231
/// Writes a single sample as a signed 32-bit value.
229232
pub fn write_sample_i32(&mut self, sample: i32) -> io::Result<()> {
230-
self.write_sample(sample,
231-
|writer, sample| writer.write_i32::<LittleEndian>(sample))
233+
self.write_sample(sample, |writer, sample|
234+
writer.write_i32::<LittleEndian>(sample))
232235
}
233236

234237
fn write_sample<F, S>(&mut self, sample: S, write_data: F) -> io::Result<()>
235238
where F: Fn(&mut T, S) -> io::Result<()>
236239
{
237-
Ok(try!(write_data(&mut self.writer, sample)))
240+
try!(write_data(&mut self.writer, sample));
241+
self.written_samples = self.written_samples + 1;
242+
Ok(())
243+
}
244+
245+
/// Updates the header at the beginning of the file with the new chunk sizes.
246+
pub fn sync_header(&mut self) -> io::Result<()> {
247+
let data_chunk_size = self.written_samples * self.pcm_format.bits_per_sample as u32 / 8;
248+
let riff_chunk_size = 36 + data_chunk_size;
249+
250+
// File size minus eight bytes
251+
try!(self.writer.seek(SeekFrom::Start(4)));
252+
try!(self.writer.write_u32_l(riff_chunk_size));
253+
254+
// Data size minus eight bytes
255+
try!(self.writer.seek(SeekFrom::Start(40)));
256+
try!(self.writer.write_u32_l(data_chunk_size));
257+
258+
// Seek back to the end so we can continue writing
259+
try!(self.writer.seek(SeekFrom::End(0)));
260+
261+
Ok(())
238262
}
239263

240264
/// Consumes this writer, returning the underlying value.
@@ -300,5 +324,63 @@ mod tests {
300324
assert_eq!(16, wave_reader.pcm_format.bits_per_sample);
301325
}
302326

327+
// Write validation tests
328+
329+
#[test]
330+
fn test_header_sync_when_no_data_written() {
331+
let data = Vec::new();
332+
let mut cursor = Cursor::new(data);
333+
let mut wave_writer = WaveWriter::new(1, 44100, 16, cursor).unwrap();
334+
wave_writer.sync_header().unwrap();
335+
let mut cursor = wave_writer.into_inner();
336+
337+
cursor.set_position(0);
338+
339+
let wave_reader = WaveReader::new(cursor).unwrap();
340+
let cursor = wave_reader.into_inner();
341+
let data = cursor.into_inner();
342+
343+
assert_eq!(44, data.len());
344+
// We're not currently surfacing the chunk/subchunk info in the reader
345+
// so just access the data directly.
346+
347+
// Should match 36 in little-endian format.
348+
assert_eq!(b"\x24\x00\x00\x00", &data[4..8]);
349+
350+
// Should match 0 in little-endian format.
351+
assert_eq!(b"\x00\x00\x00\x00", &data[40..44]);
352+
}
353+
354+
#[test]
355+
fn test_header_sync_when_ten_samples_written() {
356+
let data = Vec::new();
357+
let mut cursor = Cursor::new(data);
358+
let mut wave_writer = WaveWriter::new(1, 44100, 16, cursor).unwrap();
359+
360+
for i in 0..10 {
361+
wave_writer.write_sample_i16(i as i16).unwrap();
362+
}
363+
364+
wave_writer.sync_header().unwrap();
365+
366+
let mut cursor = wave_writer.into_inner();
367+
368+
cursor.set_position(0);
369+
370+
let wave_reader = WaveReader::new(cursor).unwrap();
371+
let cursor = wave_reader.into_inner();
372+
let data = cursor.into_inner();
373+
374+
assert_eq!(64, data.len());
375+
// We're not currently surfacing the chunk/subchunk info in the reader
376+
// so just access the data directly.
377+
378+
// Should match 56 in little-endian format.
379+
assert_eq!(b"\x38\x00\x00\x00", &data[4..8]);
380+
381+
// Should match 20 in little-endian format.
382+
assert_eq!(b"\x14\x00\x00\x00", &data[40..44]);
383+
}
384+
303385
// TODO test header
304386
}

0 commit comments

Comments
 (0)