aboutsummaryrefslogtreecommitdiff
path: root/externals/oboe/apps/OboeTester/app/src/main/cpp/NativeAudioContext.cpp
diff options
context:
space:
mode:
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);
-}
-