1 /*
2 * silencedetect.cxx
3 *
4 * Open Phone Abstraction Library (OPAL)
5 * Formally known as the Open H323 project.
6 *
7 * Copyright (c) 2001 Post Increment
8 *
9 * The contents of this file are subject to the Mozilla Public License
10 * Version 1.0 (the "License"); you may not use this file except in
11 * compliance with the License. You may obtain a copy of the License at
12 * http://www.mozilla.org/MPL/
13 *
14 * Software distributed under the License is distributed on an "AS IS"
15 * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
16 * the License for the specific language governing rights and limitations
17 * under the License.
18 *
19 * The Original Code is Open Phone Abstraction Library.
20 *
21 * The Initial Developer of the Original Code is Equivalence Pty. Ltd.
22 *
23 * Contributor(s): ______________________________________.
24 *
25 * $Revision: 23735 $
26 * $Author: rjongbloed $
27 * $Date: 2009-10-30 21:20:26 -0500 (Fri, 30 Oct 2009) $
28 */
29
30 #include <ptlib.h>
31
32 #ifdef __GNUC__
33 #pragma implementation "silencedetect.h"
34 #endif
35 #include <opal/buildopts.h>
36
37 #include <codec/silencedetect.h>
38 #include <opal/patch.h>
39
40 #define new PNEW
41
42
43 extern "C" {
44 unsigned char linear2ulaw(int pcm_val);
45 int ulaw2linear(unsigned char u_val);
46 };
47
48
operator <<(ostream & strm,OpalSilenceDetector::Mode mode)49 ostream & operator<<(ostream & strm, OpalSilenceDetector::Mode mode)
50 {
51 static const char * const names[OpalSilenceDetector::NumModes] = {
52 "NoSilenceDetection",
53 "FixedSilenceDetection",
54 "AdaptiveSilenceDetection"
55 };
56
57 if (mode >= 0 && mode < OpalSilenceDetector::NumModes && names[mode] != NULL)
58 strm << names[mode];
59 else
60 strm << "OpalSilenceDetector::Modes<" << mode << '>';
61 return strm;
62 }
63
64
65 ///////////////////////////////////////////////////////////////////////////////
66
OpalSilenceDetector(const Params & theParam)67 OpalSilenceDetector::OpalSilenceDetector(const Params & theParam)
68 #ifdef _MSC_VER
69 #pragma warning(disable:4355)
70 #endif
71 : receiveHandler(PCREATE_NOTIFIER(ReceivedPacket)),
72 #ifdef _MSC_VER
73 #pragma warning(default:4355)
74 #endif
75 clockRate (8000)
76 {
77 // Initialise the adaptive threshold variables.
78 SetParameters(theParam);
79
80 PTRACE(4, "Silence\tHandler created");
81 }
82
83
AdaptiveReset()84 void OpalSilenceDetector::AdaptiveReset()
85 {
86 // Initialise threshold level
87 levelThreshold = 0;
88
89 // Initialise the adaptive threshold variables.
90 signalMinimum = UINT_MAX;
91 silenceMaximum = 0;
92 signalReceivedTime = 0;
93 silenceReceivedTime = 0;
94
95 // Restart in silent mode
96 inTalkBurst = false;
97 lastTimestamp = 0;
98 receivedTime = 0;
99 }
100
101
SetParameters(const Params & newParam,const int rate)102 void OpalSilenceDetector::SetParameters(const Params & newParam, const int rate /*= 0*/)
103 {
104 PWaitAndSignal mutex(inUse);
105 if (rate)
106 clockRate = rate;
107 mode = newParam.m_mode;
108 signalDeadband = newParam.m_signalDeadband * clockRate / 1000;
109 silenceDeadband = newParam.m_silenceDeadband * clockRate / 1000;
110 adaptivePeriod = newParam.m_adaptivePeriod * clockRate / 1000;
111 if (mode == FixedSilenceDetection)
112 levelThreshold = newParam.m_threshold;// note: this value compared to uLaw encoded signal level
113 else
114 AdaptiveReset();
115
116 PTRACE(4, "Silence\tParameters set: "
117 "mode=" << mode << ", "
118 "threshold=" << levelThreshold << ", "
119 "silencedb=" << silenceDeadband << " samples, "
120 "signaldb=" << signalDeadband << " samples, "
121 "period=" << adaptivePeriod << " samples");
122 }
123
124
SetClockRate(const int rate)125 void OpalSilenceDetector::SetClockRate(const int rate)
126 {
127 PWaitAndSignal mutex(inUse);
128 signalDeadband = signalDeadband * 1000 / clockRate * rate / 1000;
129 silenceDeadband = silenceDeadband * 1000 / clockRate * rate / 1000;
130 adaptivePeriod = adaptivePeriod * 1000 / clockRate * rate / 1000;
131 clockRate = rate;
132 if (mode == AdaptiveSilenceDetection)
133 AdaptiveReset();
134 }
135
136
GetStatus(PBoolean * isInTalkBurst,unsigned * currentThreshold) const137 OpalSilenceDetector::Mode OpalSilenceDetector::GetStatus(PBoolean * isInTalkBurst,
138 unsigned * currentThreshold) const
139 {
140 if (isInTalkBurst != NULL)
141 *isInTalkBurst = inTalkBurst;
142
143 if (currentThreshold != NULL)
144 *currentThreshold = ulaw2linear((BYTE)(levelThreshold ^ 0xff));
145
146 return mode;
147 }
148
149
ReceivedPacket(RTP_DataFrame & frame,INT)150 void OpalSilenceDetector::ReceivedPacket(RTP_DataFrame & frame, INT)
151 {
152 // Already silent
153 if (frame.GetPayloadSize() == 0)
154 return;
155
156 PWaitAndSignal mutex(inUse);
157
158 // Can never have silence if NoSilenceDetection
159 if (mode == NoSilenceDetection)
160 return;
161
162 unsigned thisTimestamp = frame.GetTimestamp();
163 if (lastTimestamp == 0) {
164 lastTimestamp = thisTimestamp;
165 return;
166 }
167
168 unsigned timeSinceLastFrame = thisTimestamp - lastTimestamp;
169 lastTimestamp = thisTimestamp;
170
171 // Average is absolute value up to 32767
172 unsigned level = GetAverageSignalLevel(frame.GetPayloadPtr(), frame.GetPayloadSize());
173
174 // Can never have average signal level that high, this indicates that the
175 // hardware cannot do silence detection.
176 if (level == UINT_MAX)
177 return;
178
179 // Convert to a logarithmic scale - use uLaw which is complemented
180 level = linear2ulaw(level) ^ 0xff;
181
182 // Now if signal level above threshold we are "talking"
183 PBoolean haveSignal = level > levelThreshold;
184
185 // If no change ie still talking or still silent, reset frame counter
186 if (inTalkBurst == haveSignal)
187 receivedTime = 0;
188 else {
189 receivedTime += timeSinceLastFrame;
190 // If have had enough consecutive frames talking/silent, swap modes.
191 if (receivedTime >= (inTalkBurst ? silenceDeadband : signalDeadband)) {
192 inTalkBurst = !inTalkBurst;
193 PTRACE(4, "Silence\tDetector transition: "
194 << (inTalkBurst ? "Talk" : "Silent")
195 << " level=" << level << " threshold=" << levelThreshold);
196
197 // If we had talk/silence transition restart adaptive threshold measurements
198 signalMinimum = UINT_MAX;
199 silenceMaximum = 0;
200 signalReceivedTime = 0;
201 silenceReceivedTime = 0;
202
203 // If we just have moved to sending a talk burst, set the RTP marker
204 if (inTalkBurst)
205 frame.SetMarker(true);
206 }
207 }
208
209 if (mode == FixedSilenceDetection) {
210 if (!inTalkBurst)
211 frame.SetPayloadSize(0); // Not in talk burst so silence the frame
212 return;
213 }
214
215 // Adaptive silence detection
216 if (levelThreshold == 0) {
217 if (level > 1) {
218 // Bootstrap condition, use first frame level as silence level
219 levelThreshold = level/2;
220 PTRACE(4, "Silence\tThreshold initialised to: " << levelThreshold);
221 }
222 // inTalkBurst always PFalse here, so return silent
223 frame.SetPayloadSize(0);
224 return;
225 }
226
227 // Count the number of silent and signal frames and calculate min/max
228 if (haveSignal) {
229 if (level < signalMinimum)
230 signalMinimum = level;
231 signalReceivedTime=signalReceivedTime+timeSinceLastFrame;
232 }
233 else {
234 if (level > silenceMaximum)
235 silenceMaximum = level;
236 silenceReceivedTime=silenceReceivedTime+timeSinceLastFrame;
237 }
238
239 // See if we have had enough frames to look at proportions of silence/signal
240 if ((signalReceivedTime + silenceReceivedTime) > adaptivePeriod) {
241
242 /* Now we have had a period of time to look at some average values we can
243 make some adjustments to the threshold. There are four cases:
244 */
245 if (signalReceivedTime >= adaptivePeriod) {
246 /* If every frame was noisy, move threshold up. Don't want to move too
247 fast so only go a quarter of the way to minimum signal value over the
248 period. This avoids oscillations, and time will continue to make the
249 level go up if there really is a lot of background noise.
250 */
251 int delta = (signalMinimum - levelThreshold)/4;
252 if (delta != 0) {
253 levelThreshold += delta;
254 PTRACE(4, "Silence\tThreshold increased to: " << levelThreshold);
255 }
256 }
257 else if (silenceReceivedTime >= adaptivePeriod) {
258 /* If every frame was silent, move threshold down. Again do not want to
259 move too quickly, but we do want it to move faster down than up, so
260 move to halfway to maximum value of the quiet period. As a rule the
261 lower the threshold the better as it would improve response time to
262 the start of a talk burst.
263 */
264 unsigned newThreshold = (levelThreshold + silenceMaximum)/2 + 1;
265 if (levelThreshold != newThreshold) {
266 levelThreshold = newThreshold;
267 PTRACE(4, "Silence\tThreshold decreased to: " << levelThreshold);
268 }
269 }
270 else if (signalReceivedTime > silenceReceivedTime) {
271 /* We haven't got a definitive silent or signal period, but if we are
272 constantly hovering at the threshold and have more signal than
273 silence we should creep up a bit.
274 */
275 levelThreshold++;
276 PTRACE(4, "Silence\tThreshold incremented to: " << levelThreshold
277 << " signal=" << signalReceivedTime << ' ' << signalMinimum
278 << " silence=" << silenceReceivedTime << ' ' << silenceMaximum);
279 }
280
281 signalMinimum = UINT_MAX;
282 silenceMaximum = 0;
283 signalReceivedTime = 0;
284 silenceReceivedTime = 0;
285 }
286
287 if (!inTalkBurst)
288 frame.SetPayloadSize(0); // Not in talk burst so silence the frame
289 }
290
291
292 /////////////////////////////////////////////////////////////////////////////
293
GetAverageSignalLevel(const BYTE * buffer,PINDEX size)294 unsigned OpalPCM16SilenceDetector::GetAverageSignalLevel(const BYTE * buffer, PINDEX size)
295 {
296 // Calculate the average signal level of this frame
297 int sum = 0;
298 PINDEX samples = size/2;
299 const short * pcm = (const short *)buffer;
300 const short * end = pcm + samples;
301 while (pcm != end) {
302 if (*pcm < 0)
303 sum -= *pcm++;
304 else
305 sum += *pcm++;
306 }
307
308 return sum/samples;
309 }
310
311
312 /////////////////////////////////////////////////////////////////////////////
313