diff options
author | andrew@webrtc.org <andrew@webrtc.org@4adac7df-926f-26a2-2b94-8c16560cd09d> | 2012-10-22 18:19:23 +0000 |
---|---|---|
committer | andrew@webrtc.org <andrew@webrtc.org@4adac7df-926f-26a2-2b94-8c16560cd09d> | 2012-10-22 18:19:23 +0000 |
commit | b015cbede88899f67a53fbbe581b02ce8e327949 (patch) | |
tree | 530a64a3cfdbbabacab974c183326517d49e761e /voice_engine/dtmf_inband.cc | |
download | webrtc-b015cbede88899f67a53fbbe581b02ce8e327949.tar.gz |
Move src/ -> webrtc/
TBR=niklas.enbom@webrtc.org
Review URL: https://webrtc-codereview.appspot.com/915006
git-svn-id: http://webrtc.googlecode.com/svn/trunk/webrtc@2963 4adac7df-926f-26a2-2b94-8c16560cd09d
Diffstat (limited to 'voice_engine/dtmf_inband.cc')
-rw-r--r-- | voice_engine/dtmf_inband.cc | 389 |
1 files changed, 389 insertions, 0 deletions
diff --git a/voice_engine/dtmf_inband.cc b/voice_engine/dtmf_inband.cc new file mode 100644 index 00000000..689bc543 --- /dev/null +++ b/voice_engine/dtmf_inband.cc @@ -0,0 +1,389 @@ +/* + * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "dtmf_inband.h" + +#include "critical_section_wrapper.h" +#include "trace.h" +#include <cassert> + +namespace webrtc { + +const WebRtc_Word16 Dtmf_a_times2Tab8Khz[8]= +{ + 27978, 26956, 25701, 24219, + 19073, 16325, 13085, 9314 +}; + +const WebRtc_Word16 Dtmf_a_times2Tab16Khz[8]= +{ + 31548, 31281, 30951, 30556, + 29144, 28361, 27409, 26258 +}; + +const WebRtc_Word16 Dtmf_a_times2Tab32Khz[8]= +{ + 32462,32394, 32311, 32210, 31849, 31647, 31400, 31098 +}; + +// Second table is sin(2*pi*f/fs) in Q14 + +const WebRtc_Word16 Dtmf_ym2Tab8Khz[8]= +{ + 8527, 9315, 10163, 11036, + 13322, 14206, 15021, 15708 +}; + +const WebRtc_Word16 Dtmf_ym2Tab16Khz[8]= +{ + 4429, 4879, 5380, 5918, + 7490, 8207, 8979, 9801 +}; + +const WebRtc_Word16 Dtmf_ym2Tab32Khz[8]= +{ + 2235, 2468, 2728, 3010, 3853, 4249, 4685, 5164 +}; + +const WebRtc_Word16 Dtmf_dBm0kHz[37]= +{ + 16141, 14386, 12821, 11427, 10184, 9077, + 8090, 7210, 6426, 5727, 5104, 4549, + 4054, 3614, 3221, 2870, 2558, 2280, + 2032, 1811, 1614, 1439, 1282, 1143, + 1018, 908, 809, 721, 643, 573, + 510, 455, 405, 361, 322, 287, + 256 +}; + + +DtmfInband::DtmfInband(const WebRtc_Word32 id) : + _critSect(*CriticalSectionWrapper::CreateCriticalSection()), + _id(id), + _outputFrequencyHz(8000), + _frameLengthSamples(0), + _remainingSamples(0), + _eventCode(0), + _attenuationDb(0), + _lengthMs(0), + _reinit(true), + _playing(false), + _delaySinceLastToneMS(1000) +{ + memset(_oldOutputLow, 0, sizeof(_oldOutputLow)); + memset(_oldOutputHigh, 0, sizeof(_oldOutputHigh)); +} + +DtmfInband::~DtmfInband() +{ + delete &_critSect; +} + +int +DtmfInband::SetSampleRate(const WebRtc_UWord16 frequency) +{ + if (frequency != 8000 && + frequency != 16000 && + frequency != 32000) + { + // invalid sample rate + assert(false); + return -1; + } + _outputFrequencyHz = frequency; + return 0; +} + +int +DtmfInband::GetSampleRate(WebRtc_UWord16& frequency) +{ + frequency = _outputFrequencyHz; + return 0; +} + +void +DtmfInband::Init() +{ + _remainingSamples = 0; + _frameLengthSamples = 0; + _eventCode = 0; + _attenuationDb = 0; + _lengthMs = 0; + _reinit = true; + _oldOutputLow[0] = 0; + _oldOutputLow[1] = 0; + _oldOutputHigh[0] = 0; + _oldOutputHigh[1] = 0; + _delaySinceLastToneMS = 1000; +} + +int +DtmfInband::AddTone(const WebRtc_UWord8 eventCode, + WebRtc_Word32 lengthMs, + WebRtc_Word32 attenuationDb) +{ + CriticalSectionScoped lock(&_critSect); + + if (attenuationDb > 36 || eventCode > 15) + { + assert(false); + return -1; + } + + if (IsAddingTone()) + { + WEBRTC_TRACE(kTraceWarning, kTraceVoice, VoEId(_id,-1), + "DtmfInband::AddTone() new tone interrupts ongoing tone"); + } + + ReInit(); + + _frameLengthSamples = static_cast<WebRtc_Word16> (_outputFrequencyHz / 100); + _eventCode = static_cast<WebRtc_Word16> (eventCode); + _attenuationDb = static_cast<WebRtc_Word16> (attenuationDb); + _remainingSamples = static_cast<WebRtc_Word32> + (lengthMs * (_outputFrequencyHz / 1000)); + _lengthMs = lengthMs; + + return 0; +} + +int +DtmfInband::ResetTone() +{ + CriticalSectionScoped lock(&_critSect); + + ReInit(); + + _frameLengthSamples = static_cast<WebRtc_Word16> (_outputFrequencyHz / 100); + _remainingSamples = static_cast<WebRtc_Word32> + (_lengthMs * (_outputFrequencyHz / 1000)); + + return 0; +} + +int +DtmfInband::StartTone(const WebRtc_UWord8 eventCode, + WebRtc_Word32 attenuationDb) +{ + CriticalSectionScoped lock(&_critSect); + + if (attenuationDb > 36 || eventCode > 15) + { + assert(false); + return -1; + } + + if (IsAddingTone()) + { + return -1; + } + + ReInit(); + + _frameLengthSamples = static_cast<WebRtc_Word16> (_outputFrequencyHz / 100); + _eventCode = static_cast<WebRtc_Word16> (eventCode); + _attenuationDb = static_cast<WebRtc_Word16> (attenuationDb); + _playing = true; + + return 0; +} + +int +DtmfInband::StopTone() +{ + CriticalSectionScoped lock(&_critSect); + + if (!_playing) + { + return 0; + } + + _playing = false; + + return 0; +} + +// Shall be called between tones +void +DtmfInband::ReInit() +{ + _reinit = true; +} + +bool +DtmfInband::IsAddingTone() +{ + CriticalSectionScoped lock(&_critSect); + return (_remainingSamples > 0 || _playing); +} + +int +DtmfInband::Get10msTone(WebRtc_Word16 output[320], + WebRtc_UWord16& outputSizeInSamples) +{ + CriticalSectionScoped lock(&_critSect); + if (DtmfFix_generate(output, + _eventCode, + _attenuationDb, + _frameLengthSamples, + _outputFrequencyHz) == -1) + { + return -1; + } + _remainingSamples -= _frameLengthSamples; + outputSizeInSamples = _frameLengthSamples; + _delaySinceLastToneMS = 0; + return 0; +} + +void +DtmfInband::UpdateDelaySinceLastTone() +{ + _delaySinceLastToneMS += kDtmfFrameSizeMs; + // avoid wraparound + if (_delaySinceLastToneMS > (1<<30)) + { + _delaySinceLastToneMS = 1000; + } +} + +WebRtc_UWord32 +DtmfInband::DelaySinceLastTone() const +{ + return _delaySinceLastToneMS; +} + +WebRtc_Word16 +DtmfInband::DtmfFix_generate(WebRtc_Word16 *decoded, + const WebRtc_Word16 value, + const WebRtc_Word16 volume, + const WebRtc_Word16 frameLen, + const WebRtc_Word16 fs) +{ + const WebRtc_Word16 *a_times2Tbl; + const WebRtc_Word16 *y2_Table; + WebRtc_Word16 a1_times2 = 0, a2_times2 = 0; + + if (fs==8000) { + a_times2Tbl=Dtmf_a_times2Tab8Khz; + y2_Table=Dtmf_ym2Tab8Khz; + } else if (fs==16000) { + a_times2Tbl=Dtmf_a_times2Tab16Khz; + y2_Table=Dtmf_ym2Tab16Khz; + } else if (fs==32000) { + a_times2Tbl=Dtmf_a_times2Tab32Khz; + y2_Table=Dtmf_ym2Tab32Khz; + } else { + return(-1); + } + + if ((value==1)||(value==2)||(value==3)||(value==12)) { + a1_times2=a_times2Tbl[0]; + if (_reinit) { + _oldOutputLow[0]=y2_Table[0]; + _oldOutputLow[1]=0; + } + } else if ((value==4)||(value==5)||(value==6)||(value==13)) { + a1_times2=a_times2Tbl[1]; + if (_reinit) { + _oldOutputLow[0]=y2_Table[1]; + _oldOutputLow[1]=0; + } + } else if ((value==7)||(value==8)||(value==9)||(value==14)) { + a1_times2=a_times2Tbl[2]; + if (_reinit) { + _oldOutputLow[0]=y2_Table[2]; + _oldOutputLow[1]=0; + } + } else if ((value==10)||(value==0)||(value==11)||(value==15)) { + a1_times2=a_times2Tbl[3]; + if (_reinit) { + _oldOutputLow[0]=y2_Table[3]; + _oldOutputLow[1]=0; + } + } + if ((value==1)||(value==4)||(value==7)||(value==10)) { + a2_times2=a_times2Tbl[4]; + if (_reinit) { + _oldOutputHigh[0]=y2_Table[4]; + _oldOutputHigh[1]=0; + _reinit=false; + } + } else if ((value==2)||(value==5)||(value==8)||(value==0)) { + a2_times2=a_times2Tbl[5]; + if (_reinit) { + _oldOutputHigh[0]=y2_Table[5]; + _oldOutputHigh[1]=0; + _reinit=false; + } + } else if ((value==3)||(value==6)||(value==9)||(value==11)) { + a2_times2=a_times2Tbl[6]; + if (_reinit) { + _oldOutputHigh[0]=y2_Table[6]; + _oldOutputHigh[1]=0; + _reinit=false; + } + } else if ((value==12)||(value==13)||(value==14)||(value==15)) { + a2_times2=a_times2Tbl[7]; + if (_reinit) { + _oldOutputHigh[0]=y2_Table[7]; + _oldOutputHigh[1]=0; + _reinit=false; + } + } + + return (DtmfFix_generateSignal(a1_times2, + a2_times2, + volume, + decoded, + frameLen)); +} + +WebRtc_Word16 +DtmfInband::DtmfFix_generateSignal(const WebRtc_Word16 a1_times2, + const WebRtc_Word16 a2_times2, + const WebRtc_Word16 volume, + WebRtc_Word16 *signal, + const WebRtc_Word16 length) +{ + int i; + + /* Generate Signal */ + for (i=0;i<length;i++) { + WebRtc_Word32 tempVal; + WebRtc_Word16 tempValLow, tempValHigh; + + /* Use recursion formula y[n] = a*2*y[n-1] - y[n-2] */ + tempValLow = (WebRtc_Word16)(((( (WebRtc_Word32)(a1_times2 * + _oldOutputLow[1])) + 8192) >> 14) - _oldOutputLow[0]); + tempValHigh = (WebRtc_Word16)(((( (WebRtc_Word32)(a2_times2 * + _oldOutputHigh[1])) + 8192) >> 14) - _oldOutputHigh[0]); + + /* Update memory */ + _oldOutputLow[0]=_oldOutputLow[1]; + _oldOutputLow[1]=tempValLow; + _oldOutputHigh[0]=_oldOutputHigh[1]; + _oldOutputHigh[1]=tempValHigh; + + tempVal = (WebRtc_Word32)(kDtmfAmpLow * tempValLow) + + (WebRtc_Word32)(kDtmfAmpHigh * tempValHigh); + + /* Norm the signal to Q14 */ + tempVal=(tempVal+16384)>>15; + + /* Scale the signal to correct dbM0 value */ + signal[i]=(WebRtc_Word16)((tempVal*Dtmf_dBm0kHz[volume]+8192)>>14); + } + + return(0); +} + +} // namespace webrtc |