1#![cfg_attr(not(test), no_std)]
3
4pub mod decoder;
5pub mod enums;
6
7#[cfg(test)]
8mod tests {
9 use crate::decoder::Decoder;
10 use crate::enums::{State, Tone};
11 use rodio::{Decoder as RDecoder, Source};
12 use std::fs::File;
13 use std::io::BufReader;
14
15 #[test]
17 fn decoder_works() {
18 let expected = vec![
19 (Tone::One, State::On),
20 (Tone::One, State::Off),
21 (Tone::Two, State::On),
22 (Tone::Two, State::Off),
23 (Tone::Three, State::On),
24 (Tone::Three, State::Off),
25 (Tone::Nine, State::On),
26 (Tone::Nine, State::Off),
27 (Tone::Eight, State::On),
28 (Tone::Eight, State::Off),
29 (Tone::Seven, State::On),
30 (Tone::Seven, State::Off),
31 (Tone::Five, State::On),
32 (Tone::Five, State::Off),
33 (Tone::Four, State::On),
34 (Tone::Four, State::Off),
35 (Tone::Nine, State::On),
36 (Tone::Nine, State::Off),
37 (Tone::Three, State::On),
38 (Tone::Three, State::Off),
39 (Tone::Two, State::On),
40 (Tone::Two, State::Off),
41 (Tone::Three, State::On),
42 (Tone::Three, State::Off),
43 (Tone::Eight, State::On),
44 (Tone::Eight, State::Off),
45 (Tone::Seven, State::On),
46 (Tone::Seven, State::Off),
47 (Tone::A, State::On),
48 (Tone::A, State::Off),
49 (Tone::B, State::On),
50 (Tone::B, State::Off),
51 (Tone::C, State::On),
52 (Tone::C, State::Off),
53 (Tone::D, State::On),
54 (Tone::D, State::Off),
55 (Tone::Asterisk, State::On),
56 (Tone::Asterisk, State::Off),
57 (Tone::Pound, State::On),
58 ];
59
60 let file = BufReader::new(File::open("data/dtmf_test.wav").unwrap());
61 let source = RDecoder::new(file).unwrap();
62 let samples = source.convert_samples();
63 let sample_rate = samples.sample_rate();
64 let data: Vec<f32> = samples.collect();
65
66 {
68 let mut actual = vec![];
69 let mut decoder = Decoder::new(sample_rate, |tone, state| {
70 actual.push((tone, state));
71 });
72
73 for s in data.chunks(1) {
74 decoder.process(s);
75 }
76
77 assert_eq!(expected, actual);
78 }
79
80 {
82 let mut actual = vec![];
83 let mut decoder = Decoder::new(sample_rate, |tone, state| {
84 actual.push((tone, state));
85 });
86
87 for s in data.chunks(40_000) {
88 decoder.process(s);
89 }
90
91 assert_eq!(expected, actual);
92 }
93 }
94
95 #[test]
96 fn decoder_works_noisy() {
97 let file = BufReader::new(File::open("data/noisy_dtmf.mp3").unwrap());
98 let source = RDecoder::new(file).unwrap();
99 let samples = source.convert_samples();
100 let sample_rate = samples.sample_rate();
101 let data: Vec<f32> = samples.collect();
102
103 let c1;
105 {
106 let mut actual = vec![];
107 let mut decoder = Decoder::new(sample_rate, |tone, state| {
108 actual.push((tone, state));
109 });
110
111 for s in data.chunks(1) {
112 decoder.process(s);
113 }
114
115 c1 = actual.len();
116 log_sanity_check(actual);
117 }
118
119 let c2;
121 {
122 let mut actual = vec![];
123 let mut decoder = Decoder::new(sample_rate, |tone, state| {
124 actual.push((tone, state));
125 });
126
127 for s in data.chunks(40_000) {
128 decoder.process(s);
129 }
130
131 c2 = actual.len();
132 log_sanity_check(actual);
133 }
134
135 assert_eq!(c1, c2);
136 assert_eq!(c1, 8047); }
138
139 #[test]
140 fn no_false_positives() {
141 let file = BufReader::new(File::open("data/radio_chatter.mp3").unwrap());
144 let source = RDecoder::new(file).unwrap();
145 let samples = source.convert_samples();
146 let sample_rate = samples.sample_rate();
147 let data: Vec<f32> = samples.collect();
148
149 let c1;
151 {
152 let mut actual = vec![];
153 let mut decoder = Decoder::new(sample_rate, |tone, state| {
154 actual.push((tone, state));
155 });
156
157 for s in data.chunks(1) {
158 decoder.process(s);
159 }
160
161 c1 = actual.len();
162 log_sanity_check(actual);
163 }
164
165 let c2;
167 {
168 let mut actual = vec![];
169 let mut decoder = Decoder::new(sample_rate, |tone, state| {
170 actual.push((tone, state));
171 });
172
173 for s in data.chunks(40_000) {
174 decoder.process(s);
175 }
176
177 c2 = actual.len();
178 log_sanity_check(actual);
179 }
180
181 assert_eq!(c1, c2);
182 assert_eq!(c1, 34); }
184
185 #[test]
187 fn dont_on_when_on_dont_off_when_off() {
188 let expected = vec![(Tone::One, State::On), (Tone::One, State::Off)];
189
190 let file = BufReader::new(File::open("data/dtmf_edge.wav").unwrap());
191 let source = RDecoder::new(file).unwrap();
192 let samples = source.convert_samples();
193 let sample_rate = samples.sample_rate();
194 let data: Vec<f32> = samples.collect();
195
196 {
198 let mut actual = vec![];
199 let mut decoder = Decoder::new(sample_rate, |tone, state| {
200 actual.push((tone, state));
201 });
202
203 for s in data.chunks(1) {
204 decoder.process(s);
205 }
206
207 assert_eq!(expected, actual);
208 }
209
210 {
212 let mut actual = vec![];
213 let mut decoder = Decoder::new(sample_rate, |tone, state| {
214 actual.push((tone, state));
215 });
216
217 for s in data.chunks(40_000) {
218 decoder.process(s);
219 }
220
221 assert_eq!(expected, actual);
222 }
223 }
224
225 fn log_sanity_check(mut log: Vec<(Tone, State)>) {
226 if log.last().unwrap().1 == State::On {
228 log.push((log.last().unwrap().0, State::Off));
229 }
230
231 for c in log.chunks(2) {
233 assert_eq!(c[0].0, c[1].0);
234 assert_eq!(State::On, c[0].1);
235 assert_eq!(State::Off, c[1].1);
236 }
237 }
238}