1use hound::SampleFormat;
2use std::io::{Read, Seek};
3use std::num::TryFromIntError;
4
5#[derive(Debug, Clone, Copy, Eq, PartialEq)]
6pub struct Metadata {
7 pub sample_rate: u32,
8 pub sample_count: u32,
9 pub loop_start: Option<u32>,
10 pub end: Option<u32>,
11 pub bits_per_sample: u16,
12}
13
14pub struct QWaveReader<R: Read> {
15 reader: hound::WavReader<R>,
16 loop_start: Option<u32>,
17 loop_length: Option<u32>,
18}
19
20impl<R: Read + Seek> QWaveReader<R> {
21 pub fn new(reader: R) -> Result<Self, String> {
22 let mut chunk_reader =
23 cuet::ChunkReader::new(reader).map_err(|e| e.to_string())?;
24
25 let cue_chunk = chunk_reader
26 .read_next_chunk(Some(*b"cue "))
27 .map_err(|e| e.to_string())?;
28
29 let loop_start = cue_chunk.and_then(|(_, bytes)| {
30 let pts = cuet::parse_cue_points(&bytes[..]);
31
32 if pts.is_empty() {
33 None
34 } else {
35 Some(pts[0].sample_offset)
36 }
37 });
38
39 let loop_length = if loop_start.is_some() {
40 let list_chunk = chunk_reader
41 .read_next_chunk(Some(*b"LIST"))
42 .map_err(|e| e.to_string())?;
43
44 list_chunk.and_then(|(_, bytes)| {
45 let labeled_texts =
46 cuet::extract_labeled_text_from_list(&bytes);
47 labeled_texts
48 .first()
49 .filter(|ltxt| ltxt.purpose_id == *b"mark")
50 .map(|ltxt| ltxt.sample_length)
51 })
52 } else {
53 None
54 };
55
56 let reader = hound::WavReader::new(
57 chunk_reader.restore_cursor().map_err(|e| e.to_string())?,
58 )
59 .map_err(|e| e.to_string())?;
60
61 Ok(QWaveReader {
62 reader,
63 loop_start,
64 loop_length,
65 })
66 }
67}
68
69impl<R: Read> QWaveReader<R> {
70 pub fn metadata(&self) -> Metadata {
71 let sample_count = self.reader.duration();
72
73 let end = if let (Some(start), Some(length)) =
74 (self.loop_start, self.loop_length)
75 {
76 start.checked_add(length)
77 } else {
78 None
79 };
80
81 Metadata {
82 sample_rate: self.reader.spec().sample_rate,
83 sample_count,
84 loop_start: self.loop_start,
85 end,
86 bits_per_sample: self.reader.spec().bits_per_sample,
87 }
88 }
89
90 pub fn collect_samples(&mut self) -> Result<Vec<i16>, String> {
91 let mut error = Option::<String>::None;
92 let spec = self.reader.spec();
93 let duration = self
94 .reader
95 .duration()
96 .try_into()
97 .map_err(|e: TryFromIntError| e.to_string())?;
98
99 if spec.channels != 1 {
100 return Err("Too many channels".into());
101 }
102
103 if spec.sample_format != SampleFormat::Int {
104 return Err("Float samples are unsupported".into());
105 }
106
107 let samp_to_i16 = if spec.bits_per_sample == 8 {
108 |s| s << 8
109 } else if spec.bits_per_sample == 16 {
110 |s| s
111 } else {
112 return Err("Samples must be 8- or 16-bits".into());
113 };
114
115 let samples = self
116 .reader
117 .samples::<i16>()
118 .take(duration)
119 .map_while(|s| match s {
120 Ok(s) => Some(samp_to_i16(s)),
121 Err(e) => {
122 error = Some(e.to_string());
123 None
124 }
125 })
126 .collect();
127
128 if let Some(e) = error {
129 Err(e)
130 } else {
131 Ok(samples)
132 }
133 }
134}