xref: /aosp_15_r20/external/oboe/apps/OboeTester/app/src/main/cpp/TestColdStartLatency.cpp (revision 05767d913155b055644481607e6fa1e35e2fe72c)
1 /*
2  * Copyright 2023 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include <stdlib.h>
18 #include <aaudio/AAudioExtensions.h>
19 
20 #include "common/OboeDebug.h"
21 #include "common/AudioClock.h"
22 #include "TestColdStartLatency.h"
23 #include "OboeTools.h"
24 
25 using namespace oboe;
26 
open(bool useInput,bool useLowLatency,bool useMmap,bool useExclusive)27 int32_t TestColdStartLatency::open(bool useInput, bool useLowLatency, bool useMmap, bool
28         useExclusive) {
29 
30     mDataCallback = std::make_shared<MyDataCallback>();
31 
32     // Enable MMAP if needed
33     bool wasMMapEnabled = AAudioExtensions::getInstance().isMMapEnabled();
34     AAudioExtensions::getInstance().setMMapEnabled(useMmap);
35 
36     int64_t beginOpenNanos = AudioClock::getNanoseconds();
37 
38     AudioStreamBuilder builder;
39     Result result = builder.setFormat(AudioFormat::Float)
40             ->setPerformanceMode(useLowLatency ? PerformanceMode::LowLatency :
41                     PerformanceMode::None)
42             ->setDirection(useInput ? Direction::Input : Direction::Output)
43             ->setChannelCount(kChannelCount)
44             ->setDataCallback(mDataCallback)
45             ->setSharingMode(useExclusive ? SharingMode::Exclusive : SharingMode::Shared)
46             ->openStream(mStream);
47 
48     int64_t endOpenNanos = AudioClock::getNanoseconds();
49     int64_t actualDurationNanos = endOpenNanos - beginOpenNanos;
50     mOpenTimeMicros = actualDurationNanos / NANOS_PER_MICROSECOND;
51 
52     // Revert MMAP back to its previous state
53     AAudioExtensions::getInstance().setMMapEnabled(wasMMapEnabled);
54 
55     mDeviceId = mStream->getDeviceId();
56 
57     return (int32_t) result;
58 }
59 
start()60 int32_t TestColdStartLatency::start() {
61     mBeginStartNanos = AudioClock::getNanoseconds();
62     Result result = mStream->requestStart();
63     int64_t endStartNanos = AudioClock::getNanoseconds();
64     int64_t actualDurationNanos = endStartNanos - mBeginStartNanos;
65     mStartTimeMicros = actualDurationNanos / NANOS_PER_MICROSECOND;
66     return (int32_t) result;
67 }
68 
close()69 int32_t TestColdStartLatency::close() {
70     Result result1 = mStream->requestStop();
71     Result result2 = mStream->close();
72     return (int32_t)((result1 != Result::OK) ? result1 : result2);
73 }
74 
getColdStartTimeMicros()75 int32_t TestColdStartLatency::getColdStartTimeMicros() {
76     int64_t position;
77     int64_t timestampNanos;
78     if (mStream->getDirection() == Direction::Output) {
79         auto result = mStream->getTimestamp(CLOCK_MONOTONIC);
80         if (!result) {
81             return -1; // ERROR
82         }
83         auto frameTimestamp = result.value();
84         // Calculate the time that frame[0] would have been played by the speaker.
85         position = frameTimestamp.position;
86         timestampNanos = frameTimestamp.timestamp;
87     } else {
88         position = mStream->getFramesRead();
89         timestampNanos = AudioClock::getNanoseconds();
90     }
91     double sampleRate = (double) mStream->getSampleRate();
92 
93     int64_t elapsedNanos = NANOS_PER_SECOND * (position / sampleRate);
94     int64_t timeOfFrameZero = timestampNanos - elapsedNanos;
95     int64_t coldStartLatencyNanos = timeOfFrameZero - mBeginStartNanos;
96     return coldStartLatencyNanos / NANOS_PER_MICROSECOND;
97 }
98 
99 // Callback that sleeps then touches the audio buffer.
onAudioReady(AudioStream * audioStream,void * audioData,int32_t numFrames)100 DataCallbackResult TestColdStartLatency::MyDataCallback::onAudioReady(
101         AudioStream *audioStream,
102         void *audioData,
103         int32_t numFrames) {
104     float *floatData = (float *) audioData;
105     const int numSamples = numFrames * kChannelCount;
106     if (audioStream->getDirection() == Direction::Output) {
107         // Fill mono buffer with a sine wave.
108         for (int i = 0; i < numSamples; i++) {
109             *floatData++ = sinf(mPhase) * 0.2f;
110             if ((i % kChannelCount) == (kChannelCount - 1)) {
111                 mPhase += kPhaseIncrement;
112                 // Wrap the phase around in a circle.
113                 if (mPhase >= M_PI) mPhase -= 2 * M_PI;
114             }
115         }
116     }
117     return DataCallbackResult::Continue;
118 }
119