325 lines · 11.5 KB · C++
RawDownload
////  AudioRingBuffer.cpp//  libraries/audio/src////  Created by Stephen Birarda on 2/1/13.//  Copyright 2013 High Fidelity, Inc.////  Distributed under the Apache License, Version 2.0.//  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html// #include "AudioRingBuffer.h" #include <cstdlib>#include <cstring>#include <functional>#include <math.h> #include <QtCore/QDebug> #include <udt/PacketHeaders.h>#include <LogHandler.h> #include "AudioLogging.h" static const QString RING_BUFFER_OVERFLOW_DEBUG { "AudioRingBuffer::writeData has overflown the buffer. Overwriting old data." };static const QString DROPPED_SILENT_DEBUG { "AudioRingBuffer::addSilentSamples dropping silent samples to prevent overflow." }; template <class T>AudioRingBufferTemplate<T>::AudioRingBufferTemplate(int numFrameSamples, int numFramesCapacity) :    _numFrameSamples(numFrameSamples),    _frameCapacity(numFramesCapacity),    _sampleCapacity(numFrameSamples * numFramesCapacity),    _bufferLength(numFrameSamples * (numFramesCapacity + 1)){    if (numFrameSamples) {        _buffer = new Sample[_bufferLength];        memset(_buffer, 0, _bufferLength * SampleSize);        _nextOutput = _buffer;        _endOfLastWrite = _buffer;    }} template <class T>AudioRingBufferTemplate<T>::~AudioRingBufferTemplate() {    delete[] _buffer;} template <class T>void AudioRingBufferTemplate<T>::clear() {    _endOfLastWrite = _buffer;    _nextOutput = _buffer;} template <class T>void AudioRingBufferTemplate<T>::reset() {    clear();    _overflowCount = 0;} template <class T>void AudioRingBufferTemplate<T>::resizeForFrameSize(int numFrameSamples) {    delete[] _buffer;    _numFrameSamples = numFrameSamples;    _sampleCapacity = numFrameSamples * _frameCapacity;    _bufferLength = numFrameSamples * (_frameCapacity + 1);     if (numFrameSamples) {        _buffer = new Sample[_bufferLength];        memset(_buffer, 0, _bufferLength * SampleSize);    } else {        _buffer = nullptr;    }     reset();} template <class T>int AudioRingBufferTemplate<T>::readSamples(Sample* destination, int maxSamples) {    return readData((char*)destination, maxSamples * SampleSize) / SampleSize;} template <class T>int AudioRingBufferTemplate<T>::appendSamples(Sample* destination, int maxSamples, bool append) {    if (append) {        return appendData((char*)destination, maxSamples * SampleSize) / SampleSize;    } else {        return readData((char*)destination, maxSamples * SampleSize) / SampleSize;    }} template <class T>int AudioRingBufferTemplate<T>::writeSamples(const Sample* source, int maxSamples) {    return writeData((char*)source, maxSamples * SampleSize) / SampleSize;} template <class T>int AudioRingBufferTemplate<T>::readData(char *data, int maxSize) {    // only copy up to the number of samples we have available    int maxSamples = maxSize / SampleSize;    int numReadSamples = std::min(maxSamples, samplesAvailable());     if (_nextOutput + numReadSamples > _buffer + _bufferLength) {        // we're going to need to do two reads to get this data, it wraps around the edge        int numSamplesToEnd = (_buffer + _bufferLength) - _nextOutput;         // read to the end of the buffer        memcpy(data, _nextOutput, numSamplesToEnd * SampleSize);         // read the rest from the beginning of the buffer        memcpy(data + (numSamplesToEnd * SampleSize), _buffer, (numReadSamples - numSamplesToEnd) * SampleSize);    } else {        memcpy(data, _nextOutput, numReadSamples * SampleSize);    }     shiftReadPosition(numReadSamples);     return numReadSamples * SampleSize;} template <class T>int AudioRingBufferTemplate<T>::appendData(char *data, int maxSize) {    // only copy up to the number of samples we have available    int maxSamples = maxSize / SampleSize;    int numReadSamples = std::min(maxSamples, samplesAvailable());     Sample* dest = reinterpret_cast<Sample*>(data);    Sample* output = _nextOutput;    if (_nextOutput + numReadSamples > _buffer + _bufferLength) {        // we're going to need to do two reads to get this data, it wraps around the edge        int numSamplesToEnd = (_buffer + _bufferLength) - _nextOutput;         // read to the end of the buffer        for (int i = 0; i < numSamplesToEnd; i++) {            *dest++ += *output++;        }         // read the rest from the beginning of the buffer        output = _buffer;        for (int i = 0; i < (numReadSamples - numSamplesToEnd); i++) {            *dest++ += *output++;        }    } else {        for (int i = 0; i < numReadSamples; i++) {            *dest++ += *output++;        }    }     shiftReadPosition(numReadSamples);     return numReadSamples * SampleSize;} namespace {    int repeatedOverflowMessageID = 0;    std::once_flag messageIDFlag;} template <class T>int AudioRingBufferTemplate<T>::writeData(const char* data, int maxSize) {    // only copy up to the number of samples we have capacity for    int maxSamples = maxSize / SampleSize;    int numWriteSamples = std::min(maxSamples, _sampleCapacity);    int samplesRoomFor = _sampleCapacity - samplesAvailable();     if (numWriteSamples > samplesRoomFor) {        // there's not enough room for this write. erase old data to make room for this new data        int samplesToDelete = numWriteSamples - samplesRoomFor;        _nextOutput = shiftedPositionAccomodatingWrap(_nextOutput, samplesToDelete);        _overflowCount++;         std::call_once(messageIDFlag, [](int* id) { *id = LogHandler::getInstance().newRepeatedMessageID(); },            &repeatedOverflowMessageID);        HIFI_FCDEBUG_ID(audio(), repeatedOverflowMessageID, RING_BUFFER_OVERFLOW_DEBUG);    }     if (_endOfLastWrite + numWriteSamples > _buffer + _bufferLength) {        // we're going to need to do two writes to set this data, it wraps around the edge        int numSamplesToEnd = (_buffer + _bufferLength) - _endOfLastWrite;         // write to the end of the buffer        memcpy(_endOfLastWrite, data, numSamplesToEnd * SampleSize);         // write the rest to the beginning of the buffer        memcpy(_buffer, data + (numSamplesToEnd * SampleSize), (numWriteSamples - numSamplesToEnd) * SampleSize);    } else {        memcpy(_endOfLastWrite, data, numWriteSamples * SampleSize);    }     _endOfLastWrite = shiftedPositionAccomodatingWrap(_endOfLastWrite, numWriteSamples);     return numWriteSamples * SampleSize;} template <class T>int AudioRingBufferTemplate<T>::samplesAvailable() const {    if (!_endOfLastWrite) {        return 0;    }     int sampleDifference = _endOfLastWrite - _nextOutput;    if (sampleDifference < 0) {        sampleDifference += _bufferLength;    }    return sampleDifference;} template <class T>typename AudioRingBufferTemplate<T>::Sample* AudioRingBufferTemplate<T>::shiftedPositionAccomodatingWrap(Sample* position, int numSamplesShift) const {    // NOTE: It is possible to shift out-of-bounds if (|numSamplesShift| > 2 * _bufferLength), but this should not occur    if (numSamplesShift > 0 && position + numSamplesShift >= _buffer + _bufferLength) {        // this shift will wrap the position around to the beginning of the ring        return position + numSamplesShift - _bufferLength;    } else if (numSamplesShift < 0 && position + numSamplesShift < _buffer) {        // this shift will go around to the end of the ring        return position + numSamplesShift + _bufferLength;    } else {        return position + numSamplesShift;    }} template <class T>int AudioRingBufferTemplate<T>::addSilentSamples(int silentSamples) {    // NOTE: This implementation is nearly identical to writeData save for s/memcpy/memset, refer to comments there    int numWriteSamples = std::min(silentSamples, _sampleCapacity);    int samplesRoomFor = _sampleCapacity - samplesAvailable();     if (numWriteSamples > samplesRoomFor) {        numWriteSamples = samplesRoomFor;         HIFI_FCDEBUG(audio(), DROPPED_SILENT_DEBUG);    }     if (_endOfLastWrite + numWriteSamples > _buffer + _bufferLength) {        int numSamplesToEnd = (_buffer + _bufferLength) - _endOfLastWrite;        memset(_endOfLastWrite, 0, numSamplesToEnd * SampleSize);        memset(_buffer, 0, (numWriteSamples - numSamplesToEnd) * SampleSize);    } else {        memset(_endOfLastWrite, 0, numWriteSamples * SampleSize);    }     _endOfLastWrite = shiftedPositionAccomodatingWrap(_endOfLastWrite, numWriteSamples);     return numWriteSamples;} template <class T>float AudioRingBufferTemplate<T>::getFrameLoudness(const Sample* frameStart) const {    // FIXME: This is a bad measure of loudness - normal estimation uses sqrt(sum(x*x))    float loudness = 0.0f;    const Sample* sampleAt = frameStart;    const Sample* bufferLastAt = _buffer + _bufferLength - 1;     for (int i = 0; i < _numFrameSamples; ++i) {        loudness += (float) std::abs(*sampleAt);        // wrap if necessary        sampleAt = sampleAt == bufferLastAt ? _buffer : sampleAt + 1;    }    loudness /= _numFrameSamples;    loudness /= AudioConstants::MAX_SAMPLE_VALUE;     return loudness;} template <class T>float AudioRingBufferTemplate<T>::getFrameLoudness(ConstIterator frameStart) const {    if (frameStart.isNull()) {        return 0.0f;    }    return getFrameLoudness(&(*frameStart));} template <class T>int AudioRingBufferTemplate<T>::writeSamples(ConstIterator source, int maxSamples) {    int samplesToCopy = std::min(maxSamples, _sampleCapacity);    int samplesRoomFor = _sampleCapacity - samplesAvailable();    if (samplesToCopy > samplesRoomFor) {        // there's not enough room for this write.  erase old data to make room for this new data        int samplesToDelete = samplesToCopy - samplesRoomFor;        _nextOutput = shiftedPositionAccomodatingWrap(_nextOutput, samplesToDelete);        _overflowCount++;         std::call_once(messageIDFlag, [](int* id) { *id = LogHandler::getInstance().newRepeatedMessageID(); },            &repeatedOverflowMessageID);        HIFI_FCDEBUG_ID(audio(), repeatedOverflowMessageID, RING_BUFFER_OVERFLOW_DEBUG);    }     Sample* bufferLast = _buffer + _bufferLength - 1;    for (int i = 0; i < samplesToCopy; i++) {        *_endOfLastWrite = *source;        _endOfLastWrite = (_endOfLastWrite == bufferLast) ? _buffer : _endOfLastWrite + 1;        ++source;    }     return samplesToCopy;} template <class T>int AudioRingBufferTemplate<T>::writeSamplesWithFade(ConstIterator source, int maxSamples, float fade) {    int samplesToCopy = std::min(maxSamples, _sampleCapacity);    int samplesRoomFor = _sampleCapacity - samplesAvailable();    if (samplesToCopy > samplesRoomFor) {        // there's not enough room for this write.  erase old data to make room for this new data        int samplesToDelete = samplesToCopy - samplesRoomFor;        _nextOutput = shiftedPositionAccomodatingWrap(_nextOutput, samplesToDelete);        _overflowCount++;         std::call_once(messageIDFlag, [](int* id) { *id = LogHandler::getInstance().newRepeatedMessageID(); },            &repeatedOverflowMessageID);        HIFI_FCDEBUG_ID(audio(), repeatedOverflowMessageID, RING_BUFFER_OVERFLOW_DEBUG);    }     Sample* bufferLast = _buffer + _bufferLength - 1;    for (int i = 0; i < samplesToCopy; i++) {        *_endOfLastWrite = (Sample)((float)(*source) * fade);        _endOfLastWrite = (_endOfLastWrite == bufferLast) ? _buffer : _endOfLastWrite + 1;        ++source;    }     return samplesToCopy;} // explicit instantiations for scratch/mix bufferstemplate class AudioRingBufferTemplate<int16_t>;template class AudioRingBufferTemplate<float>;