blob: 7ae24db22d523292a71d69b5f2a45be98a0effd2 [file] [log] [blame]
andrew@webrtc.orgb015cbe2012-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
pbos@webrtc.org471ae722013-05-21 13:52:32 +000011#include "webrtc/voice_engine/dtmf_inband.h"
andrew@webrtc.orgb015cbe2012-10-22 18:19:23 +000012
pbos@webrtc.org3f45c2e2013-08-05 16:22:53 +000013#include <assert.h>
14
pbos@webrtc.org471ae722013-05-21 13:52:32 +000015#include "webrtc/system_wrappers/interface/critical_section_wrapper.h"
16#include "webrtc/system_wrappers/interface/trace.h"
andrew@webrtc.orgb015cbe2012-10-22 18:19:23 +000017
18namespace webrtc {
19
pbos@webrtc.org54f03bc2013-04-09 10:09:10 +000020const int16_t Dtmf_a_times2Tab8Khz[8]=
andrew@webrtc.orgb015cbe2012-10-22 18:19:23 +000021{
22 27978, 26956, 25701, 24219,
23 19073, 16325, 13085, 9314
24};
25
pbos@webrtc.org54f03bc2013-04-09 10:09:10 +000026const int16_t Dtmf_a_times2Tab16Khz[8]=
andrew@webrtc.orgb015cbe2012-10-22 18:19:23 +000027{
28 31548, 31281, 30951, 30556,
29 29144, 28361, 27409, 26258
30};
31
pbos@webrtc.org54f03bc2013-04-09 10:09:10 +000032const int16_t Dtmf_a_times2Tab32Khz[8]=
andrew@webrtc.orgb015cbe2012-10-22 18:19:23 +000033{
34 32462,32394, 32311, 32210, 31849, 31647, 31400, 31098
35};
36
37// Second table is sin(2*pi*f/fs) in Q14
38
pbos@webrtc.org54f03bc2013-04-09 10:09:10 +000039const int16_t Dtmf_ym2Tab8Khz[8]=
andrew@webrtc.orgb015cbe2012-10-22 18:19:23 +000040{
41 8527, 9315, 10163, 11036,
42 13322, 14206, 15021, 15708
43};
44
pbos@webrtc.org54f03bc2013-04-09 10:09:10 +000045const int16_t Dtmf_ym2Tab16Khz[8]=
andrew@webrtc.orgb015cbe2012-10-22 18:19:23 +000046{
47 4429, 4879, 5380, 5918,
48 7490, 8207, 8979, 9801
49};
50
pbos@webrtc.org54f03bc2013-04-09 10:09:10 +000051const int16_t Dtmf_ym2Tab32Khz[8]=
andrew@webrtc.orgb015cbe2012-10-22 18:19:23 +000052{
53 2235, 2468, 2728, 3010, 3853, 4249, 4685, 5164
54};
55
pbos@webrtc.org54f03bc2013-04-09 10:09:10 +000056const int16_t Dtmf_dBm0kHz[37]=
andrew@webrtc.orgb015cbe2012-10-22 18:19:23 +000057{
58 16141, 14386, 12821, 11427, 10184, 9077,
59 8090, 7210, 6426, 5727, 5104, 4549,
60 4054, 3614, 3221, 2870, 2558, 2280,
61 2032, 1811, 1614, 1439, 1282, 1143,
62 1018, 908, 809, 721, 643, 573,
63 510, 455, 405, 361, 322, 287,
64 256
65};
66
67
pbos@webrtc.orgca7a9a22013-05-14 08:31:39 +000068DtmfInband::DtmfInband(int32_t id) :
andrew@webrtc.orgb015cbe2012-10-22 18:19:23 +000069 _critSect(*CriticalSectionWrapper::CreateCriticalSection()),
70 _id(id),
71 _outputFrequencyHz(8000),
72 _frameLengthSamples(0),
73 _remainingSamples(0),
74 _eventCode(0),
75 _attenuationDb(0),
76 _lengthMs(0),
77 _reinit(true),
78 _playing(false),
79 _delaySinceLastToneMS(1000)
80{
81 memset(_oldOutputLow, 0, sizeof(_oldOutputLow));
82 memset(_oldOutputHigh, 0, sizeof(_oldOutputHigh));
83}
84
85DtmfInband::~DtmfInband()
86{
87 delete &_critSect;
88}
89
90int
pbos@webrtc.orgca7a9a22013-05-14 08:31:39 +000091DtmfInband::SetSampleRate(uint16_t frequency)
andrew@webrtc.orgb015cbe2012-10-22 18:19:23 +000092{
93 if (frequency != 8000 &&
94 frequency != 16000 &&
95 frequency != 32000)
96 {
97 // invalid sample rate
98 assert(false);
99 return -1;
100 }
101 _outputFrequencyHz = frequency;
102 return 0;
103}
104
105int
pbos@webrtc.org54f03bc2013-04-09 10:09:10 +0000106DtmfInband::GetSampleRate(uint16_t& frequency)
andrew@webrtc.orgb015cbe2012-10-22 18:19:23 +0000107{
108 frequency = _outputFrequencyHz;
109 return 0;
110}
111
112void
113DtmfInband::Init()
114{
115 _remainingSamples = 0;
116 _frameLengthSamples = 0;
117 _eventCode = 0;
118 _attenuationDb = 0;
119 _lengthMs = 0;
120 _reinit = true;
121 _oldOutputLow[0] = 0;
122 _oldOutputLow[1] = 0;
123 _oldOutputHigh[0] = 0;
124 _oldOutputHigh[1] = 0;
125 _delaySinceLastToneMS = 1000;
126}
127
128int
pbos@webrtc.orgca7a9a22013-05-14 08:31:39 +0000129DtmfInband::AddTone(uint8_t eventCode,
pbos@webrtc.org54f03bc2013-04-09 10:09:10 +0000130 int32_t lengthMs,
131 int32_t attenuationDb)
andrew@webrtc.orgb015cbe2012-10-22 18:19:23 +0000132{
133 CriticalSectionScoped lock(&_critSect);
134
135 if (attenuationDb > 36 || eventCode > 15)
136 {
137 assert(false);
138 return -1;
139 }
140
141 if (IsAddingTone())
142 {
143 WEBRTC_TRACE(kTraceWarning, kTraceVoice, VoEId(_id,-1),
144 "DtmfInband::AddTone() new tone interrupts ongoing tone");
145 }
146
147 ReInit();
148
pbos@webrtc.org54f03bc2013-04-09 10:09:10 +0000149 _frameLengthSamples = static_cast<int16_t> (_outputFrequencyHz / 100);
150 _eventCode = static_cast<int16_t> (eventCode);
151 _attenuationDb = static_cast<int16_t> (attenuationDb);
152 _remainingSamples = static_cast<int32_t>
andrew@webrtc.orgb015cbe2012-10-22 18:19:23 +0000153 (lengthMs * (_outputFrequencyHz / 1000));
154 _lengthMs = lengthMs;
155
156 return 0;
157}
158
159int
160DtmfInband::ResetTone()
161{
162 CriticalSectionScoped lock(&_critSect);
163
164 ReInit();
165
pbos@webrtc.org54f03bc2013-04-09 10:09:10 +0000166 _frameLengthSamples = static_cast<int16_t> (_outputFrequencyHz / 100);
167 _remainingSamples = static_cast<int32_t>
andrew@webrtc.orgb015cbe2012-10-22 18:19:23 +0000168 (_lengthMs * (_outputFrequencyHz / 1000));
169
170 return 0;
171}
172
173int
pbos@webrtc.orgca7a9a22013-05-14 08:31:39 +0000174DtmfInband::StartTone(uint8_t eventCode,
pbos@webrtc.org54f03bc2013-04-09 10:09:10 +0000175 int32_t attenuationDb)
andrew@webrtc.orgb015cbe2012-10-22 18:19:23 +0000176{
177 CriticalSectionScoped lock(&_critSect);
178
179 if (attenuationDb > 36 || eventCode > 15)
180 {
181 assert(false);
182 return -1;
183 }
184
185 if (IsAddingTone())
186 {
187 return -1;
188 }
189
190 ReInit();
191
pbos@webrtc.org54f03bc2013-04-09 10:09:10 +0000192 _frameLengthSamples = static_cast<int16_t> (_outputFrequencyHz / 100);
193 _eventCode = static_cast<int16_t> (eventCode);
194 _attenuationDb = static_cast<int16_t> (attenuationDb);
andrew@webrtc.orgb015cbe2012-10-22 18:19:23 +0000195 _playing = true;
196
197 return 0;
198}
199
200int
201DtmfInband::StopTone()
202{
203 CriticalSectionScoped lock(&_critSect);
204
205 if (!_playing)
206 {
207 return 0;
208 }
209
210 _playing = false;
211
212 return 0;
213}
214
215// Shall be called between tones
216void
217DtmfInband::ReInit()
218{
219 _reinit = true;
220}
221
222bool
223DtmfInband::IsAddingTone()
224{
225 CriticalSectionScoped lock(&_critSect);
226 return (_remainingSamples > 0 || _playing);
227}
228
229int
pbos@webrtc.org54f03bc2013-04-09 10:09:10 +0000230DtmfInband::Get10msTone(int16_t output[320],
231 uint16_t& outputSizeInSamples)
andrew@webrtc.orgb015cbe2012-10-22 18:19:23 +0000232{
233 CriticalSectionScoped lock(&_critSect);
234 if (DtmfFix_generate(output,
235 _eventCode,
236 _attenuationDb,
237 _frameLengthSamples,
238 _outputFrequencyHz) == -1)
239 {
240 return -1;
241 }
242 _remainingSamples -= _frameLengthSamples;
243 outputSizeInSamples = _frameLengthSamples;
244 _delaySinceLastToneMS = 0;
245 return 0;
246}
247
248void
249DtmfInband::UpdateDelaySinceLastTone()
250{
251 _delaySinceLastToneMS += kDtmfFrameSizeMs;
252 // avoid wraparound
253 if (_delaySinceLastToneMS > (1<<30))
254 {
255 _delaySinceLastToneMS = 1000;
256 }
257}
258
pbos@webrtc.org54f03bc2013-04-09 10:09:10 +0000259uint32_t
andrew@webrtc.orgb015cbe2012-10-22 18:19:23 +0000260DtmfInband::DelaySinceLastTone() const
261{
262 return _delaySinceLastToneMS;
263}
264
pbos@webrtc.org54f03bc2013-04-09 10:09:10 +0000265int16_t
266DtmfInband::DtmfFix_generate(int16_t *decoded,
pbos@webrtc.orgca7a9a22013-05-14 08:31:39 +0000267 int16_t value,
268 int16_t volume,
269 int16_t frameLen,
270 int16_t fs)
andrew@webrtc.orgb015cbe2012-10-22 18:19:23 +0000271{
pbos@webrtc.org54f03bc2013-04-09 10:09:10 +0000272 const int16_t *a_times2Tbl;
273 const int16_t *y2_Table;
274 int16_t a1_times2 = 0, a2_times2 = 0;
andrew@webrtc.orgb015cbe2012-10-22 18:19:23 +0000275
276 if (fs==8000) {
277 a_times2Tbl=Dtmf_a_times2Tab8Khz;
278 y2_Table=Dtmf_ym2Tab8Khz;
279 } else if (fs==16000) {
280 a_times2Tbl=Dtmf_a_times2Tab16Khz;
281 y2_Table=Dtmf_ym2Tab16Khz;
282 } else if (fs==32000) {
283 a_times2Tbl=Dtmf_a_times2Tab32Khz;
284 y2_Table=Dtmf_ym2Tab32Khz;
285 } else {
286 return(-1);
287 }
288
289 if ((value==1)||(value==2)||(value==3)||(value==12)) {
290 a1_times2=a_times2Tbl[0];
291 if (_reinit) {
292 _oldOutputLow[0]=y2_Table[0];
293 _oldOutputLow[1]=0;
294 }
295 } else if ((value==4)||(value==5)||(value==6)||(value==13)) {
296 a1_times2=a_times2Tbl[1];
297 if (_reinit) {
298 _oldOutputLow[0]=y2_Table[1];
299 _oldOutputLow[1]=0;
300 }
301 } else if ((value==7)||(value==8)||(value==9)||(value==14)) {
302 a1_times2=a_times2Tbl[2];
303 if (_reinit) {
304 _oldOutputLow[0]=y2_Table[2];
305 _oldOutputLow[1]=0;
306 }
307 } else if ((value==10)||(value==0)||(value==11)||(value==15)) {
308 a1_times2=a_times2Tbl[3];
309 if (_reinit) {
310 _oldOutputLow[0]=y2_Table[3];
311 _oldOutputLow[1]=0;
312 }
313 }
314 if ((value==1)||(value==4)||(value==7)||(value==10)) {
315 a2_times2=a_times2Tbl[4];
316 if (_reinit) {
317 _oldOutputHigh[0]=y2_Table[4];
318 _oldOutputHigh[1]=0;
319 _reinit=false;
320 }
321 } else if ((value==2)||(value==5)||(value==8)||(value==0)) {
322 a2_times2=a_times2Tbl[5];
323 if (_reinit) {
324 _oldOutputHigh[0]=y2_Table[5];
325 _oldOutputHigh[1]=0;
326 _reinit=false;
327 }
328 } else if ((value==3)||(value==6)||(value==9)||(value==11)) {
329 a2_times2=a_times2Tbl[6];
330 if (_reinit) {
331 _oldOutputHigh[0]=y2_Table[6];
332 _oldOutputHigh[1]=0;
333 _reinit=false;
334 }
335 } else if ((value==12)||(value==13)||(value==14)||(value==15)) {
336 a2_times2=a_times2Tbl[7];
337 if (_reinit) {
338 _oldOutputHigh[0]=y2_Table[7];
339 _oldOutputHigh[1]=0;
340 _reinit=false;
341 }
342 }
343
344 return (DtmfFix_generateSignal(a1_times2,
345 a2_times2,
346 volume,
347 decoded,
348 frameLen));
349}
350
pbos@webrtc.org54f03bc2013-04-09 10:09:10 +0000351int16_t
pbos@webrtc.orgca7a9a22013-05-14 08:31:39 +0000352DtmfInband::DtmfFix_generateSignal(int16_t a1_times2,
353 int16_t a2_times2,
354 int16_t volume,
pbos@webrtc.org54f03bc2013-04-09 10:09:10 +0000355 int16_t *signal,
pbos@webrtc.orgca7a9a22013-05-14 08:31:39 +0000356 int16_t length)
andrew@webrtc.orgb015cbe2012-10-22 18:19:23 +0000357{
358 int i;
359
360 /* Generate Signal */
361 for (i=0;i<length;i++) {
pbos@webrtc.org54f03bc2013-04-09 10:09:10 +0000362 int32_t tempVal;
363 int16_t tempValLow, tempValHigh;
andrew@webrtc.orgb015cbe2012-10-22 18:19:23 +0000364
365 /* Use recursion formula y[n] = a*2*y[n-1] - y[n-2] */
pbos@webrtc.org54f03bc2013-04-09 10:09:10 +0000366 tempValLow = (int16_t)(((( (int32_t)(a1_times2 *
andrew@webrtc.orgb015cbe2012-10-22 18:19:23 +0000367 _oldOutputLow[1])) + 8192) >> 14) - _oldOutputLow[0]);
pbos@webrtc.org54f03bc2013-04-09 10:09:10 +0000368 tempValHigh = (int16_t)(((( (int32_t)(a2_times2 *
andrew@webrtc.orgb015cbe2012-10-22 18:19:23 +0000369 _oldOutputHigh[1])) + 8192) >> 14) - _oldOutputHigh[0]);
370
371 /* Update memory */
372 _oldOutputLow[0]=_oldOutputLow[1];
373 _oldOutputLow[1]=tempValLow;
374 _oldOutputHigh[0]=_oldOutputHigh[1];
375 _oldOutputHigh[1]=tempValHigh;
376
pbos@webrtc.org54f03bc2013-04-09 10:09:10 +0000377 tempVal = (int32_t)(kDtmfAmpLow * tempValLow) +
378 (int32_t)(kDtmfAmpHigh * tempValHigh);
andrew@webrtc.orgb015cbe2012-10-22 18:19:23 +0000379
380 /* Norm the signal to Q14 */
381 tempVal=(tempVal+16384)>>15;
382
383 /* Scale the signal to correct dbM0 value */
pbos@webrtc.org54f03bc2013-04-09 10:09:10 +0000384 signal[i]=(int16_t)((tempVal*Dtmf_dBm0kHz[volume]+8192)>>14);
andrew@webrtc.orgb015cbe2012-10-22 18:19:23 +0000385 }
386
387 return(0);
388}
389
390} // namespace webrtc