/* * Copyright 2023 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. */ #include #include #include "common/OboeDebug.h" #include "oboe/AudioClock.h" #include "TestColdStartLatency.h" #include "OboeTools.h" using namespace oboe; int32_t TestColdStartLatency::open(bool useInput, bool useLowLatency, bool useMmap, bool useExclusive) { mDataCallback = std::make_shared(); // Enable MMAP if needed bool wasMMapEnabled = AAudioExtensions::getInstance().isMMapEnabled(); AAudioExtensions::getInstance().setMMapEnabled(useMmap); int64_t beginOpenNanos = AudioClock::getNanoseconds(); AudioStreamBuilder builder; Result result = builder.setFormat(AudioFormat::Float) ->setPerformanceMode(useLowLatency ? PerformanceMode::LowLatency : PerformanceMode::None) ->setDirection(useInput ? Direction::Input : Direction::Output) ->setChannelCount(kChannelCount) ->setDataCallback(mDataCallback) ->setSharingMode(useExclusive ? SharingMode::Exclusive : SharingMode::Shared) ->openStream(mStream); int64_t endOpenNanos = AudioClock::getNanoseconds(); int64_t actualDurationNanos = endOpenNanos - beginOpenNanos; mOpenTimeMicros = actualDurationNanos / NANOS_PER_MICROSECOND; // Revert MMAP back to its previous state AAudioExtensions::getInstance().setMMapEnabled(wasMMapEnabled); mDeviceId = mStream->getDeviceId(); return (int32_t) result; } int32_t TestColdStartLatency::start() { mBeginStartNanos = AudioClock::getNanoseconds(); Result result = mStream->requestStart(); int64_t endStartNanos = AudioClock::getNanoseconds(); int64_t actualDurationNanos = endStartNanos - mBeginStartNanos; mStartTimeMicros = actualDurationNanos / NANOS_PER_MICROSECOND; return (int32_t) result; } int32_t TestColdStartLatency::close() { Result result1 = mStream->requestStop(); Result result2 = mStream->close(); return (int32_t)((result1 != Result::OK) ? result1 : result2); } int32_t TestColdStartLatency::getColdStartTimeMicros() { int64_t position; int64_t timestampNanos; if (mStream->getDirection() == Direction::Output) { auto result = mStream->getTimestamp(CLOCK_MONOTONIC); if (!result) { return -1; // ERROR } auto frameTimestamp = result.value(); // Calculate the time that frame[0] would have been played by the speaker. position = frameTimestamp.position; timestampNanos = frameTimestamp.timestamp; } else { position = mStream->getFramesRead(); timestampNanos = AudioClock::getNanoseconds(); } double sampleRate = (double) mStream->getSampleRate(); int64_t elapsedNanos = NANOS_PER_SECOND * (position / sampleRate); int64_t timeOfFrameZero = timestampNanos - elapsedNanos; int64_t coldStartLatencyNanos = timeOfFrameZero - mBeginStartNanos; return coldStartLatencyNanos / NANOS_PER_MICROSECOND; } // Callback that sleeps then touches the audio buffer. DataCallbackResult TestColdStartLatency::MyDataCallback::onAudioReady( AudioStream *audioStream, void *audioData, int32_t numFrames) { float *floatData = (float *) audioData; const int numSamples = numFrames * kChannelCount; if (audioStream->getDirection() == Direction::Output) { // Fill mono buffer with a sine wave. for (int i = 0; i < numSamples; i++) { *floatData++ = sinf(mPhase) * 0.2f; if ((i % kChannelCount) == (kChannelCount - 1)) { mPhase += kPhaseIncrement; // Wrap the phase around in a circle. if (mPhase >= M_PI) mPhase -= 2 * M_PI; } } } return DataCallbackResult::Continue; }