mirror of
https://github.com/overte-org/overte.git
synced 2025-07-12 13:18:38 +02:00
325 lines
12 KiB
C++
325 lines
12 KiB
C++
//
|
|
// 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 buffers
|
|
template class AudioRingBufferTemplate<int16_t>;
|
|
template class AudioRingBufferTemplate<float>;
|