1use super::{CodecError, Decoder, Encoder, Sample};
8
9pub const EVENT_CLOCK: u32 = 8000;
10pub const DEFAULT_EVENT_MS: u32 = 100;
11const INTERIM_STEP_MS: u64 = 50;
12const DEFAULT_VOLUME: u8 = 10;
13
14pub struct TelephoneEventDecoder {}
15
16impl TelephoneEventDecoder {
17 pub fn new() -> Self {
18 Self {}
19 }
20}
21
22impl Default for TelephoneEventDecoder {
23 fn default() -> Self {
24 Self::new()
25 }
26}
27
28impl Decoder for TelephoneEventDecoder {
29 fn decode_into(&mut self, _data: &[u8], _out: &mut [Sample]) -> Result<usize, CodecError> {
30 Ok(0)
31 }
32
33 fn max_decode_samples(&self, _n_bytes: usize) -> usize {
34 0
35 }
36
37 fn sample_rate(&self) -> u32 {
38 8000
39 }
40
41 fn channels(&self) -> u16 {
42 1
43 }
44}
45
46pub struct TelephoneEventEncoder {}
47
48impl TelephoneEventEncoder {
49 pub fn new() -> Self {
50 Self {}
51 }
52}
53
54impl Default for TelephoneEventEncoder {
55 fn default() -> Self {
56 Self::new()
57 }
58}
59
60impl Encoder for TelephoneEventEncoder {
61 fn encode_into(&mut self, _samples: &[Sample], _out: &mut [u8]) -> Result<usize, CodecError> {
62 Ok(0)
63 }
64
65 fn max_encode_bytes(&self, _n_samples: usize) -> usize {
66 0
67 }
68
69 fn sample_rate(&self) -> u32 {
70 8000
71 }
72
73 fn channels(&self) -> u16 {
74 1
75 }
76}
77
78pub fn digit_to_event(c: char) -> Option<u8> {
80 match c {
81 '0'..='9' => Some(c as u8 - b'0'),
82 '*' => Some(10),
83 '#' => Some(11),
84 'A'..='D' | 'a'..='d' => Some(12 + (c.to_ascii_uppercase() as u8 - b'A')),
85 _ => None,
86 }
87}
88
89pub fn event_to_digit(event: u8) -> char {
91 match event {
92 0..=9 => (b'0' + event) as char,
93 10 => '*',
94 11 => '#',
95 12..=15 => (b'A' + event - 12) as char,
96 _ => '?',
97 }
98}
99
100#[derive(Debug, Clone, Copy, PartialEq, Eq)]
101pub struct DtmfEvent {
102 pub event: u8,
103 pub end: bool,
104 pub volume: u8,
105 pub duration: u16,
106}
107
108pub fn parse_telephone_event(data: &[u8]) -> Option<DtmfEvent> {
110 if data.len() < 4 || data.len() % 4 != 0 {
111 return None;
112 }
113 let off = (data.len() / 4 - 1) * 4;
114 Some(DtmfEvent {
115 event: data[off],
116 end: (data[off + 1] & 0x80) != 0,
117 volume: data[off + 1] & 0x3F,
118 duration: ((data[off + 2] as u16) << 8) | (data[off + 3] as u16),
119 })
120}
121
122#[derive(Debug, Clone, Copy, PartialEq, Eq)]
123pub struct DtmfPacket {
124 pub payload: [u8; 4],
125 pub marker: bool,
126}
127
128pub fn encode_event_payload(event: u8, end: bool, volume: u8, duration: u16) -> [u8; 4] {
130 let flags = (if end { 0x80 } else { 0x00 }) | (volume & 0x3F);
131 [event, flags, (duration >> 8) as u8, (duration & 0xFF) as u8]
132}
133
134pub fn dtmf_sequence(digit: u8, duration_ms: u32, clock_rate: u32) -> alloc::vec::Vec<DtmfPacket> {
137 let total_samples = u64::from(duration_ms) * u64::from(clock_rate) / 1000;
138 let total_duration = total_samples.min(u64::from(u16::MAX)) as u16;
139 let step = ((INTERIM_STEP_MS * u64::from(clock_rate) / 1000).min(u64::from(u16::MAX))) as u16;
140
141 let mut packets = alloc::vec::Vec::new();
142 let mut first = true;
143 let mut d = step;
144 while d < total_duration {
145 packets.push(DtmfPacket {
146 payload: encode_event_payload(digit, false, DEFAULT_VOLUME, d),
147 marker: first,
148 });
149 first = false;
150 d = d.saturating_add(step);
151 }
152 for _ in 0..3 {
153 packets.push(DtmfPacket {
154 payload: encode_event_payload(digit, true, DEFAULT_VOLUME, total_duration),
155 marker: first,
156 });
157 first = false;
158 }
159 packets
160}
161
162pub fn dtmf_press(digit: u8) -> alloc::vec::Vec<DtmfPacket> {
163 dtmf_sequence(digit, DEFAULT_EVENT_MS, EVENT_CLOCK)
164}
165
166#[cfg(test)]
167mod tests {
168 use super::*;
169
170 #[test]
171 fn digit_mapping() {
172 assert_eq!(digit_to_event('0'), Some(0));
173 assert_eq!(digit_to_event('9'), Some(9));
174 assert_eq!(digit_to_event('*'), Some(10));
175 assert_eq!(digit_to_event('#'), Some(11));
176 assert_eq!(digit_to_event('A'), Some(12));
177 assert_eq!(digit_to_event('d'), Some(15));
178 assert_eq!(digit_to_event('x'), None);
179 }
180
181 #[test]
182 fn reverse_mapping() {
183 for c in "0123456789*#ABCD".chars() {
184 let e = digit_to_event(c).unwrap();
185 assert_eq!(event_to_digit(e), c);
186 }
187 }
188
189 #[test]
190 fn encode_payload_shape() {
191 assert_eq!(
192 encode_event_payload(5, true, 10, 1600),
193 [5, 0x8A, 0x06, 0x40]
194 );
195 }
196
197 #[test]
198 fn parse_event() {
199 let e = parse_telephone_event(&[5, 0x8A, 0x06, 0x40]).unwrap();
200 assert_eq!(e.event, 5);
201 assert!(e.end);
202 assert_eq!(e.volume, 10);
203 assert_eq!(e.duration, 1600);
204 }
205
206 #[test]
207 fn parse_takes_last_block() {
208 let e = parse_telephone_event(&[3, 0x0A, 0x01, 0x90, 5, 0x8A, 0x06, 0x40]).unwrap();
209 assert_eq!(e.event, 5);
210 assert!(e.end);
211 }
212
213 #[test]
214 fn parse_rejects_malformed() {
215 assert!(parse_telephone_event(&[1, 2, 3]).is_none());
216 assert!(parse_telephone_event(&[]).is_none());
217 assert!(parse_telephone_event(&[1, 2, 3, 4, 5]).is_none());
218 }
219
220 #[test]
221 fn sequence_shape() {
222 let packets = dtmf_sequence(5, 200, EVENT_CLOCK);
223 assert!(packets.len() >= 4);
224 assert!(packets[0].marker);
225 assert!(!packets[1..].iter().any(|p| p.marker));
226 assert!(packets.iter().all(|p| p.payload[0] == 5));
227 let last = packets.last().unwrap();
228 assert_eq!(last.payload[1] & 0x80, 0x80);
229 let dur = ((last.payload[2] as u16) << 8) | last.payload[3] as u16;
230 assert_eq!(dur, 1600);
231 }
232
233 #[test]
234 fn sequence_is_loss_resilient() {
235 let packets = dtmf_sequence(3, 100, EVENT_CLOCK);
236 assert_eq!(
237 packets.iter().filter(|p| p.payload[1] & 0x80 != 0).count(),
238 3
239 );
240 }
241
242 #[test]
243 fn roundtrip_encode_parse() {
244 for digit in 0u8..16 {
245 let press = dtmf_press(digit);
246 let e = parse_telephone_event(&press.last().unwrap().payload).unwrap();
247 assert_eq!(e.event, digit);
248 assert!(e.end);
249 }
250 }
251
252 #[test]
253 fn extreme_durations_do_not_overflow() {
254 let seq = dtmf_sequence(1, u32::MAX, EVENT_CLOCK);
255 let last = seq.last().unwrap();
256 let dur = ((last.payload[2] as u16) << 8) | last.payload[3] as u16;
257 assert_eq!(dur, u16::MAX);
258 }
259}