blob: 689bc543d7487cb3e6e8102ada8346afc8691edd [file] [log] [blame]
andrew@webrtc.orga7b57da2012-10-22 18:19:23 +00001/*
2 * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved.
3 *
4 * Use of this source code is governed by a BSD-style license
5 * that can be found in the LICENSE file in the root of the source
6 * tree. An additional intellectual property rights grant can be found
7 * in the file PATENTS. All contributing project authors may
8 * be found in the AUTHORS file in the root of the source tree.
9 */
10
11#include "dtmf_inband.h"
12
13#include "critical_section_wrapper.h"
14#include "trace.h"
15#include <cassert>
16
17namespace webrtc {
18
19const WebRtc_Word16 Dtmf_a_times2Tab8Khz[8]=
20{
21 27978, 26956, 25701, 24219,
22 19073, 16325, 13085, 9314
23};
24
25const WebRtc_Word16 Dtmf_a_times2Tab16Khz[8]=
26{
27 31548, 31281, 30951, 30556,
28 29144, 28361, 27409, 26258
29};
30
31const WebRtc_Word16 Dtmf_a_times2Tab32Khz[8]=
32{
33 32462,32394, 32311, 32210, 31849, 31647, 31400, 31098
34};
35
36// Second table is sin(2*pi*f/fs) in Q14
37
38const WebRtc_Word16 Dtmf_ym2Tab8Khz[8]=
39{
40 8527, 9315, 10163, 11036,
41 13322, 14206, 15021, 15708
42};
43
44const WebRtc_Word16 Dtmf_ym2Tab16Khz[8]=
45{
46 4429, 4879, 5380, 5918,
47 7490, 8207, 8979, 9801
48};
49
50const WebRtc_Word16 Dtmf_ym2Tab32Khz[8]=
51{
52 2235, 2468, 2728, 3010, 3853, 4249, 4685, 5164
53};
54
55const WebRtc_Word16 Dtmf_dBm0kHz[37]=
56{
57 16141, 14386, 12821, 11427, 10184, 9077,
58 8090, 7210, 6426, 5727, 5104, 4549,
59 4054, 3614, 3221, 2870, 2558, 2280,
60 2032, 1811, 1614, 1439, 1282, 1143,
61 1018, 908, 809, 721, 643, 573,
62 510, 455, 405, 361, 322, 287,
63 256
64};
65
66
67DtmfInband::DtmfInband(const WebRtc_Word32 id) :
68 _critSect(*CriticalSectionWrapper::CreateCriticalSection()),
69 _id(id),
70 _outputFrequencyHz(8000),
71 _frameLengthSamples(0),
72 _remainingSamples(0),
73 _eventCode(0),
74 _attenuationDb(0),
75 _lengthMs(0),
76 _reinit(true),
77 _playing(false),
78 _delaySinceLastToneMS(1000)
79{
80 memset(_oldOutputLow, 0, sizeof(_oldOutputLow));
81 memset(_oldOutputHigh, 0, sizeof(_oldOutputHigh));
82}
83
84DtmfInband::~DtmfInband()
85{
86 delete &_critSect;
87}
88
89int
90DtmfInband::SetSampleRate(const WebRtc_UWord16 frequency)
91{
92 if (frequency != 8000 &&
93 frequency != 16000 &&
94 frequency != 32000)
95 {
96 // invalid sample rate
97 assert(false);
98 return -1;
99 }
100 _outputFrequencyHz = frequency;
101 return 0;
102}
103
104int
105DtmfInband::GetSampleRate(WebRtc_UWord16& frequency)
106{
107 frequency = _outputFrequencyHz;
108 return 0;
109}
110
111void
112DtmfInband::Init()
113{
114 _remainingSamples = 0;
115 _frameLengthSamples = 0;
116 _eventCode = 0;
117 _attenuationDb = 0;
118 _lengthMs = 0;
119 _reinit = true;
120 _oldOutputLow[0] = 0;
121 _oldOutputLow[1] = 0;
122 _oldOutputHigh[0] = 0;
123 _oldOutputHigh[1] = 0;
124 _delaySinceLastToneMS = 1000;
125}
126
127int
128DtmfInband::AddTone(const WebRtc_UWord8 eventCode,
129 WebRtc_Word32 lengthMs,
130 WebRtc_Word32 attenuationDb)
131{
132 CriticalSectionScoped lock(&_critSect);
133
134 if (attenuationDb > 36 || eventCode > 15)
135 {
136 assert(false);
137 return -1;
138 }
139
140 if (IsAddingTone())
141 {
142 WEBRTC_TRACE(kTraceWarning, kTraceVoice, VoEId(_id,-1),
143 "DtmfInband::AddTone() new tone interrupts ongoing tone");
144 }
145
146 ReInit();
147
148 _frameLengthSamples = static_cast<WebRtc_Word16> (_outputFrequencyHz / 100);
149 _eventCode = static_cast<WebRtc_Word16> (eventCode);
150 _attenuationDb = static_cast<WebRtc_Word16> (attenuationDb);
151 _remainingSamples = static_cast<WebRtc_Word32>
152 (lengthMs * (_outputFrequencyHz / 1000));
153 _lengthMs = lengthMs;
154
155 return 0;
156}
157
158int
159DtmfInband::ResetTone()
160{
161 CriticalSectionScoped lock(&_critSect);
162
163 ReInit();
164
165 _frameLengthSamples = static_cast<WebRtc_Word16> (_outputFrequencyHz / 100);
166 _remainingSamples = static_cast<WebRtc_Word32>
167 (_lengthMs * (_outputFrequencyHz / 1000));
168
169 return 0;
170}
171
172int
173DtmfInband::StartTone(const WebRtc_UWord8 eventCode,
174 WebRtc_Word32 attenuationDb)
175{
176 CriticalSectionScoped lock(&_critSect);
177
178 if (attenuationDb > 36 || eventCode > 15)
179 {
180 assert(false);
181 return -1;
182 }
183
184 if (IsAddingTone())
185 {
186 return -1;
187 }
188
189 ReInit();
190
191 _frameLengthSamples = static_cast<WebRtc_Word16> (_outputFrequencyHz / 100);
192 _eventCode = static_cast<WebRtc_Word16> (eventCode);
193 _attenuationDb = static_cast<WebRtc_Word16> (attenuationDb);
194 _playing = true;
195
196 return 0;
197}
198
199int
200DtmfInband::StopTone()
201{
202 CriticalSectionScoped lock(&_critSect);
203
204 if (!_playing)
205 {
206 return 0;
207 }
208
209 _playing = false;
210
211 return 0;
212}
213
214// Shall be called between tones
215void
216DtmfInband::ReInit()
217{
218 _reinit = true;
219}
220
221bool
222DtmfInband::IsAddingTone()
223{
224 CriticalSectionScoped lock(&_critSect);
225 return (_remainingSamples > 0 || _playing);
226}
227
228int
229DtmfInband::Get10msTone(WebRtc_Word16 output[320],
230 WebRtc_UWord16& outputSizeInSamples)
231{
232 CriticalSectionScoped lock(&_critSect);
233 if (DtmfFix_generate(output,
234 _eventCode,
235 _attenuationDb,
236 _frameLengthSamples,
237 _outputFrequencyHz) == -1)
238 {
239 return -1;
240 }
241 _remainingSamples -= _frameLengthSamples;
242 outputSizeInSamples = _frameLengthSamples;
243 _delaySinceLastToneMS = 0;
244 return 0;
245}
246
247void
248DtmfInband::UpdateDelaySinceLastTone()
249{
250 _delaySinceLastToneMS += kDtmfFrameSizeMs;
251 // avoid wraparound
252 if (_delaySinceLastToneMS > (1<<30))
253 {
254 _delaySinceLastToneMS = 1000;
255 }
256}
257
258WebRtc_UWord32
259DtmfInband::DelaySinceLastTone() const
260{
261 return _delaySinceLastToneMS;
262}
263
264WebRtc_Word16
265DtmfInband::DtmfFix_generate(WebRtc_Word16 *decoded,
266 const WebRtc_Word16 value,
267 const WebRtc_Word16 volume,
268 const WebRtc_Word16 frameLen,
269 const WebRtc_Word16 fs)
270{
271 const WebRtc_Word16 *a_times2Tbl;
272 const WebRtc_Word16 *y2_Table;
273 WebRtc_Word16 a1_times2 = 0, a2_times2 = 0;
274
275 if (fs==8000) {
276 a_times2Tbl=Dtmf_a_times2Tab8Khz;
277 y2_Table=Dtmf_ym2Tab8Khz;
278 } else if (fs==16000) {
279 a_times2Tbl=Dtmf_a_times2Tab16Khz;
280 y2_Table=Dtmf_ym2Tab16Khz;
281 } else if (fs==32000) {
282 a_times2Tbl=Dtmf_a_times2Tab32Khz;
283 y2_Table=Dtmf_ym2Tab32Khz;
284 } else {
285 return(-1);
286 }
287
288 if ((value==1)||(value==2)||(value==3)||(value==12)) {
289 a1_times2=a_times2Tbl[0];
290 if (_reinit) {
291 _oldOutputLow[0]=y2_Table[0];
292 _oldOutputLow[1]=0;
293 }
294 } else if ((value==4)||(value==5)||(value==6)||(value==13)) {
295 a1_times2=a_times2Tbl[1];
296 if (_reinit) {
297 _oldOutputLow[0]=y2_Table[1];
298 _oldOutputLow[1]=0;
299 }
300 } else if ((value==7)||(value==8)||(value==9)||(value==14)) {
301 a1_times2=a_times2Tbl[2];
302 if (_reinit) {
303 _oldOutputLow[0]=y2_Table[2];
304 _oldOutputLow[1]=0;
305 }
306 } else if ((value==10)||(value==0)||(value==11)||(value==15)) {
307 a1_times2=a_times2Tbl[3];
308 if (_reinit) {
309 _oldOutputLow[0]=y2_Table[3];
310 _oldOutputLow[1]=0;
311 }
312 }
313 if ((value==1)||(value==4)||(value==7)||(value==10)) {
314 a2_times2=a_times2Tbl[4];
315 if (_reinit) {
316 _oldOutputHigh[0]=y2_Table[4];
317 _oldOutputHigh[1]=0;
318 _reinit=false;
319 }
320 } else if ((value==2)||(value==5)||(value==8)||(value==0)) {
321 a2_times2=a_times2Tbl[5];
322 if (_reinit) {
323 _oldOutputHigh[0]=y2_Table[5];
324 _oldOutputHigh[1]=0;
325 _reinit=false;
326 }
327 } else if ((value==3)||(value==6)||(value==9)||(value==11)) {
328 a2_times2=a_times2Tbl[6];
329 if (_reinit) {
330 _oldOutputHigh[0]=y2_Table[6];
331 _oldOutputHigh[1]=0;
332 _reinit=false;
333 }
334 } else if ((value==12)||(value==13)||(value==14)||(value==15)) {
335 a2_times2=a_times2Tbl[7];
336 if (_reinit) {
337 _oldOutputHigh[0]=y2_Table[7];
338 _oldOutputHigh[1]=0;
339 _reinit=false;
340 }
341 }
342
343 return (DtmfFix_generateSignal(a1_times2,
344 a2_times2,
345 volume,
346 decoded,
347 frameLen));
348}
349
350WebRtc_Word16
351DtmfInband::DtmfFix_generateSignal(const WebRtc_Word16 a1_times2,
352 const WebRtc_Word16 a2_times2,
353 const WebRtc_Word16 volume,
354 WebRtc_Word16 *signal,
355 const WebRtc_Word16 length)
356{
357 int i;
358
359 /* Generate Signal */
360 for (i=0;i<length;i++) {
361 WebRtc_Word32 tempVal;
362 WebRtc_Word16 tempValLow, tempValHigh;
363
364 /* Use recursion formula y[n] = a*2*y[n-1] - y[n-2] */
365 tempValLow = (WebRtc_Word16)(((( (WebRtc_Word32)(a1_times2 *
366 _oldOutputLow[1])) + 8192) >> 14) - _oldOutputLow[0]);
367 tempValHigh = (WebRtc_Word16)(((( (WebRtc_Word32)(a2_times2 *
368 _oldOutputHigh[1])) + 8192) >> 14) - _oldOutputHigh[0]);
369
370 /* Update memory */
371 _oldOutputLow[0]=_oldOutputLow[1];
372 _oldOutputLow[1]=tempValLow;
373 _oldOutputHigh[0]=_oldOutputHigh[1];
374 _oldOutputHigh[1]=tempValHigh;
375
376 tempVal = (WebRtc_Word32)(kDtmfAmpLow * tempValLow) +
377 (WebRtc_Word32)(kDtmfAmpHigh * tempValHigh);
378
379 /* Norm the signal to Q14 */
380 tempVal=(tempVal+16384)>>15;
381
382 /* Scale the signal to correct dbM0 value */
383 signal[i]=(WebRtc_Word16)((tempVal*Dtmf_dBm0kHz[volume]+8192)>>14);
384 }
385
386 return(0);
387}
388
389} // namespace webrtc