1 /******************************************************************************\
2 * Copyright (c) 2004-2020
3 *
4 * Author(s):
5 * Volker Fischer
6 *
7 * Note: We are assuming here that put and get operations are secured by a mutex
8 * and accessing does not occur at the same time.
9 *
10 ******************************************************************************
11 *
12 * This program is free software; you can redistribute it and/or modify it under
13 * the terms of the GNU General Public License as published by the Free Software
14 * Foundation; either version 2 of the License, or (at your option) any later
15 * version.
16 *
17 * This program is distributed in the hope that it will be useful, but WITHOUT
18 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
19 * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
20 * details.
21 *
22 * You should have received a copy of the GNU General Public License along with
23 * this program; if not, write to the Free Software Foundation, Inc.,
24 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
25 *
26 \******************************************************************************/
27
28 #include "buffer.h"
29
30 /* Network buffer implementation **********************************************/
Init(const int iNewBlockSize,const int iNewNumBlocks,const bool bNUseSequenceNumber,const bool bPreserve)31 void CNetBuf::Init ( const int iNewBlockSize, const int iNewNumBlocks, const bool bNUseSequenceNumber, const bool bPreserve )
32 {
33 // store the sequence number activation flag
34 bUseSequenceNumber = bNUseSequenceNumber;
35
36 // in simulation mode the size is not changed during operation -> we do
37 // not have to implement special code for this case
38 // only enter the "preserve" branch, if object was already initialized
39 // and the block sizes are the same
40 if ( bPreserve && ( !bIsSimulation ) && bIsInitialized && ( iBlockSize == iNewBlockSize ) )
41 {
42 // extract all data from buffer in temporary storage
43 CVector<CVector<uint8_t>> vecvecTempMemory = vecvecMemory; // allocate worst case memory by copying
44
45 if ( !bNUseSequenceNumber )
46 {
47 int iPreviousDataCnt = 0;
48
49 while ( Get ( vecvecTempMemory[iPreviousDataCnt], iBlockSize ) )
50 {
51 iPreviousDataCnt++;
52 }
53
54 // now resize the buffer to the new size (buffer is empty after this operation)
55 Resize ( iNewNumBlocks, iNewBlockSize );
56
57 // copy the previous data back in the buffer (make sure we only copy as much
58 // data back as the new buffer size can hold)
59 int iDataCnt = 0;
60
61 while ( ( iDataCnt < iPreviousDataCnt ) && Put ( vecvecTempMemory[iDataCnt], iBlockSize ) )
62 {
63 iDataCnt++;
64 }
65 }
66 else
67 {
68 // store current complete buffer state in temporary memory
69 CVector<int> veciTempBlockValid ( iNumBlocksMemory );
70 const uint8_t iOldSequenceNumberAtGetPos = iSequenceNumberAtGetPos;
71 const int iOldNumBlocksMemory = iNumBlocksMemory;
72 const int iOldBlockGetPos = iBlockGetPos;
73 int iCurBlockPos = 0;
74
75 while ( iBlockGetPos < iNumBlocksMemory )
76 {
77 veciTempBlockValid[iCurBlockPos] = veciBlockValid[iBlockGetPos];
78 vecvecTempMemory[iCurBlockPos++] = vecvecMemory[iBlockGetPos++];
79 }
80
81 for ( iBlockGetPos = 0; iBlockGetPos < iOldBlockGetPos; iBlockGetPos++ )
82 {
83 veciTempBlockValid[iCurBlockPos] = veciBlockValid[iBlockGetPos];
84 vecvecTempMemory[iCurBlockPos++] = vecvecMemory[iBlockGetPos];
85 }
86
87 // now resize the buffer to the new size
88 Resize ( iNewNumBlocks, iNewBlockSize );
89
90 // write back the temporary data in new memory
91 iSequenceNumberAtGetPos = iOldSequenceNumberAtGetPos;
92 iBlockGetPos = 0; // per definition
93
94 for ( int iCurPos = 0; iCurPos < std::min ( iNewNumBlocks, iOldNumBlocksMemory ); iCurPos++ )
95 {
96 veciBlockValid[iCurPos] = veciTempBlockValid[iCurPos];
97 vecvecMemory[iCurPos] = vecvecTempMemory[iCurPos];
98 }
99 }
100 }
101 else
102 {
103 Resize ( iNewNumBlocks, iNewBlockSize );
104 }
105
106 // set initialized flag
107 bIsInitialized = true;
108 }
109
Resize(const int iNewNumBlocks,const int iNewBlockSize)110 void CNetBuf::Resize ( const int iNewNumBlocks, const int iNewBlockSize )
111 {
112 // allocate memory for actual data buffer
113 vecvecMemory.Init ( iNewNumBlocks );
114 veciBlockValid.Init ( iNewNumBlocks, 0 ); // initialize with zeros = invalid
115
116 if ( !bIsSimulation )
117 {
118 for ( int iBlock = 0; iBlock < iNewNumBlocks; iBlock++ )
119 {
120 vecvecMemory[iBlock].Init ( iNewBlockSize );
121 }
122 }
123
124 // init buffer pointers and buffer state (empty buffer) and store buffer properties
125 iBlockGetPos = 0;
126 iBlockPutPos = 0;
127 eBufState = BS_EMPTY;
128 iBlockSize = iNewBlockSize;
129 iNumBlocksMemory = iNewNumBlocks;
130 }
131
Put(const CVector<uint8_t> & vecbyData,int iInSize)132 bool CNetBuf::Put ( const CVector<uint8_t>& vecbyData, int iInSize )
133 {
134 // if the sequence number is used, we need a complete different way of applying
135 // the new network packet
136 if ( bUseSequenceNumber )
137 {
138 // check that the input size is a multiple of the block size
139 if ( ( iInSize % ( iBlockSize + iNumBytesSeqNum ) ) != 0 )
140 {
141 return false;
142 }
143
144 // to get the number of input blocks we assume that the number of bytes for
145 // the sequence number is much smaller than the number of coded audio bytes
146 const int iNumBlocks = /* floor */ ( iInSize / iBlockSize );
147
148 // copy new data in internal buffer
149 for ( int iBlock = 0; iBlock < iNumBlocks; iBlock++ )
150 {
151 // extract sequence number of current received block (per definition
152 // the sequence number is appended after the coded audio data)
153 const int iCurrentSequenceNumber = vecbyData[iBlock * ( iBlockSize + iNumBytesSeqNum ) + iBlockSize];
154
155 // calculate the sequence number difference and take care of wrap
156 int iSeqNumDiff = iCurrentSequenceNumber - static_cast<int> ( iSequenceNumberAtGetPos );
157
158 if ( iSeqNumDiff < -128 )
159 {
160 iSeqNumDiff += 256;
161 }
162 else if ( iSeqNumDiff >= 128 )
163 {
164 iSeqNumDiff -= 256;
165 }
166
167 // The 1-byte sequence number wraps around at a count of 256. So, if a packet is delayed
168 // further than this we cannot detect it. But it does not matter since such a packet is
169 // more than 100 ms delayed so we have a bad network situation anyway. Therefore we
170 // assume that the sequence number difference between the received and local counter is
171 // correct. The idea of the following code is that we always move our "buffer window" so
172 // that the received packet fits into the buffer. By doing this we are robust against
173 // sample rate offsets between client/server or buffer glitches in the audio driver since
174 // we adjust the window. The downside is that we never throw away single packets which arrive
175 // too late so we throw away valid packets when we move the "buffer window" to the delayed
176 // packet and then back to the correct place when the next normal packet is received. But
177 // tests showed that the new buffer strategy does not perform worse than the old jitter
178 // buffer which did not use any sequence number at all.
179 if ( iSeqNumDiff < 0 )
180 {
181 // the received packet comes too late so we shift the "buffer window" to the past
182 // until the received packet is the very first packet in the buffer
183 for ( int i = iSeqNumDiff; i < 0; i++ )
184 {
185 // insert an invalid block at the shifted position
186 veciBlockValid[iBlockGetPos] = 0; // invalidate
187
188 // we decrease the local sequence number and get position and take care of wrap
189 iSequenceNumberAtGetPos--;
190 iBlockGetPos--;
191
192 if ( iBlockGetPos < 0 )
193 {
194 iBlockGetPos += iNumBlocksMemory;
195 }
196 }
197
198 // insert the new packet at the beginning of the buffer since it was delayed
199 iBlockPutPos = iBlockGetPos;
200 }
201 else if ( iSeqNumDiff >= iNumBlocksMemory )
202 {
203 // the received packet comes too early so we move the "buffer window" in the
204 // future until the received packet is the last packet in the buffer
205 for ( int i = 0; i < iSeqNumDiff - iNumBlocksMemory + 1; i++ )
206 {
207 // insert an invalid block at the shifted position
208 veciBlockValid[iBlockGetPos] = 0; // invalidate
209
210 // we increase the local sequence number and get position and take care of wrap
211 iSequenceNumberAtGetPos++;
212 iBlockGetPos++;
213
214 if ( iBlockGetPos >= iNumBlocksMemory )
215 {
216 iBlockGetPos -= iNumBlocksMemory;
217 }
218 }
219
220 // insert the new packet at the end of the buffer since it is too early (since
221 // we add an offset to the get position, we have to take care of wrapping)
222 iBlockPutPos = iBlockGetPos + iNumBlocksMemory - 1;
223
224 if ( iBlockPutPos >= iNumBlocksMemory )
225 {
226 iBlockPutPos -= iNumBlocksMemory;
227 }
228 }
229 else
230 {
231 // this is the regular case: the received packet fits into the buffer so
232 // we will write it at the correct position based on the sequence number
233 iBlockPutPos = iBlockGetPos + iSeqNumDiff;
234
235 if ( iBlockPutPos >= iNumBlocksMemory )
236 {
237 iBlockPutPos -= iNumBlocksMemory;
238 }
239 }
240
241 // for simulation buffer only update pointer, no data copying
242 if ( !bIsSimulation )
243 {
244 // copy one block of data in buffer
245 std::copy ( vecbyData.begin() + iBlock * ( iBlockSize + iNumBytesSeqNum ),
246 vecbyData.begin() + iBlock * ( iBlockSize + iNumBytesSeqNum ) + iBlockSize,
247 vecvecMemory[iBlockPutPos].begin() );
248 }
249
250 // valid packet added, set flag
251 veciBlockValid[iBlockPutPos] = 1;
252 }
253 }
254 else
255 {
256 // check if there is not enough space available and that the input size is a
257 // multiple of the block size
258 if ( ( GetAvailSpace() < iInSize ) || ( ( iInSize % iBlockSize ) != 0 ) )
259 {
260 return false;
261 }
262
263 // copy new data in internal buffer
264 const int iNumBlocks = iInSize / iBlockSize;
265
266 for ( int iBlock = 0; iBlock < iNumBlocks; iBlock++ )
267 {
268 // for simultion buffer only update pointer, no data copying
269 if ( !bIsSimulation )
270 {
271 // copy one block of data in buffer
272 std::copy ( vecbyData.begin() + iBlock * iBlockSize,
273 vecbyData.begin() + iBlock * iBlockSize + iBlockSize,
274 vecvecMemory[iBlockPutPos].begin() );
275 }
276
277 // set the put position one block further
278 iBlockPutPos++;
279
280 // take care about wrap around of put pointer
281 if ( iBlockPutPos == iNumBlocksMemory )
282 {
283 iBlockPutPos = 0;
284 }
285 }
286
287 // set buffer state flag
288 if ( iBlockPutPos == iBlockGetPos )
289 {
290 eBufState = BS_FULL;
291 }
292 else
293 {
294 eBufState = BS_OK;
295 }
296 }
297
298 return true;
299 }
300
Get(CVector<uint8_t> & vecbyData,const int iOutSize)301 bool CNetBuf::Get ( CVector<uint8_t>& vecbyData, const int iOutSize )
302 {
303 bool bReturn = true;
304
305 // check requested output size and available buffer data
306 if ( ( iOutSize == 0 ) || ( iOutSize != iBlockSize ) || ( GetAvailData() < iOutSize ) )
307 {
308 return false;
309 }
310
311 // if using sequence numbers, we do not use the block put position
312 // at all but only determine the state from the "valid block" indicator
313 if ( bUseSequenceNumber )
314 {
315 bReturn = ( veciBlockValid[iBlockGetPos] > 0 );
316
317 // invalidate the block we are now taking from the buffer
318 veciBlockValid[iBlockGetPos] = 0; // zero means invalid
319 }
320
321 // for simultion buffer or invalid block only update pointer, no data copying
322 if ( !bIsSimulation && bReturn )
323 {
324 // copy data from internal buffer in output buffer
325 std::copy ( vecvecMemory[iBlockGetPos].begin(), vecvecMemory[iBlockGetPos].begin() + iBlockSize, vecbyData.begin() );
326 }
327
328 // set the get position and sequence number one block further
329 iBlockGetPos++;
330 iSequenceNumberAtGetPos++; // wraps around automatically
331
332 // take care about wrap around of get pointer
333 if ( iBlockGetPos == iNumBlocksMemory )
334 {
335 iBlockGetPos = 0;
336 }
337
338 // set buffer state flag
339 if ( iBlockPutPos == iBlockGetPos )
340 {
341 eBufState = BS_EMPTY;
342 }
343 else
344 {
345 eBufState = BS_OK;
346 }
347
348 return bReturn;
349 }
350
GetAvailSpace() const351 int CNetBuf::GetAvailSpace() const
352 {
353 // calculate available space in buffer
354 int iAvBlocks = iBlockGetPos - iBlockPutPos;
355
356 // check for special case and wrap around
357 if ( iAvBlocks < 0 )
358 {
359 iAvBlocks += iNumBlocksMemory; // wrap around
360 }
361 else
362 {
363 if ( ( iAvBlocks == 0 ) && ( eBufState == BS_EMPTY ) )
364 {
365 iAvBlocks = iNumBlocksMemory;
366 }
367 }
368
369 return iAvBlocks * iBlockSize;
370 }
371
GetAvailData() const372 int CNetBuf::GetAvailData() const
373 {
374 // in case of using sequence numbers, we always return data from the
375 // buffer per definition
376 int iAvBlocks = iNumBlocksMemory;
377
378 if ( !bUseSequenceNumber )
379 {
380 // calculate available data in buffer
381 iAvBlocks = iBlockPutPos - iBlockGetPos;
382
383 // check for special case and wrap around
384 if ( iAvBlocks < 0 )
385 {
386 iAvBlocks += iNumBlocksMemory; // wrap around
387 }
388 else
389 {
390 if ( ( iAvBlocks == 0 ) && ( eBufState == BS_FULL ) )
391 {
392 iAvBlocks = iNumBlocksMemory;
393 }
394 }
395 }
396
397 return iAvBlocks * iBlockSize;
398 }
399
400 /* Network buffer with statistic calculations implementation ******************/
CNetBufWithStats()401 CNetBufWithStats::CNetBufWithStats() :
402 CNetBuf ( false ), // base class init: no simulation mode
403 iMaxStatisticCount ( MAX_STATISTIC_COUNT ),
404 bUseDoubleSystemFrameSize ( false ),
405 dAutoFilt_WightUpNormal ( IIR_WEIGTH_UP_NORMAL ),
406 dAutoFilt_WightDownNormal ( IIR_WEIGTH_DOWN_NORMAL ),
407 dAutoFilt_WightUpFast ( IIR_WEIGTH_UP_FAST ),
408 dAutoFilt_WightDownFast ( IIR_WEIGTH_DOWN_FAST ),
409 dErrorRateBound ( ERROR_RATE_BOUND ),
410 dUpMaxErrorBound ( UP_MAX_ERROR_BOUND )
411 {
412 // Define the sizes of the simulation buffers,
413 // must be NUM_STAT_SIMULATION_BUFFERS elements!
414 // Avoid the buffer length 1 because we do not have a solution for a
415 // sample rate offset correction. Caused by the jitter we usually get bad
416 // performance with just one buffer.
417 viBufSizesForSim[0] = 2;
418 viBufSizesForSim[1] = 3;
419 viBufSizesForSim[2] = 4;
420 viBufSizesForSim[3] = 5;
421 viBufSizesForSim[4] = 6;
422 viBufSizesForSim[5] = 7;
423 viBufSizesForSim[6] = 8;
424 viBufSizesForSim[7] = 9;
425 viBufSizesForSim[8] = 10;
426 viBufSizesForSim[9] = 11;
427
428 // set all simulation buffers in simulation mode
429 for ( int i = 0; i < NUM_STAT_SIMULATION_BUFFERS; i++ )
430 {
431 SimulationBuffer[i].SetIsSimulation ( true );
432 }
433 }
434
GetErrorRates(CVector<double> & vecErrRates,double & dLimit,double & dMaxUpLimit)435 void CNetBufWithStats::GetErrorRates ( CVector<double>& vecErrRates, double& dLimit, double& dMaxUpLimit )
436 {
437 // get all the averages of the error statistic
438 vecErrRates.Init ( NUM_STAT_SIMULATION_BUFFERS );
439
440 for ( int i = 0; i < NUM_STAT_SIMULATION_BUFFERS; i++ )
441 {
442 vecErrRates[i] = ErrorRateStatistic[i].GetAverage();
443 }
444
445 // get the limits for the decisions
446 dLimit = dErrorRateBound;
447 dMaxUpLimit = dUpMaxErrorBound;
448 }
449
Init(const int iNewBlockSize,const int iNewNumBlocks,const bool bNUseSequenceNumber,const bool bPreserve)450 void CNetBufWithStats::Init ( const int iNewBlockSize, const int iNewNumBlocks, const bool bNUseSequenceNumber, const bool bPreserve )
451 {
452 // call base class Init
453 CNetBuf::Init ( iNewBlockSize, iNewNumBlocks, bNUseSequenceNumber, bPreserve );
454
455 // inits for statistics calculation
456 if ( !bPreserve )
457 {
458 // set the auto filter weights and max statistic count
459 if ( bUseDoubleSystemFrameSize )
460 {
461 dAutoFilt_WightUpNormal = IIR_WEIGTH_UP_NORMAL_DOUBLE_FRAME_SIZE;
462 dAutoFilt_WightDownNormal = IIR_WEIGTH_DOWN_NORMAL_DOUBLE_FRAME_SIZE;
463 dAutoFilt_WightUpFast = IIR_WEIGTH_UP_FAST_DOUBLE_FRAME_SIZE;
464 dAutoFilt_WightDownFast = IIR_WEIGTH_DOWN_FAST_DOUBLE_FRAME_SIZE;
465 iMaxStatisticCount = MAX_STATISTIC_COUNT_DOUBLE_FRAME_SIZE;
466 dErrorRateBound = ERROR_RATE_BOUND_DOUBLE_FRAME_SIZE;
467 dUpMaxErrorBound = UP_MAX_ERROR_BOUND_DOUBLE_FRAME_SIZE;
468 }
469 else
470 {
471 dAutoFilt_WightUpNormal = IIR_WEIGTH_UP_NORMAL;
472 dAutoFilt_WightDownNormal = IIR_WEIGTH_DOWN_NORMAL;
473 dAutoFilt_WightUpFast = IIR_WEIGTH_UP_FAST;
474 dAutoFilt_WightDownFast = IIR_WEIGTH_DOWN_FAST;
475 iMaxStatisticCount = MAX_STATISTIC_COUNT;
476 dErrorRateBound = ERROR_RATE_BOUND;
477 dUpMaxErrorBound = UP_MAX_ERROR_BOUND;
478 }
479
480 for ( int i = 0; i < NUM_STAT_SIMULATION_BUFFERS; i++ )
481 {
482 // init simulation buffers with the correct size
483 SimulationBuffer[i].Init ( iNewBlockSize, viBufSizesForSim[i], bNUseSequenceNumber );
484
485 // init statistics
486 ErrorRateStatistic[i].Init ( iMaxStatisticCount, true );
487 }
488
489 // reset the initialization counter which controls the initialization
490 // phase length
491 ResetInitCounter();
492
493 // init auto buffer setting with a meaningful value, also init the
494 // IIR parameter with this value
495 iCurAutoBufferSizeSetting = 6;
496 dCurIIRFilterResult = iCurAutoBufferSizeSetting;
497 iCurDecidedResult = iCurAutoBufferSizeSetting;
498 }
499 }
500
ResetInitCounter()501 void CNetBufWithStats::ResetInitCounter()
502 {
503 // start initialization phase of IIR filtering, use a quarter the size
504 // of the error rate statistic buffers which should be ok for a good
505 // initialization value (initialization phase should be as short as
506 // possible)
507 iInitCounter = iMaxStatisticCount / 4;
508 }
509
Put(const CVector<uint8_t> & vecbyData,const int iInSize)510 bool CNetBufWithStats::Put ( const CVector<uint8_t>& vecbyData, const int iInSize )
511 {
512 // call base class Put
513 const bool bPutOK = CNetBuf::Put ( vecbyData, iInSize );
514
515 // update statistics calculations
516 for ( int i = 0; i < NUM_STAT_SIMULATION_BUFFERS; i++ )
517 {
518 ErrorRateStatistic[i].Update ( !SimulationBuffer[i].Put ( vecbyData, iInSize ) );
519 }
520
521 return bPutOK;
522 }
523
Get(CVector<uint8_t> & vecbyData,const int iOutSize)524 bool CNetBufWithStats::Get ( CVector<uint8_t>& vecbyData, const int iOutSize )
525 {
526 // call base class Get
527 const bool bGetOK = CNetBuf::Get ( vecbyData, iOutSize );
528
529 // update statistics calculations
530 for ( int i = 0; i < NUM_STAT_SIMULATION_BUFFERS; i++ )
531 {
532 ErrorRateStatistic[i].Update ( !SimulationBuffer[i].Get ( vecbyData, iOutSize ) );
533 }
534
535 // update auto setting
536 UpdateAutoSetting();
537
538 return bGetOK;
539 }
540
UpdateAutoSetting()541 void CNetBufWithStats::UpdateAutoSetting()
542 {
543 int iCurDecision = 0; // dummy initialization
544 int iCurMaxUpDecision = 0; // dummy initialization
545 bool bDecisionFound;
546
547 // Get regular error rate decision -----------------------------------------
548 // Use a specified error bound to identify the best buffer size for the
549 // current network situation. Start with the smallest buffer and
550 // test for the error rate until the rate is below the bound.
551 bDecisionFound = false;
552
553 for ( int i = 0; i < NUM_STAT_SIMULATION_BUFFERS - 1; i++ )
554 {
555 if ( ( !bDecisionFound ) && ( ErrorRateStatistic[i].GetAverage() <= dErrorRateBound ) )
556 {
557 iCurDecision = viBufSizesForSim[i];
558 bDecisionFound = true;
559 }
560 }
561
562 if ( !bDecisionFound )
563 {
564 // in case no buffer is below bound, use largest buffer size
565 iCurDecision = viBufSizesForSim[NUM_STAT_SIMULATION_BUFFERS - 1];
566 }
567
568 // Get maximum upper error rate decision -----------------------------------
569 // Use a specified error bound to identify the maximum upper error rate
570 // to identify if we have a too low buffer setting which gives a very
571 // bad performance constantly. Start with the smallest buffer and
572 // test for the error rate until the rate is below the bound.
573 bDecisionFound = false;
574
575 for ( int i = 0; i < NUM_STAT_SIMULATION_BUFFERS - 1; i++ )
576 {
577 if ( ( !bDecisionFound ) && ( ErrorRateStatistic[i].GetAverage() <= dUpMaxErrorBound ) )
578 {
579 iCurMaxUpDecision = viBufSizesForSim[i];
580 bDecisionFound = true;
581 }
582 }
583
584 if ( !bDecisionFound )
585 {
586 // in case no buffer is below bound, use largest buffer size
587 iCurMaxUpDecision = viBufSizesForSim[NUM_STAT_SIMULATION_BUFFERS - 1];
588
589 // This is a worst case, something very bad had happened. Hopefully
590 // this was just temporary so that we initiate a new initialization
591 // phase to get quickly back to normal buffer sizes (hopefully).
592 ResetInitCounter();
593 }
594
595 // Post calculation (filtering) --------------------------------------------
596 // Define different weights for up and down direction. Up direction
597 // filtering shall be slower than for down direction since we assume
598 // that the lower value is the actual value which can be used for
599 // the current network condition. If the current error rate estimation
600 // is higher, it may be a temporary problem which should not change
601 // the current jitter buffer size significantly.
602 // For the initialization phase, use lower weight values to get faster
603 // adaptation.
604 double dWeightUp, dWeightDown;
605 const double dHysteresisValue = FILTER_DECISION_HYSTERESIS;
606 bool bUseFastAdaptation = false;
607
608 // check for initialization phase
609 if ( iInitCounter > 0 )
610 {
611 // decrease init counter
612 iInitCounter--;
613
614 // use the fast adaptation
615 bUseFastAdaptation = true;
616 }
617
618 // if the current detected buffer setting is below the maximum upper bound
619 // decision, then we enable a booster to go up to the minimum required
620 // number of buffer blocks (i.e. we use weights for fast adaptation)
621 if ( iCurAutoBufferSizeSetting < iCurMaxUpDecision )
622 {
623 bUseFastAdaptation = true;
624 }
625
626 if ( bUseFastAdaptation )
627 {
628 dWeightUp = dAutoFilt_WightUpFast;
629 dWeightDown = dAutoFilt_WightDownFast;
630 }
631 else
632 {
633 dWeightUp = dAutoFilt_WightUpNormal;
634 dWeightDown = dAutoFilt_WightDownNormal;
635 }
636
637 // apply non-linear IIR filter
638 MathUtils().UpDownIIR1 ( dCurIIRFilterResult, static_cast<double> ( iCurDecision ), dWeightUp, dWeightDown );
639
640 // clang-format off
641 /*
642 // TEST store important detection parameters in file for debugging
643 static FILE* pFile = fopen ( "test.dat", "w" );
644 static int icnt = 0;
645 if ( icnt == 50 )
646 {
647 fprintf ( pFile, "%d %e\n", iCurDecision, dCurIIRFilterResult );
648 fflush ( pFile );
649 icnt = 0;
650 }
651 else
652 {
653 icnt++;
654 }
655 */
656 // clang-format on
657
658 // apply a hysteresis
659 iCurAutoBufferSizeSetting = MathUtils().DecideWithHysteresis ( dCurIIRFilterResult, iCurDecidedResult, dHysteresisValue );
660
661 // Initialization phase check and correction -------------------------------
662 // sometimes in the very first period after a connection we get a bad error
663 // rate result -> delete this from the initialization phase
664 if ( iInitCounter == iMaxStatisticCount / 8 )
665 {
666 // check error rate of the largest buffer as the indicator
667 if ( ErrorRateStatistic[NUM_STAT_SIMULATION_BUFFERS - 1].GetAverage() > dErrorRateBound )
668 {
669 for ( int i = 0; i < NUM_STAT_SIMULATION_BUFFERS; i++ )
670 {
671 ErrorRateStatistic[i].Reset();
672 }
673 }
674 }
675 }
676