/*
 *  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 "webrtc/common_audio/signal_processing/include/signal_processing_library.h"
#include "webrtc/modules/interface/module_common_types.h"
#include "webrtc/system_wrappers/interface/critical_section_wrapper.h"
#include "webrtc/voice_engine/level_indicator.h"

namespace webrtc {

namespace voe {

// Number of bars on the indicator.
// Note that the number of elements is specified because we are indexing it
// in the range of 0-32
const int8_t permutation[33] =
    {0,1,2,3,4,4,5,5,5,5,6,6,6,6,6,7,7,7,7,8,8,8,9,9,9,9,9,9,9,9,9,9,9};


AudioLevel::AudioLevel() :
    _critSect(*CriticalSectionWrapper::CreateCriticalSection()),
    _absMax(0),
    _count(0),
    _currentLevel(0),
    _currentLevelFullRange(0) {
}

AudioLevel::~AudioLevel() {
    delete &_critSect;
}

void AudioLevel::Clear()
{
    CriticalSectionScoped cs(&_critSect);
    _absMax = 0;
    _count = 0;
    _currentLevel = 0;
    _currentLevelFullRange = 0;
}

void AudioLevel::ComputeLevel(const AudioFrame& audioFrame)
{
    int16_t absValue(0);

    // Check speech level (works for 2 channels as well)
    absValue = WebRtcSpl_MaxAbsValueW16(
        audioFrame.data_,
        audioFrame.samples_per_channel_*audioFrame.num_channels_);

    // Protect member access using a lock since this method is called on a
    // dedicated audio thread in the RecordedDataIsAvailable() callback.
    CriticalSectionScoped cs(&_critSect);

    if (absValue > _absMax)
    _absMax = absValue;

    // Update level approximately 10 times per second
    if (_count++ == kUpdateFrequency)
    {
        _currentLevelFullRange = _absMax;

        _count = 0;

        // Highest value for a int16_t is 0x7fff = 32767
        // Divide with 1000 to get in the range of 0-32 which is the range of
        // the permutation vector
        int32_t position = _absMax/1000;

        // Make it less likely that the bar stays at position 0. I.e. only if
        // its in the range 0-250 (instead of 0-1000)
        if ((position == 0) && (_absMax > 250))
        {
            position = 1;
        }
        _currentLevel = permutation[position];

        // Decay the absolute maximum (divide by 4)
        _absMax >>= 2;
    }
}

int8_t AudioLevel::Level() const
{
    CriticalSectionScoped cs(&_critSect);
    return _currentLevel;
}

int16_t AudioLevel::LevelFullRange() const
{
    CriticalSectionScoped cs(&_critSect);
    return _currentLevelFullRange;
}

}  // namespace voe

}  // namespace webrtc
