#include <memory>
#include "oboe/Oboe.h"
#include "common/OboeDebug.h"
#include "opensles/AudioStreamBuffered.h"
#include "common/AudioClock.h"
namespace oboe {
constexpr int kDefaultBurstsPerBuffer = 16; constexpr int kMinBurstsPerBuffer = 4; constexpr int kMinFramesPerBuffer = 48 * 32;
AudioStreamBuffered::AudioStreamBuffered(const AudioStreamBuilder &builder)
: AudioStream(builder) {
}
void AudioStreamBuffered::allocateFifo() {
if (usingFIFO()) {
int32_t capacityFrames = getBufferCapacityInFrames();
if (capacityFrames == oboe::kUnspecified) {
capacityFrames = getFramesPerBurst() * kDefaultBurstsPerBuffer;
} else {
int32_t minFramesPerBufferByBursts = getFramesPerBurst() * kMinBurstsPerBuffer;
if (capacityFrames <= minFramesPerBufferByBursts) {
capacityFrames = minFramesPerBufferByBursts;
} else {
capacityFrames = std::max(kMinFramesPerBuffer, capacityFrames);
int32_t numBursts = (capacityFrames + getFramesPerBurst() - 1)
/ getFramesPerBurst();
capacityFrames = numBursts * getFramesPerBurst();
}
}
mFifoBuffer = std::make_unique<FifoBuffer>(getBytesPerFrame(), capacityFrames);
mBufferCapacityInFrames = capacityFrames;
mBufferSizeInFrames = mBufferCapacityInFrames;
}
}
void AudioStreamBuffered::updateFramesWritten() {
if (mFifoBuffer) {
mFramesWritten = static_cast<int64_t>(mFifoBuffer->getWriteCounter());
} }
void AudioStreamBuffered::updateFramesRead() {
if (mFifoBuffer) {
mFramesRead = static_cast<int64_t>(mFifoBuffer->getReadCounter());
} }
DataCallbackResult AudioStreamBuffered::onDefaultCallback(void *audioData, int numFrames) {
int32_t framesTransferred = 0;
if (getDirection() == oboe::Direction::Output) {
framesTransferred = mFifoBuffer->readNow(audioData, numFrames);
} else {
framesTransferred = mFifoBuffer->write(audioData, numFrames); }
if (framesTransferred < numFrames) {
LOGD("AudioStreamBuffered::%s(): xrun! framesTransferred = %d, numFrames = %d",
__func__, framesTransferred, numFrames);
incrementXRunCount();
}
markCallbackTime(static_cast<int32_t>(numFrames)); return DataCallbackResult::Continue;
}
void AudioStreamBuffered::markCallbackTime(int32_t numFrames) {
mLastBackgroundSize = numFrames;
mBackgroundRanAtNanoseconds = AudioClock::getNanoseconds();
}
int64_t AudioStreamBuffered::predictNextCallbackTime() {
if (mBackgroundRanAtNanoseconds == 0) {
return 0;
}
int64_t nanosPerBuffer = (kNanosPerSecond * mLastBackgroundSize) / getSampleRate();
const int64_t margin = 200 * kNanosPerMicrosecond; return mBackgroundRanAtNanoseconds + nanosPerBuffer + margin;
}
ResultWithValue<int32_t> AudioStreamBuffered::transfer(
void *readBuffer,
const void *writeBuffer,
int32_t numFrames,
int64_t timeoutNanoseconds) {
if (readBuffer != nullptr && writeBuffer != nullptr) {
LOGE("AudioStreamBuffered::%s(): both buffers are not NULL", __func__);
return ResultWithValue<int32_t>(Result::ErrorInternal);
}
if (getDirection() == Direction::Input && readBuffer == nullptr) {
LOGE("AudioStreamBuffered::%s(): readBuffer is NULL", __func__);
return ResultWithValue<int32_t>(Result::ErrorNull);
}
if (getDirection() == Direction::Output && writeBuffer == nullptr) {
LOGE("AudioStreamBuffered::%s(): writeBuffer is NULL", __func__);
return ResultWithValue<int32_t>(Result::ErrorNull);
}
if (numFrames < 0) {
LOGE("AudioStreamBuffered::%s(): numFrames is negative", __func__);
return ResultWithValue<int32_t>(Result::ErrorOutOfRange);
} else if (numFrames == 0) {
return ResultWithValue<int32_t>(numFrames);
}
if (timeoutNanoseconds < 0) {
LOGE("AudioStreamBuffered::%s(): timeoutNanoseconds is negative", __func__);
return ResultWithValue<int32_t>(Result::ErrorOutOfRange);
}
int32_t result = 0;
uint8_t *readData = reinterpret_cast<uint8_t *>(readBuffer);
const uint8_t *writeData = reinterpret_cast<const uint8_t *>(writeBuffer);
int32_t framesLeft = numFrames;
int64_t timeToQuit = 0;
bool repeat = true;
if (timeoutNanoseconds > 0) {
timeToQuit = AudioClock::getNanoseconds() + timeoutNanoseconds;
}
do {
if (getDirection() == Direction::Input) {
result = mFifoBuffer->read(readData, framesLeft);
if (result > 0) {
readData += mFifoBuffer->convertFramesToBytes(result);
framesLeft -= result;
}
} else {
uint32_t fullFrames = mFifoBuffer->getFullFramesAvailable();
int32_t emptyFrames = getBufferSizeInFrames() - static_cast<int32_t>(fullFrames);
int32_t framesToWrite = std::max(0, std::min(framesLeft, emptyFrames));
result = mFifoBuffer->write(writeData, framesToWrite);
if (result > 0) {
writeData += mFifoBuffer->convertFramesToBytes(result);
framesLeft -= result;
}
}
if (framesLeft > 0 && result >= 0 && timeoutNanoseconds > 0) {
int64_t timeNow = AudioClock::getNanoseconds();
if (timeNow >= timeToQuit) {
LOGE("AudioStreamBuffered::%s(): TIMEOUT", __func__);
repeat = false; } else {
int64_t sleepForNanos;
int64_t wakeTimeNanos = predictNextCallbackTime();
if (wakeTimeNanos <= 0) {
sleepForNanos = (getFramesPerBurst() * kNanosPerSecond) / getSampleRate();
} else {
if (wakeTimeNanos > timeToQuit) {
wakeTimeNanos = timeToQuit;
}
sleepForNanos = wakeTimeNanos - timeNow;
const int64_t minSleepTime = kNanosPerMillisecond; if (sleepForNanos < minSleepTime) {
sleepForNanos = minSleepTime;
}
}
AudioClock::sleepForNanos(sleepForNanos);
}
} else {
repeat = false;
}
} while(repeat);
if (result < 0) {
return ResultWithValue<int32_t>(static_cast<Result>(result));
} else {
int32_t framesWritten = numFrames - framesLeft;
return ResultWithValue<int32_t>(framesWritten);
}
}
ResultWithValue<int32_t> AudioStreamBuffered::write(const void *buffer,
int32_t numFrames,
int64_t timeoutNanoseconds) {
if (getState() == StreamState::Closed){
return ResultWithValue<int32_t>(Result::ErrorClosed);
}
if (getDirection() == Direction::Input) {
return ResultWithValue<int32_t>(Result::ErrorUnavailable); }
Result result = updateServiceFrameCounter();
if (result != Result::OK) return ResultWithValue<int32_t>(static_cast<Result>(result));
return transfer(nullptr, buffer, numFrames, timeoutNanoseconds);
}
ResultWithValue<int32_t> AudioStreamBuffered::read(void *buffer,
int32_t numFrames,
int64_t timeoutNanoseconds) {
if (getState() == StreamState::Closed){
return ResultWithValue<int32_t>(Result::ErrorClosed);
}
if (getDirection() == Direction::Output) {
return ResultWithValue<int32_t>(Result::ErrorUnavailable); }
Result result = updateServiceFrameCounter();
if (result != Result::OK) return ResultWithValue<int32_t>(static_cast<Result>(result));
return transfer(buffer, nullptr, numFrames, timeoutNanoseconds);
}
ResultWithValue<int32_t> AudioStreamBuffered::setBufferSizeInFrames(int32_t requestedFrames)
{
if (getState() == StreamState::Closed){
return ResultWithValue<int32_t>(Result::ErrorClosed);
}
if (!mFifoBuffer) {
return ResultWithValue<int32_t>(Result::ErrorUnimplemented);
}
if (requestedFrames > mFifoBuffer->getBufferCapacityInFrames()) {
requestedFrames = mFifoBuffer->getBufferCapacityInFrames();
} else if (requestedFrames < getFramesPerBurst()) {
requestedFrames = getFramesPerBurst();
}
mBufferSizeInFrames = requestedFrames;
return ResultWithValue<int32_t>(requestedFrames);
}
int32_t AudioStreamBuffered::getBufferCapacityInFrames() const {
if (mFifoBuffer) {
return mFifoBuffer->getBufferCapacityInFrames();
} else {
return AudioStream::getBufferCapacityInFrames();
}
}
bool AudioStreamBuffered::isXRunCountSupported() const {
return (!isDataCallbackSpecified());
}
}