diff options
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.cpp | 810 |
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); -} - |
