niklase@google.com | 470e71d | 2011-07-07 08:21:25 +0000 | [diff] [blame] | 1 | /* |
| 2 | * Copyright (c) 2011 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 | /* |
| 12 | * This file contains the DTMF tone generator and its parameters. |
| 13 | * |
| 14 | * A sinusoid is generated using the recursive oscillator model |
| 15 | * |
| 16 | * y[n] = sin(w*n + phi) = 2*cos(w) * y[n-1] - y[n-2] |
| 17 | * = a * y[n-1] - y[n-2] |
| 18 | * |
| 19 | * initialized with |
| 20 | * y[-2] = 0 |
| 21 | * y[-1] = sin(w) |
| 22 | * |
| 23 | * A DTMF signal is a combination of two sinusoids, depending |
| 24 | * on which event is sent (i.e, which key is pressed). The following |
| 25 | * table maps each key (event codes in parentheses) into two tones: |
| 26 | * |
| 27 | * 1209 Hz 1336 Hz 1477 Hz 1633 Hz |
| 28 | * 697 Hz 1 (ev. 1) 2 (ev. 2) 3 (ev. 3) A (ev. 12) |
| 29 | * 770 Hz 4 (ev. 4) 5 (ev. 5) 6 (ev. 6) B (ev. 13) |
| 30 | * 852 Hz 7 (ev. 7) 8 (ev. 8) 9 (ev. 9) C (ev. 14) |
| 31 | * 941 Hz * (ev. 10) 0 (ev. 0) # (ev. 11) D (ev. 15) |
| 32 | * |
| 33 | * The two tones are added to form the DTMF signal. |
| 34 | * |
| 35 | */ |
| 36 | |
| 37 | #include "dtmf_tonegen.h" |
| 38 | |
| 39 | #include "signal_processing_library.h" |
| 40 | |
| 41 | #include "neteq_error_codes.h" |
| 42 | |
| 43 | #ifdef NETEQ_ATEVENT_DECODE |
| 44 | /* Must compile NetEQ with DTMF support to enable the functionality */ |
| 45 | |
| 46 | /*******************/ |
| 47 | /* Constant tables */ |
| 48 | /*******************/ |
| 49 | |
| 50 | /* |
| 51 | * All tables corresponding to the oscillator model are organized so that |
| 52 | * the coefficients for a specific frequency is found in the same position |
| 53 | * in every table. The positions for the tones follow this layout: |
| 54 | * |
| 55 | * dummyVector[8] = |
| 56 | * { |
| 57 | * 697 Hz, 770 Hz, 852 Hz, 941 Hz, |
| 58 | * 1209 Hz, 1336 Hz, 1477 Hz, 1633 Hz |
| 59 | * }; |
| 60 | */ |
| 61 | |
| 62 | /* |
| 63 | * Tables for the constant a = 2*cos(w) = 2*cos(2*pi*f/fs) |
| 64 | * in the oscillator model, for 8, 16, 32 and 48 kHz sample rate. |
| 65 | * Table values in Q14. |
| 66 | */ |
| 67 | |
pbos@webrtc.org | 0946a56 | 2013-04-09 00:28:06 +0000 | [diff] [blame] | 68 | const int16_t WebRtcNetEQ_dtfm_aTbl8Khz[8] = |
niklase@google.com | 470e71d | 2011-07-07 08:21:25 +0000 | [diff] [blame] | 69 | { |
| 70 | 27980, 26956, 25701, 24219, |
| 71 | 19073, 16325, 13085, 9315 |
| 72 | }; |
| 73 | |
| 74 | #ifdef NETEQ_WIDEBAND |
pbos@webrtc.org | 0946a56 | 2013-04-09 00:28:06 +0000 | [diff] [blame] | 75 | const int16_t WebRtcNetEQ_dtfm_aTbl16Khz[8]= |
niklase@google.com | 470e71d | 2011-07-07 08:21:25 +0000 | [diff] [blame] | 76 | { |
| 77 | 31548, 31281, 30951, 30556, |
| 78 | 29144, 28361, 27409, 26258 |
| 79 | }; |
| 80 | #endif |
| 81 | |
| 82 | #ifdef NETEQ_32KHZ_WIDEBAND |
pbos@webrtc.org | 0946a56 | 2013-04-09 00:28:06 +0000 | [diff] [blame] | 83 | const int16_t WebRtcNetEQ_dtfm_aTbl32Khz[8]= |
niklase@google.com | 470e71d | 2011-07-07 08:21:25 +0000 | [diff] [blame] | 84 | { |
| 85 | 32462, 32394, 32311, 32210, |
| 86 | 31849, 31647, 31400, 31098 |
| 87 | }; |
| 88 | #endif |
| 89 | |
| 90 | #ifdef NETEQ_48KHZ_WIDEBAND |
pbos@webrtc.org | 0946a56 | 2013-04-09 00:28:06 +0000 | [diff] [blame] | 91 | const int16_t WebRtcNetEQ_dtfm_aTbl48Khz[8]= |
niklase@google.com | 470e71d | 2011-07-07 08:21:25 +0000 | [diff] [blame] | 92 | { |
| 93 | 32632, 32602, 32564, 32520, |
| 94 | 32359, 32268, 32157, 32022 |
| 95 | }; |
| 96 | #endif |
| 97 | |
| 98 | /* |
| 99 | * Initialization values y[-1] = sin(w) = sin(2*pi*f/fs), for 8, 16, 32 and 48 kHz sample rate. |
| 100 | * Table values in Q14. |
| 101 | */ |
| 102 | |
pbos@webrtc.org | 0946a56 | 2013-04-09 00:28:06 +0000 | [diff] [blame] | 103 | const int16_t WebRtcNetEQ_dtfm_yInitTab8Khz[8] = |
niklase@google.com | 470e71d | 2011-07-07 08:21:25 +0000 | [diff] [blame] | 104 | { |
| 105 | 8528, 9315, 10163, 11036, |
| 106 | 13323, 14206,15021, 15708 |
| 107 | }; |
| 108 | |
| 109 | #ifdef NETEQ_WIDEBAND |
pbos@webrtc.org | 0946a56 | 2013-04-09 00:28:06 +0000 | [diff] [blame] | 110 | const int16_t WebRtcNetEQ_dtfm_yInitTab16Khz[8]= |
niklase@google.com | 470e71d | 2011-07-07 08:21:25 +0000 | [diff] [blame] | 111 | { |
| 112 | 4429, 4879, 5380, 5918, |
| 113 | 7490, 8207, 8979, 9801 |
| 114 | }; |
| 115 | #endif |
| 116 | |
| 117 | #ifdef NETEQ_32KHZ_WIDEBAND |
pbos@webrtc.org | 0946a56 | 2013-04-09 00:28:06 +0000 | [diff] [blame] | 118 | const int16_t WebRtcNetEQ_dtfm_yInitTab32Khz[8]= |
niklase@google.com | 470e71d | 2011-07-07 08:21:25 +0000 | [diff] [blame] | 119 | { |
| 120 | 2235, 2468, 2728, 3010, |
| 121 | 3853, 4249, 4685, 5164 |
| 122 | }; |
| 123 | #endif |
| 124 | |
| 125 | #ifdef NETEQ_48KHZ_WIDEBAND |
pbos@webrtc.org | 0946a56 | 2013-04-09 00:28:06 +0000 | [diff] [blame] | 126 | const int16_t WebRtcNetEQ_dtfm_yInitTab48Khz[8]= |
niklase@google.com | 470e71d | 2011-07-07 08:21:25 +0000 | [diff] [blame] | 127 | { |
| 128 | 1493, 1649, 1823, 2013, |
| 129 | 2582, 2851, 3148, 3476 |
| 130 | }; |
| 131 | #endif |
| 132 | |
| 133 | /* Volume in dBm0 from 0 to -63, where 0 is the first table entry. |
| 134 | Everything below -36 is discarded, wherefore the table stops at -36. |
| 135 | Table entries are in Q14. |
| 136 | */ |
| 137 | |
pbos@webrtc.org | 0946a56 | 2013-04-09 00:28:06 +0000 | [diff] [blame] | 138 | const int16_t WebRtcNetEQ_dtfm_dBm0[37] = { 16141, 14386, 12821, 11427, 10184, 9077, 8090, |
niklase@google.com | 470e71d | 2011-07-07 08:21:25 +0000 | [diff] [blame] | 139 | 7210, 6426, 5727, 5104, 4549, 4054, 3614, |
| 140 | 3221, 2870, 2558, 2280, 2032, 1811, 1614, |
| 141 | 1439, 1282, 1143, 1018, 908, 809, 721, 643, |
| 142 | 573, 510, 455, 405, 361, 322, 287, 256 }; |
| 143 | |
| 144 | /**************************************************************************** |
| 145 | * WebRtcNetEQ_DTMFGenerate(...) |
| 146 | * |
| 147 | * Generate 10 ms DTMF signal according to input parameters. |
| 148 | * |
| 149 | * Input: |
| 150 | * - DTMFdecInst : DTMF instance |
| 151 | * - value : DTMF event number (0-15) |
| 152 | * - volume : Volume of generated signal (0-36) |
| 153 | * Volume is given in negative dBm0, i.e., volume == 0 |
| 154 | * means 0 dBm0 while volume == 36 mean -36 dBm0. |
| 155 | * - sampFreq : Sample rate in Hz |
| 156 | * |
| 157 | * Output: |
| 158 | * - signal : Pointer to vector where DTMF signal is stored; |
| 159 | * Vector must be at least sampFreq/100 samples long. |
| 160 | * - DTMFdecInst : Updated DTMF instance |
| 161 | * |
| 162 | * Return value : >0 - Number of samples written to signal |
| 163 | * : <0 - error |
| 164 | */ |
| 165 | |
pbos@webrtc.org | 0946a56 | 2013-04-09 00:28:06 +0000 | [diff] [blame] | 166 | int16_t WebRtcNetEQ_DTMFGenerate(dtmf_tone_inst_t *DTMFdecInst, int16_t value, |
| 167 | int16_t volume, int16_t *signal, |
| 168 | uint16_t sampFreq, int16_t extFrameLen) |
niklase@google.com | 470e71d | 2011-07-07 08:21:25 +0000 | [diff] [blame] | 169 | { |
pbos@webrtc.org | 0946a56 | 2013-04-09 00:28:06 +0000 | [diff] [blame] | 170 | const int16_t *aTbl; /* pointer to a-coefficient table */ |
| 171 | const int16_t *yInitTable; /* pointer to initialization value table */ |
| 172 | int16_t a1 = 0; /* a-coefficient for first tone (low tone) */ |
| 173 | int16_t a2 = 0; /* a-coefficient for second tone (high tone) */ |
niklase@google.com | 470e71d | 2011-07-07 08:21:25 +0000 | [diff] [blame] | 174 | int i; |
| 175 | int frameLen; /* number of samples to generate */ |
henrik.lundin@webrtc.org | f0effa1 | 2012-09-11 12:44:06 +0000 | [diff] [blame] | 176 | int lowIndex = 0; /* Default to avoid compiler warnings. */ |
| 177 | int highIndex = 4; /* Default to avoid compiler warnings. */ |
pbos@webrtc.org | 0946a56 | 2013-04-09 00:28:06 +0000 | [diff] [blame] | 178 | int32_t tempVal; |
| 179 | int16_t tempValLow; |
| 180 | int16_t tempValHigh; |
niklase@google.com | 470e71d | 2011-07-07 08:21:25 +0000 | [diff] [blame] | 181 | |
| 182 | /* Sanity check for volume */ |
| 183 | if ((volume < 0) || (volume > 36)) |
| 184 | { |
| 185 | return DTMF_DEC_PARAMETER_ERROR; |
| 186 | } |
| 187 | |
| 188 | /* Sanity check for extFrameLen */ |
| 189 | if (extFrameLen < -1) |
| 190 | { |
| 191 | return DTMF_DEC_PARAMETER_ERROR; |
| 192 | } |
| 193 | |
| 194 | /* Select oscillator coefficient tables based on sample rate */ |
| 195 | if (sampFreq == 8000) |
| 196 | { |
| 197 | aTbl = WebRtcNetEQ_dtfm_aTbl8Khz; |
| 198 | yInitTable = WebRtcNetEQ_dtfm_yInitTab8Khz; |
| 199 | frameLen = 80; |
| 200 | #ifdef NETEQ_WIDEBAND |
| 201 | } |
| 202 | else if (sampFreq == 16000) |
| 203 | { |
| 204 | aTbl = WebRtcNetEQ_dtfm_aTbl16Khz; |
| 205 | yInitTable = WebRtcNetEQ_dtfm_yInitTab16Khz; |
| 206 | frameLen = 160; |
| 207 | #endif |
| 208 | #ifdef NETEQ_32KHZ_WIDEBAND |
| 209 | } |
| 210 | else if (sampFreq == 32000) |
| 211 | { |
| 212 | aTbl = WebRtcNetEQ_dtfm_aTbl32Khz; |
| 213 | yInitTable = WebRtcNetEQ_dtfm_yInitTab32Khz; |
| 214 | frameLen = 320; |
| 215 | #endif |
| 216 | #ifdef NETEQ_48KHZ_WIDEBAND |
| 217 | } |
| 218 | else if (sampFreq == 48000) |
| 219 | { |
| 220 | aTbl = WebRtcNetEQ_dtfm_aTbl48Khz; |
| 221 | yInitTable = WebRtcNetEQ_dtfm_yInitTab48Khz; |
| 222 | frameLen = 480; |
| 223 | #endif |
| 224 | } |
| 225 | else |
| 226 | { |
| 227 | /* unsupported sample rate */ |
| 228 | return DTMF_GEN_UNKNOWN_SAMP_FREQ; |
| 229 | } |
| 230 | |
| 231 | if (extFrameLen >= 0) |
| 232 | { |
| 233 | frameLen = extFrameLen; |
| 234 | } |
| 235 | |
| 236 | /* select low frequency based on event value */ |
| 237 | switch (value) |
| 238 | { |
| 239 | case 1: |
| 240 | case 2: |
| 241 | case 3: |
| 242 | case 12: /* first row on keypad */ |
| 243 | { |
| 244 | lowIndex = 0; /* low frequency: 697 Hz */ |
| 245 | break; |
| 246 | } |
| 247 | case 4: |
| 248 | case 5: |
| 249 | case 6: |
| 250 | case 13: /* second row on keypad */ |
| 251 | { |
| 252 | lowIndex = 1; /* low frequency: 770 Hz */ |
| 253 | break; |
| 254 | } |
| 255 | case 7: |
| 256 | case 8: |
| 257 | case 9: |
| 258 | case 14: /* third row on keypad */ |
| 259 | { |
| 260 | lowIndex = 2; /* low frequency: 852 Hz */ |
| 261 | break; |
| 262 | } |
| 263 | case 0: |
| 264 | case 10: |
| 265 | case 11: |
| 266 | case 15: /* fourth row on keypad */ |
| 267 | { |
| 268 | lowIndex = 3; /* low frequency: 941 Hz */ |
| 269 | break; |
| 270 | } |
| 271 | default: |
| 272 | { |
| 273 | return DTMF_DEC_PARAMETER_ERROR; |
| 274 | } |
| 275 | } /* end switch */ |
| 276 | |
| 277 | /* select high frequency based on event value */ |
| 278 | switch (value) |
| 279 | { |
| 280 | case 1: |
| 281 | case 4: |
| 282 | case 7: |
| 283 | case 10: /* first column on keypad */ |
| 284 | { |
| 285 | highIndex = 4; /* high frequency: 1209 Hz */ |
| 286 | break; |
| 287 | } |
| 288 | case 2: |
| 289 | case 5: |
| 290 | case 8: |
| 291 | case 0: /* second column on keypad */ |
| 292 | { |
| 293 | highIndex = 5;/* high frequency: 1336 Hz */ |
| 294 | break; |
| 295 | } |
| 296 | case 3: |
| 297 | case 6: |
| 298 | case 9: |
| 299 | case 11: /* third column on keypad */ |
| 300 | { |
| 301 | highIndex = 6;/* high frequency: 1477 Hz */ |
| 302 | break; |
| 303 | } |
| 304 | case 12: |
| 305 | case 13: |
| 306 | case 14: |
| 307 | case 15: /* fourth column on keypad (special) */ |
| 308 | { |
| 309 | highIndex = 7;/* high frequency: 1633 Hz */ |
| 310 | break; |
| 311 | } |
niklase@google.com | 470e71d | 2011-07-07 08:21:25 +0000 | [diff] [blame] | 312 | } /* end switch */ |
| 313 | |
| 314 | /* select coefficients based on results from switches above */ |
| 315 | a1 = aTbl[lowIndex]; /* coefficient for first (low) tone */ |
| 316 | a2 = aTbl[highIndex]; /* coefficient for second (high) tone */ |
| 317 | |
| 318 | if (DTMFdecInst->reinit) |
| 319 | { |
| 320 | /* set initial values for the recursive model */ |
| 321 | DTMFdecInst->oldOutputLow[0] = yInitTable[lowIndex]; |
| 322 | DTMFdecInst->oldOutputLow[1] = 0; |
| 323 | DTMFdecInst->oldOutputHigh[0] = yInitTable[highIndex]; |
| 324 | DTMFdecInst->oldOutputHigh[1] = 0; |
| 325 | |
| 326 | /* reset reinit flag */ |
| 327 | DTMFdecInst->reinit = 0; |
| 328 | } |
| 329 | |
| 330 | /* generate signal sample by sample */ |
| 331 | for (i = 0; i < frameLen; i++) |
| 332 | { |
| 333 | |
| 334 | /* Use rescursion formula y[n] = a*y[n-1] - y[n-2] */ |
| 335 | tempValLow |
pbos@webrtc.org | 0946a56 | 2013-04-09 00:28:06 +0000 | [diff] [blame] | 336 | = (int16_t) (((WEBRTC_SPL_MUL_16_16(a1, DTMFdecInst->oldOutputLow[1]) |
niklase@google.com | 470e71d | 2011-07-07 08:21:25 +0000 | [diff] [blame] | 337 | + 8192) >> 14) - DTMFdecInst->oldOutputLow[0]); |
| 338 | tempValHigh |
pbos@webrtc.org | 0946a56 | 2013-04-09 00:28:06 +0000 | [diff] [blame] | 339 | = (int16_t) (((WEBRTC_SPL_MUL_16_16(a2, DTMFdecInst->oldOutputHigh[1]) |
niklase@google.com | 470e71d | 2011-07-07 08:21:25 +0000 | [diff] [blame] | 340 | + 8192) >> 14) - DTMFdecInst->oldOutputHigh[0]); |
| 341 | |
| 342 | /* Update recursion memory */ |
| 343 | DTMFdecInst->oldOutputLow[0] = DTMFdecInst->oldOutputLow[1]; |
| 344 | DTMFdecInst->oldOutputLow[1] = tempValLow; |
| 345 | DTMFdecInst->oldOutputHigh[0] = DTMFdecInst->oldOutputHigh[1]; |
| 346 | DTMFdecInst->oldOutputHigh[1] = tempValHigh; |
| 347 | |
| 348 | /* scale high tone with 32768 (15 left shifts) |
| 349 | and low tone with 23171 (3dB lower than high tone) */ |
| 350 | tempVal = WEBRTC_SPL_MUL_16_16(DTMF_AMP_LOW, tempValLow) |
pbos@webrtc.org | 0946a56 | 2013-04-09 00:28:06 +0000 | [diff] [blame] | 351 | + WEBRTC_SPL_LSHIFT_W32((int32_t)tempValHigh, 15); |
niklase@google.com | 470e71d | 2011-07-07 08:21:25 +0000 | [diff] [blame] | 352 | |
| 353 | /* Norm the signal to Q14 (with proper rounding) */ |
| 354 | tempVal = (tempVal + 16384) >> 15; |
| 355 | |
| 356 | /* Scale the signal to correct dbM0 value */ |
pbos@webrtc.org | 0946a56 | 2013-04-09 00:28:06 +0000 | [diff] [blame] | 357 | signal[i] = (int16_t) WEBRTC_SPL_RSHIFT_W32( |
niklase@google.com | 470e71d | 2011-07-07 08:21:25 +0000 | [diff] [blame] | 358 | (WEBRTC_SPL_MUL_16_16(tempVal, WebRtcNetEQ_dtfm_dBm0[volume]) |
| 359 | + 8192), 14); /* volume value is in Q14; use proper rounding */ |
| 360 | } |
| 361 | |
| 362 | return frameLen; |
| 363 | |
| 364 | } |
| 365 | |
| 366 | #endif /* NETEQ_ATEVENT_DECODE */ |
| 367 | |