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