aboutsummaryrefslogtreecommitdiff
path: root/externals/oboe/apps/OboeTester/app/src/main/cpp/NativeAudioContext.cpp
diff options
context:
space:
mode:
authorcrueter <crueter@eden-emu.dev>2025-08-04 04:50:14 +0200
committercrueter <crueter@eden-emu.dev>2025-08-04 04:50:14 +0200
commit51b170b470ed587f99a7c2a773e4464687f0d07a (patch)
tree4c03c510cfb17a5f5d6233df490d78f06dcfa7f7 /externals/oboe/apps/OboeTester/app/src/main/cpp/NativeAudioContext.cpp
parent04e5e6453875ffa5c79cdb421a29bc19985b1049 (diff)
[cmake] refactor: Use CPM over submodules (#143)
Transfers the majority of submodules and large externals to CPM, using source archives rather than full Git clones. Not only does this save massive amounts of clone and configure time, but dependencies are grabbed on-demand rather than being required by default. Additionally, CPM will (generally) automatically search for system dependencies, though certain dependencies have options to control this. Testing shows gains ranging from 5x to 10x in terms of overall clone/configure time. Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/143 Reviewed-by: CamilleLaVey <camillelavey99@gmail.com>
Diffstat (limited to 'externals/oboe/apps/OboeTester/app/src/main/cpp/NativeAudioContext.cpp')
-rw-r--r--externals/oboe/apps/OboeTester/app/src/main/cpp/NativeAudioContext.cpp810
1 files changed, 0 insertions, 810 deletions
diff --git a/externals/oboe/apps/OboeTester/app/src/main/cpp/NativeAudioContext.cpp b/externals/oboe/apps/OboeTester/app/src/main/cpp/NativeAudioContext.cpp
deleted file mode 100644
index f4c3f6b957..0000000000
--- a/externals/oboe/apps/OboeTester/app/src/main/cpp/NativeAudioContext.cpp
+++ /dev/null
@@ -1,810 +0,0 @@
-/*
- * Copyright 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-// Set to 1 for debugging race condition #1180 with mAAudioStream.
-// See also AudioStreamAAudio.cpp in Oboe.
-// This was left in the code so that we could test the fix again easily in the future.
-// We could not trigger the race condition without adding these get calls and the sleeps.
-#define DEBUG_CLOSE_RACE 0
-
-#include <fstream>
-#include <iostream>
-#if DEBUG_CLOSE_RACE
-#include <thread>
-#endif // DEBUG_CLOSE_RACE
-#include <vector>
-
-#include "oboe/AudioClock.h"
-#include "util/WaveFileWriter.h"
-
-#include "NativeAudioContext.h"
-
-using namespace oboe;
-
-static oboe::AudioApi convertNativeApiToAudioApi(int nativeApi) {
- switch (nativeApi) {
- default:
- case NATIVE_MODE_UNSPECIFIED:
- return oboe::AudioApi::Unspecified;
- case NATIVE_MODE_AAUDIO:
- return oboe::AudioApi::AAudio;
- case NATIVE_MODE_OPENSLES:
- return oboe::AudioApi::OpenSLES;
- }
-}
-
-class MyOboeOutputStream : public WaveFileOutputStream {
-public:
- void write(uint8_t b) override {
- mData.push_back(b);
- }
-
- int32_t length() {
- return (int32_t) mData.size();
- }
-
- uint8_t *getData() {
- return mData.data();
- }
-
-private:
- std::vector<uint8_t> mData;
-};
-
-bool ActivityContext::mUseCallback = true;
-int ActivityContext::callbackSize = 0;
-
-std::shared_ptr<oboe::AudioStream> ActivityContext::getOutputStream() {
- for (auto entry : mOboeStreams) {
- std::shared_ptr<oboe::AudioStream> oboeStream = entry.second;
- if (oboeStream->getDirection() == oboe::Direction::Output) {
- return oboeStream;
- }
- }
- return nullptr;
-}
-
-std::shared_ptr<oboe::AudioStream> ActivityContext::getInputStream() {
- for (auto entry : mOboeStreams) {
- std::shared_ptr<oboe::AudioStream> oboeStream = entry.second;
- if (oboeStream != nullptr) {
- if (oboeStream->getDirection() == oboe::Direction::Input) {
- return oboeStream;
- }
- }
- }
- return nullptr;
-}
-
-void ActivityContext::freeStreamIndex(int32_t streamIndex) {
- mOboeStreams[streamIndex].reset();
- mOboeStreams.erase(streamIndex);
-}
-
-int32_t ActivityContext::allocateStreamIndex() {
- return mNextStreamHandle++;
-}
-
-oboe::Result ActivityContext::release() {
- oboe::Result result = oboe::Result::OK;
- stopBlockingIOThread();
- for (auto entry : mOboeStreams) {
- std::shared_ptr<oboe::AudioStream> oboeStream = entry.second;
- result = oboeStream->release();
- }
- return result;
-}
-
-void ActivityContext::close(int32_t streamIndex) {
- stopBlockingIOThread();
- std::shared_ptr<oboe::AudioStream> oboeStream = getStream(streamIndex);
- if (oboeStream != nullptr) {
- oboeStream->close();
- LOGD("ActivityContext::%s() delete stream %d ", __func__, streamIndex);
- freeStreamIndex(streamIndex);
- }
-}
-
-bool ActivityContext::isMMapUsed(int32_t streamIndex) {
- std::shared_ptr<oboe::AudioStream> oboeStream = getStream(streamIndex);
- if (oboeStream == nullptr) return false;
- if (oboeStream->getAudioApi() != AudioApi::AAudio) return false;
- return AAudioExtensions::getInstance().isMMapUsed(oboeStream.get());
-}
-
-oboe::Result ActivityContext::pause() {
- oboe::Result result = oboe::Result::OK;
- stopBlockingIOThread();
- for (auto entry : mOboeStreams) {
- std::shared_ptr<oboe::AudioStream> oboeStream = entry.second;
- result = oboeStream->requestPause();
- }
- return result;
-}
-
-oboe::Result ActivityContext::stopAllStreams() {
- oboe::Result result = oboe::Result::OK;
- stopBlockingIOThread();
- for (auto entry : mOboeStreams) {
- std::shared_ptr<oboe::AudioStream> oboeStream = entry.second;
- result = oboeStream->requestStop();
- }
- return result;
-}
-
-void ActivityContext::configureBuilder(bool isInput, oboe::AudioStreamBuilder &builder) {
- // We needed the proxy because we did not know the channelCount when we setup the Builder.
- if (mUseCallback) {
- builder.setDataCallback(&oboeCallbackProxy);
- }
-}
-
-int ActivityContext::open(jint nativeApi,
- jint sampleRate,
- jint channelCount,
- jint channelMask,
- jint format,
- jint sharingMode,
- jint performanceMode,
- jint inputPreset,
- jint usage,
- jint contentType,
- jint bufferCapacityInFrames,
- jint deviceId,
- jint sessionId,
- jboolean channelConversionAllowed,
- jboolean formatConversionAllowed,
- jint rateConversionQuality,
- jboolean isMMap,
- jboolean isInput,
- jint spatializationBehavior) {
- oboe::AudioApi audioApi = oboe::AudioApi::Unspecified;
- switch (nativeApi) {
- case NATIVE_MODE_UNSPECIFIED:
- case NATIVE_MODE_AAUDIO:
- case NATIVE_MODE_OPENSLES:
- audioApi = convertNativeApiToAudioApi(nativeApi);
- break;
- default:
- return (jint) oboe::Result::ErrorOutOfRange;
- }
-
- int32_t streamIndex = allocateStreamIndex();
- if (streamIndex < 0) {
- LOGE("ActivityContext::open() stream array full");
- return (jint) oboe::Result::ErrorNoFreeHandles;
- }
-
- if (channelCount < 0 || channelCount > 256) {
- LOGE("ActivityContext::open() channels out of range");
- return (jint) oboe::Result::ErrorOutOfRange;
- }
-
- // Create an audio stream.
- oboe::AudioStreamBuilder builder;
- builder.setChannelCount(channelCount)
- ->setDirection(isInput ? oboe::Direction::Input : oboe::Direction::Output)
- ->setSharingMode((oboe::SharingMode) sharingMode)
- ->setPerformanceMode((oboe::PerformanceMode) performanceMode)
- ->setInputPreset((oboe::InputPreset)inputPreset)
- ->setUsage((oboe::Usage)usage)
- ->setContentType((oboe::ContentType)contentType)
- ->setBufferCapacityInFrames(bufferCapacityInFrames)
- ->setDeviceId(deviceId)
- ->setSessionId((oboe::SessionId) sessionId)
- ->setSampleRate(sampleRate)
- ->setFormat((oboe::AudioFormat) format)
- ->setChannelConversionAllowed(channelConversionAllowed)
- ->setFormatConversionAllowed(formatConversionAllowed)
- ->setSampleRateConversionQuality((oboe::SampleRateConversionQuality) rateConversionQuality)
- ->setSpatializationBehavior((oboe::SpatializationBehavior) spatializationBehavior)
- ;
- if (channelMask != (jint) oboe::ChannelMask::Unspecified) {
- // Set channel mask when it is specified.
- builder.setChannelMask((oboe::ChannelMask) channelMask);
- }
- if (mUseCallback) {
- builder.setFramesPerCallback(callbackSize);
- }
- configureBuilder(isInput, builder);
-
- builder.setAudioApi(audioApi);
-
- // Temporarily set the AAudio MMAP policy to disable MMAP or not.
- bool oldMMapEnabled = AAudioExtensions::getInstance().isMMapEnabled();
- AAudioExtensions::getInstance().setMMapEnabled(isMMap);
-
- // Record time for opening.
- if (isInput) {
- mInputOpenedAt = oboe::AudioClock::getNanoseconds();
- } else {
- mOutputOpenedAt = oboe::AudioClock::getNanoseconds();
- }
- // Open a stream based on the builder settings.
- std::shared_ptr<oboe::AudioStream> oboeStream;
- Result result = builder.openStream(oboeStream);
- AAudioExtensions::getInstance().setMMapEnabled(oldMMapEnabled);
- if (result != Result::OK) {
- freeStreamIndex(streamIndex);
- streamIndex = -1;
- } else {
- mOboeStreams[streamIndex] = oboeStream; // save shared_ptr
-
- mChannelCount = oboeStream->getChannelCount(); // FIXME store per stream
- mFramesPerBurst = oboeStream->getFramesPerBurst();
- mSampleRate = oboeStream->getSampleRate();
-
- createRecording();
-
- finishOpen(isInput, oboeStream);
- }
-
- if (!mUseCallback) {
- int numSamples = getFramesPerBlock() * mChannelCount;
- dataBuffer = std::make_unique<float[]>(numSamples);
- }
-
- if (result != Result::OK) {
- return (int) result;
- } else {
- configureAfterOpen();
- return streamIndex;
- }
-}
-
-oboe::Result ActivityContext::start() {
- oboe::Result result = oboe::Result::OK;
- std::shared_ptr<oboe::AudioStream> inputStream = getInputStream();
- std::shared_ptr<oboe::AudioStream> outputStream = getOutputStream();
- if (inputStream == nullptr && outputStream == nullptr) {
- LOGD("%s() - no streams defined", __func__);
- return oboe::Result::ErrorInvalidState; // not open
- }
-
- audioStreamGateway.reset();
- result = startStreams();
-
- if (!mUseCallback && result == oboe::Result::OK) {
- // Instead of using the callback, start a thread that writes the stream.
- threadEnabled.store(true);
- dataThread = new std::thread(threadCallback, this);
- }
-
-#if DEBUG_CLOSE_RACE
- // Also put a sleep for 400 msec in AudioStreamAAudio::updateFramesRead().
- if (outputStream != nullptr) {
- std::thread raceDebugger([outputStream]() {
- while (outputStream->getState() != StreamState::Closed) {
- int64_t framesRead = outputStream->getFramesRead();
- LOGD("raceDebugger, framesRead = %d, state = %d",
- (int) framesRead, (int) outputStream->getState());
- }
- });
- raceDebugger.detach();
- }
-#endif // DEBUG_CLOSE_RACE
-
- return result;
-}
-
-oboe::Result ActivityContext::flush() {
- oboe::Result result = oboe::Result::OK;
- for (auto entry : mOboeStreams) {
- std::shared_ptr<oboe::AudioStream> oboeStream = entry.second;
- result = oboeStream->requestFlush();
- }
- return result;
-}
-
-int32_t ActivityContext::saveWaveFile(const char *filename) {
- if (mRecording == nullptr) {
- LOGW("ActivityContext::saveWaveFile(%s) but no recording!", filename);
- return -1;
- }
- if (mRecording->getSizeInFrames() == 0) {
- LOGW("ActivityContext::saveWaveFile(%s) but no frames!", filename);
- return -2;
- }
- MyOboeOutputStream outStream;
- WaveFileWriter writer(&outStream);
- // You must setup the format before the first write().
- writer.setFrameRate(mSampleRate);
- writer.setSamplesPerFrame(mRecording->getChannelCount());
- writer.setBitsPerSample(24);
- writer.setFrameCount(mRecording->getSizeInFrames());
- float buffer[mRecording->getChannelCount()];
- // Read samples from start to finish.
- mRecording->rewind();
- for (int32_t frameIndex = 0; frameIndex < mRecording->getSizeInFrames(); frameIndex++) {
- mRecording->read(buffer, 1 /* numFrames */);
- for (int32_t i = 0; i < mRecording->getChannelCount(); i++) {
- writer.write(buffer[i]);
- }
- }
- writer.close();
-
- if (outStream.length() > 0) {
- auto myfile = std::ofstream(filename, std::ios::out | std::ios::binary);
- myfile.write((char *) outStream.getData(), outStream.length());
- myfile.close();
- }
-
- return outStream.length();
-}
-
-double ActivityContext::getTimestampLatency(int32_t streamIndex) {
- std::shared_ptr<oboe::AudioStream> oboeStream = getStream(streamIndex);
- if (oboeStream != nullptr) {
- auto result = oboeStream->calculateLatencyMillis();
- return (!result) ? -1.0 : result.value();
- }
- return -1.0;
-}
-
-// =================================================================== ActivityTestOutput
-void ActivityTestOutput::close(int32_t streamIndex) {
- ActivityContext::close(streamIndex);
- manyToMulti.reset(nullptr);
- monoToMulti.reset(nullptr);
- mVolumeRamp.reset();
- mSinkFloat.reset();
- mSinkI16.reset();
- mSinkI24.reset();
- mSinkI32.reset();
- mSinkMemoryDirect.reset();
-}
-
-void ActivityTestOutput::setChannelEnabled(int channelIndex, bool enabled) {
- if (manyToMulti == nullptr) {
- return;
- }
- if (enabled) {
- switch (mSignalType) {
- case SignalType::Sine:
- sineOscillators[channelIndex].frequency.disconnect();
- sineOscillators[channelIndex].output.connect(manyToMulti->inputs[channelIndex].get());
- break;
- case SignalType::Sawtooth:
- sawtoothOscillators[channelIndex].output.connect(manyToMulti->inputs[channelIndex].get());
- break;
- case SignalType::FreqSweep:
- mLinearShape.output.connect(&sineOscillators[channelIndex].frequency);
- sineOscillators[channelIndex].output.connect(manyToMulti->inputs[channelIndex].get());
- break;
- case SignalType::PitchSweep:
- mExponentialShape.output.connect(&sineOscillators[channelIndex].frequency);
- sineOscillators[channelIndex].output.connect(manyToMulti->inputs[channelIndex].get());
- break;
- case SignalType::WhiteNoise:
- mWhiteNoise.output.connect(manyToMulti->inputs[channelIndex].get());
- break;
- default:
- break;
- }
- } else {
- manyToMulti->inputs[channelIndex]->disconnect();
- }
-}
-
-void ActivityTestOutput::configureAfterOpen() {
- manyToMulti = std::make_unique<ManyToMultiConverter>(mChannelCount);
-
- std::shared_ptr<oboe::AudioStream> outputStream = getOutputStream();
-
- mVolumeRamp = std::make_shared<RampLinear>(mChannelCount);
- mVolumeRamp->setLengthInFrames(kRampMSec * outputStream->getSampleRate() /
- MILLISECONDS_PER_SECOND);
- mVolumeRamp->setTarget(mAmplitude);
-
- mSinkFloat = std::make_shared<SinkFloat>(mChannelCount);
- mSinkI16 = std::make_shared<SinkI16>(mChannelCount);
- mSinkI24 = std::make_shared<SinkI24>(mChannelCount);
- mSinkI32 = std::make_shared<SinkI32>(mChannelCount);
- static constexpr int COMPRESSED_FORMAT_BYTES_PER_FRAME = 1;
- mSinkMemoryDirect = std::make_shared<SinkMemoryDirect>(
- mChannelCount, COMPRESSED_FORMAT_BYTES_PER_FRAME);
-
- mTriangleOscillator.setSampleRate(outputStream->getSampleRate());
- mTriangleOscillator.frequency.setValue(1.0/kSweepPeriod);
- mTriangleOscillator.amplitude.setValue(1.0);
- mTriangleOscillator.setPhase(-1.0);
-
- mLinearShape.setMinimum(0.0);
- mLinearShape.setMaximum(outputStream->getSampleRate() * 0.5); // Nyquist
-
- mExponentialShape.setMinimum(110.0);
- mExponentialShape.setMaximum(outputStream->getSampleRate() * 0.5); // Nyquist
-
- mTriangleOscillator.output.connect(&(mLinearShape.input));
- mTriangleOscillator.output.connect(&(mExponentialShape.input));
- {
- double frequency = 330.0;
- // Go up by a minor third or a perfect fourth just intoned interval.
- const float interval = (mChannelCount > 8) ? (6.0f / 5.0f) : (4.0f / 3.0f);
- for (int i = 0; i < mChannelCount; i++) {
- sineOscillators[i].setSampleRate(outputStream->getSampleRate());
- sineOscillators[i].frequency.setValue(frequency);
- sineOscillators[i].amplitude.setValue(AMPLITUDE_SINE / mChannelCount);
- sawtoothOscillators[i].setSampleRate(outputStream->getSampleRate());
- sawtoothOscillators[i].frequency.setValue(frequency);
- sawtoothOscillators[i].amplitude.setValue(AMPLITUDE_SAWTOOTH / mChannelCount);
-
- frequency *= interval; // each wave is at a higher frequency
- setChannelEnabled(i, true);
- }
- }
-
- mWhiteNoise.amplitude.setValue(0.5);
-
- manyToMulti->output.connect(&(mVolumeRamp.get()->input));
-
- mVolumeRamp->output.connect(&(mSinkFloat.get()->input));
- mVolumeRamp->output.connect(&(mSinkI16.get()->input));
- mVolumeRamp->output.connect(&(mSinkI24.get()->input));
- mVolumeRamp->output.connect(&(mSinkI32.get()->input));
-
- mSinkFloat->pullReset();
- mSinkI16->pullReset();
- mSinkI24->pullReset();
- mSinkI32->pullReset();
- mSinkMemoryDirect->pullReset();
-
- configureStreamGateway();
-}
-
-void ActivityTestOutput::configureStreamGateway() {
- std::shared_ptr<oboe::AudioStream> outputStream = getOutputStream();
- if (outputStream->getFormat() == oboe::AudioFormat::I16) {
- audioStreamGateway.setAudioSink(mSinkI16);
- } else if (outputStream->getFormat() == oboe::AudioFormat::I24) {
- audioStreamGateway.setAudioSink(mSinkI24);
- } else if (outputStream->getFormat() == oboe::AudioFormat::I32) {
- audioStreamGateway.setAudioSink(mSinkI32);
- } else if (outputStream->getFormat() == oboe::AudioFormat::Float) {
- audioStreamGateway.setAudioSink(mSinkFloat);
- } else if (outputStream->getFormat() == oboe::AudioFormat::MP3) {
- audioStreamGateway.setAudioSink(mSinkMemoryDirect);
- }
-
- if (mUseCallback) {
- oboeCallbackProxy.setDataCallback(&audioStreamGateway);
- }
-}
-
-void ActivityTestOutput::runBlockingIO() {
- int32_t framesPerBlock = getFramesPerBlock();
- oboe::DataCallbackResult callbackResult = oboe::DataCallbackResult::Continue;
-
- std::shared_ptr<oboe::AudioStream> oboeStream = getOutputStream();
- if (oboeStream == nullptr) {
- LOGE("%s() : no stream found\n", __func__);
- return;
- }
-
- while (threadEnabled.load()
- && callbackResult == oboe::DataCallbackResult::Continue) {
- // generate output by calling the callback
- callbackResult = audioStreamGateway.onAudioReady(oboeStream.get(),
- dataBuffer.get(),
- framesPerBlock);
-
- auto result = oboeStream->write(dataBuffer.get(),
- framesPerBlock,
- NANOS_PER_SECOND);
-
- if (!result) {
- LOGE("%s() returned %s\n", __func__, convertToText(result.error()));
- break;
- }
- int32_t framesWritten = result.value();
- if (framesWritten < framesPerBlock) {
- LOGE("%s() : write() wrote %d of %d\n", __func__, framesWritten, framesPerBlock);
- break;
- }
- }
-}
-
-oboe::Result ActivityTestOutput::startStreams() {
- mSinkFloat->pullReset();
- mSinkI16->pullReset();
- mSinkI24->pullReset();
- mSinkI32->pullReset();
- mSinkMemoryDirect->pullReset();
- if (mVolumeRamp != nullptr) {
- mVolumeRamp->setTarget(mAmplitude);
- }
- return getOutputStream()->start();
-}
-
-void ActivityTestOutput::setupMemoryBuffer(std::unique_ptr<uint8_t[]> &buffer, int length) {
- if (mSinkMemoryDirect != nullptr) {
- mSinkMemoryDirect->setupMemoryBuffer(buffer, length);
- }
-}
-
-// ======================================================================= ActivityTestInput
-void ActivityTestInput::configureAfterOpen() {
- mInputAnalyzer.reset();
- if (mUseCallback) {
- oboeCallbackProxy.setDataCallback(&mInputAnalyzer);
- }
- mInputAnalyzer.setRecording(mRecording.get());
-}
-
-void ActivityTestInput::runBlockingIO() {
- int32_t framesPerBlock = getFramesPerBlock();
- oboe::DataCallbackResult callbackResult = oboe::DataCallbackResult::Continue;
-
- std::shared_ptr<oboe::AudioStream> oboeStream = getInputStream();
- if (oboeStream == nullptr) {
- LOGE("%s() : no stream found\n", __func__);
- return;
- }
-
- while (threadEnabled.load()
- && callbackResult == oboe::DataCallbackResult::Continue) {
-
- // Avoid glitches by waiting until there is extra data in the FIFO.
- auto err = oboeStream->waitForAvailableFrames(mMinimumFramesBeforeRead, kNanosPerSecond);
- if (!err) break;
-
- // read from input
- auto result = oboeStream->read(dataBuffer.get(),
- framesPerBlock,
- NANOS_PER_SECOND);
- if (!result) {
- LOGE("%s() : read() returned %s\n", __func__, convertToText(result.error()));
- break;
- }
- int32_t framesRead = result.value();
- if (framesRead < framesPerBlock) { // timeout?
- LOGE("%s() : read() read %d of %d\n", __func__, framesRead, framesPerBlock);
- break;
- }
-
- // analyze input
- callbackResult = mInputAnalyzer.onAudioReady(oboeStream.get(),
- dataBuffer.get(),
- framesRead);
- }
-}
-
-oboe::Result ActivityRecording::stopPlayback() {
- oboe::Result result = oboe::Result::OK;
- if (playbackStream != nullptr) {
- result = playbackStream->requestStop();
- playbackStream->close();
- mPlayRecordingCallback.setRecording(nullptr);
- delete playbackStream;
- playbackStream = nullptr;
- }
- return result;
-}
-
-oboe::Result ActivityRecording::startPlayback() {
- stop();
- oboe::AudioStreamBuilder builder;
- builder.setChannelCount(mChannelCount)
- ->setSampleRate(mSampleRate)
- ->setFormat(oboe::AudioFormat::Float)
- ->setCallback(&mPlayRecordingCallback);
- oboe::Result result = builder.openStream(&playbackStream);
- if (result != oboe::Result::OK) {
- delete playbackStream;
- playbackStream = nullptr;
- } else if (playbackStream != nullptr) {
- if (mRecording != nullptr) {
- mRecording->rewind();
- mPlayRecordingCallback.setRecording(mRecording.get());
- result = playbackStream->requestStart();
- }
- }
- return result;
-}
-
-// ======================================================================= ActivityTapToTone
-void ActivityTapToTone::configureAfterOpen() {
- monoToMulti = std::make_unique<MonoToMultiConverter>(mChannelCount);
-
- mSinkFloat = std::make_shared<SinkFloat>(mChannelCount);
- mSinkI16 = std::make_shared<SinkI16>(mChannelCount);
- mSinkI24 = std::make_shared<SinkI24>(mChannelCount);
- mSinkI32 = std::make_shared<SinkI32>(mChannelCount);
-
- std::shared_ptr<oboe::AudioStream> outputStream = getOutputStream();
- sawPingGenerator.setSampleRate(outputStream->getSampleRate());
- sawPingGenerator.frequency.setValue(FREQUENCY_SAW_PING);
- sawPingGenerator.amplitude.setValue(AMPLITUDE_SAW_PING);
-
- sawPingGenerator.output.connect(&(monoToMulti->input));
- monoToMulti->output.connect(&(mSinkFloat.get()->input));
- monoToMulti->output.connect(&(mSinkI16.get()->input));
- monoToMulti->output.connect(&(mSinkI24.get()->input));
- monoToMulti->output.connect(&(mSinkI32.get()->input));
-
- mSinkFloat->pullReset();
- mSinkI16->pullReset();
- mSinkI24->pullReset();
- mSinkI32->pullReset();
-
- configureStreamGateway();
-}
-
-// ======================================================================= ActivityFullDuplex
-void ActivityFullDuplex::configureBuilder(bool isInput, oboe::AudioStreamBuilder &builder) {
- if (isInput) {
- // Ideally the output streams should be opened first.
- std::shared_ptr<oboe::AudioStream> outputStream = getOutputStream();
- if (outputStream != nullptr) {
- // The input and output buffers will run in sync with input empty
- // and output full. So set the input capacity to match the output.
- builder.setBufferCapacityInFrames(outputStream->getBufferCapacityInFrames());
- }
- }
-}
-
-// ======================================================================= ActivityEcho
-void ActivityEcho::configureBuilder(bool isInput, oboe::AudioStreamBuilder &builder) {
- ActivityFullDuplex::configureBuilder(isInput, builder);
-
- if (mFullDuplexEcho.get() == nullptr) {
- mFullDuplexEcho = std::make_unique<FullDuplexEcho>();
- }
- // only output uses a callback, input is polled
- if (!isInput) {
- builder.setCallback((oboe::AudioStreamCallback *) &oboeCallbackProxy);
- oboeCallbackProxy.setDataCallback(mFullDuplexEcho.get());
- }
-}
-
-void ActivityEcho::finishOpen(bool isInput, std::shared_ptr<oboe::AudioStream> &oboeStream) {
- if (isInput) {
- mFullDuplexEcho->setSharedInputStream(oboeStream);
- } else {
- mFullDuplexEcho->setSharedOutputStream(oboeStream);
- }
-}
-
-// ======================================================================= ActivityRoundTripLatency
-void ActivityRoundTripLatency::configureBuilder(bool isInput, oboe::AudioStreamBuilder &builder) {
- ActivityFullDuplex::configureBuilder(isInput, builder);
-
- if (mFullDuplexLatency.get() == nullptr) {
- mFullDuplexLatency = std::make_unique<FullDuplexAnalyzer>(mLatencyAnalyzer.get());
- }
- if (!isInput) {
- // only output uses a callback, input is polled
- builder.setCallback((oboe::AudioStreamCallback *) &oboeCallbackProxy);
- oboeCallbackProxy.setDataCallback(mFullDuplexLatency.get());
- }
-}
-
-void ActivityRoundTripLatency::finishOpen(bool isInput, std::shared_ptr<oboe::AudioStream>
- &oboeStream) {
- if (isInput) {
- mFullDuplexLatency->setSharedInputStream(oboeStream);
- mFullDuplexLatency->setRecording(mRecording.get());
- } else {
- mFullDuplexLatency->setSharedOutputStream(oboeStream);
- }
-}
-
-// The timestamp latency is the difference between the input
-// and output times for a specific frame.
-// Start with the position and time from an input timestamp.
-// Map the input position to the corresponding position in output
-// and calculate its time.
-// Use the difference between framesWritten and framesRead to
-// convert input positions to output positions.
-jdouble ActivityRoundTripLatency::measureTimestampLatency() {
- if (!mFullDuplexLatency->isWriteReadDeltaValid()) return -1.0;
-
- int64_t writeReadDelta = mFullDuplexLatency->getWriteReadDelta();
- auto inputTimestampResult = mFullDuplexLatency->getInputStream()->getTimestamp(CLOCK_MONOTONIC);
- if (!inputTimestampResult) return -1.0;
- auto outputTimestampResult = mFullDuplexLatency->getOutputStream()->getTimestamp(CLOCK_MONOTONIC);
- if (!outputTimestampResult) return -1.0;
-
- int64_t inputPosition = inputTimestampResult.value().position;
- int64_t inputTimeNanos = inputTimestampResult.value().timestamp;
- int64_t ouputPosition = outputTimestampResult.value().position;
- int64_t outputTimeNanos = outputTimestampResult.value().timestamp;
-
- // Map input frame position to the corresponding output frame.
- int64_t mappedPosition = inputPosition + writeReadDelta;
- // Calculate when that frame will play.
- int32_t sampleRate = mFullDuplexLatency->getOutputStream()->getSampleRate();
- int64_t mappedTimeNanos = outputTimeNanos + ((mappedPosition - ouputPosition) * 1e9) / sampleRate;
-
- // Latency is the difference in time between when a frame was recorded and
- // when its corresponding echo was played.
- return (mappedTimeNanos - inputTimeNanos) * 1.0e-6; // convert nanos to millis
-}
-
-// ======================================================================= ActivityGlitches
-void ActivityGlitches::configureBuilder(bool isInput, oboe::AudioStreamBuilder &builder) {
- ActivityFullDuplex::configureBuilder(isInput, builder);
-
- if (mFullDuplexGlitches.get() == nullptr) {
- mFullDuplexGlitches = std::make_unique<FullDuplexAnalyzer>(&mGlitchAnalyzer);
- }
- if (!isInput) {
- // only output uses a callback, input is polled
- builder.setCallback((oboe::AudioStreamCallback *) &oboeCallbackProxy);
- oboeCallbackProxy.setDataCallback(mFullDuplexGlitches.get());
- }
-}
-
-void ActivityGlitches::finishOpen(bool isInput, std::shared_ptr<oboe::AudioStream> &oboeStream) {
- if (isInput) {
- mFullDuplexGlitches->setSharedInputStream(oboeStream);
- mFullDuplexGlitches->setRecording(mRecording.get());
- } else {
- mFullDuplexGlitches->setSharedOutputStream(oboeStream);
- }
-}
-
-// ======================================================================= ActivityDataPath
-void ActivityDataPath::configureBuilder(bool isInput, oboe::AudioStreamBuilder &builder) {
- ActivityFullDuplex::configureBuilder(isInput, builder);
-
- if (mFullDuplexDataPath.get() == nullptr) {
- mFullDuplexDataPath = std::make_unique<FullDuplexAnalyzer>(&mDataPathAnalyzer);
- }
- if (!isInput) {
- // only output uses a callback, input is polled
- builder.setCallback((oboe::AudioStreamCallback *) &oboeCallbackProxy);
- oboeCallbackProxy.setDataCallback(mFullDuplexDataPath.get());
- }
-}
-
-void ActivityDataPath::finishOpen(bool isInput, std::shared_ptr<oboe::AudioStream> &oboeStream) {
- if (isInput) {
- mFullDuplexDataPath->setSharedInputStream(oboeStream);
- mFullDuplexDataPath->setRecording(mRecording.get());
- } else {
- mFullDuplexDataPath->setSharedOutputStream(oboeStream);
- }
-}
-
-// =================================================================== ActivityTestDisconnect
-void ActivityTestDisconnect::close(int32_t streamIndex) {
- ActivityContext::close(streamIndex);
- mSinkFloat.reset();
-}
-
-void ActivityTestDisconnect::configureAfterOpen() {
- std::shared_ptr<oboe::AudioStream> outputStream = getOutputStream();
- std::shared_ptr<oboe::AudioStream> inputStream = getInputStream();
- if (outputStream) {
- mSinkFloat = std::make_unique<SinkFloat>(mChannelCount);
- sineOscillator = std::make_unique<SineOscillator>();
- monoToMulti = std::make_unique<MonoToMultiConverter>(mChannelCount);
-
- sineOscillator->setSampleRate(outputStream->getSampleRate());
- sineOscillator->frequency.setValue(440.0);
- sineOscillator->amplitude.setValue(AMPLITUDE_SINE);
- sineOscillator->output.connect(&(monoToMulti->input));
-
- monoToMulti->output.connect(&(mSinkFloat->input));
- mSinkFloat->pullReset();
- audioStreamGateway.setAudioSink(mSinkFloat);
- } else if (inputStream) {
- audioStreamGateway.setAudioSink(nullptr);
- }
- oboeCallbackProxy.setDataCallback(&audioStreamGateway);
-}
-