1 /*
2 * Copyright 2016 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 <cassert>
18 #include <stdint.h>
19 #include <stdlib.h>
20
21 #include "aaudio/AAudioLoader.h"
22 #include "aaudio/AudioStreamAAudio.h"
23 #include "common/AudioClock.h"
24 #include "common/OboeDebug.h"
25 #include "oboe/Utilities.h"
26
27 #ifdef __ANDROID__
28 #include <sys/system_properties.h>
29 #include <common/QuirksManager.h>
30
31 #endif
32
33 #ifndef OBOE_FIX_FORCE_STARTING_TO_STARTED
34 // Workaround state problems in AAudio
35 // TODO Which versions does this occur in? Verify fixed in Q.
36 #define OBOE_FIX_FORCE_STARTING_TO_STARTED 1
37 #endif // OBOE_FIX_FORCE_STARTING_TO_STARTED
38
39 using namespace oboe;
40 AAudioLoader *AudioStreamAAudio::mLibLoader = nullptr;
41
42 // 'C' wrapper for the data callback method
oboe_aaudio_data_callback_proc(AAudioStream * stream,void * userData,void * audioData,int32_t numFrames)43 static aaudio_data_callback_result_t oboe_aaudio_data_callback_proc(
44 AAudioStream *stream,
45 void *userData,
46 void *audioData,
47 int32_t numFrames) {
48
49 AudioStreamAAudio *oboeStream = reinterpret_cast<AudioStreamAAudio*>(userData);
50 if (oboeStream != nullptr) {
51 return static_cast<aaudio_data_callback_result_t>(
52 oboeStream->callOnAudioReady(stream, audioData, numFrames));
53
54 } else {
55 return static_cast<aaudio_data_callback_result_t>(DataCallbackResult::Stop);
56 }
57 }
58
59 // This runs in its own thread.
60 // Only one of these threads will be launched from internalErrorCallback().
61 // It calls app error callbacks from a static function in case the stream gets deleted.
oboe_aaudio_error_thread_proc(AudioStreamAAudio * oboeStream,Result error)62 static void oboe_aaudio_error_thread_proc(AudioStreamAAudio *oboeStream,
63 Result error) {
64 LOGD("%s() - entering >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>", __func__);
65 oboeStream->requestStop();
66 if (oboeStream->getCallback() != nullptr) {
67 oboeStream->getCallback()->onErrorBeforeClose(oboeStream, error);
68 }
69 oboeStream->close();
70 if (oboeStream->getCallback() != nullptr) {
71 // Warning, oboeStream may get deleted by this callback.
72 oboeStream->getCallback()->onErrorAfterClose(oboeStream, error);
73 }
74 LOGD("%s() - exiting <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<", __func__);
75 }
76
77 // This runs in its own thread.
78 // Only one of these threads will be launched from internalErrorCallback().
79 // Prevents deletion of the stream if the app is using AudioStreamBuilder::openSharedStream()
oboe_aaudio_error_thread_proc_shared(std::shared_ptr<AudioStream> sharedStream,Result error)80 static void oboe_aaudio_error_thread_proc_shared(std::shared_ptr<AudioStream> sharedStream,
81 Result error) {
82 AudioStreamAAudio *oboeStream = reinterpret_cast<AudioStreamAAudio*>(sharedStream.get());
83 oboe_aaudio_error_thread_proc(oboeStream, error);
84 }
85
86 namespace oboe {
87
88 /*
89 * Create a stream that uses Oboe Audio API.
90 */
AudioStreamAAudio(const AudioStreamBuilder & builder)91 AudioStreamAAudio::AudioStreamAAudio(const AudioStreamBuilder &builder)
92 : AudioStream(builder)
93 , mAAudioStream(nullptr) {
94 mCallbackThreadEnabled.store(false);
95 isSupported();
96 }
97
isSupported()98 bool AudioStreamAAudio::isSupported() {
99 mLibLoader = AAudioLoader::getInstance();
100 int openResult = mLibLoader->open();
101 return openResult == 0;
102 }
103
104 // Static 'C' wrapper for the error callback method.
105 // Launch a thread to handle the error.
106 // That other thread can safely stop, close and delete the stream.
internalErrorCallback(AAudioStream * stream,void * userData,aaudio_result_t error)107 void AudioStreamAAudio::internalErrorCallback(
108 AAudioStream *stream,
109 void *userData,
110 aaudio_result_t error) {
111 AudioStreamAAudio *oboeStream = reinterpret_cast<AudioStreamAAudio*>(userData);
112
113 // Prevents deletion of the stream if the app is using AudioStreamBuilder::openStream(shared_ptr)
114 std::shared_ptr<AudioStream> sharedStream = oboeStream->lockWeakThis();
115
116 // These checks should be enough because we assume that the stream close()
117 // will join() any active callback threads and will not allow new callbacks.
118 if (oboeStream->wasErrorCallbackCalled()) { // block extra error callbacks
119 LOGE("%s() multiple error callbacks called!", __func__);
120 } else if (stream != oboeStream->getUnderlyingStream()) {
121 LOGW("%s() stream already closed or closing", __func__); // can happen if there are bugs
122 } else if (sharedStream) {
123 // Handle error on a separate thread using shared pointer.
124 std::thread t(oboe_aaudio_error_thread_proc_shared, sharedStream,
125 static_cast<Result>(error));
126 t.detach();
127 } else {
128 // Handle error on a separate thread.
129 std::thread t(oboe_aaudio_error_thread_proc, oboeStream,
130 static_cast<Result>(error));
131 t.detach();
132 }
133 }
134
logUnsupportedAttributes()135 void AudioStreamAAudio::logUnsupportedAttributes() {
136 int sdkVersion = getSdkVersion();
137
138 // These attributes are not supported pre Android "P"
139 if (sdkVersion < __ANDROID_API_P__) {
140 if (mUsage != Usage::Media) {
141 LOGW("Usage [AudioStreamBuilder::setUsage()] "
142 "is not supported on AAudio streams running on pre-Android P versions.");
143 }
144
145 if (mContentType != ContentType::Music) {
146 LOGW("ContentType [AudioStreamBuilder::setContentType()] "
147 "is not supported on AAudio streams running on pre-Android P versions.");
148 }
149
150 if (mSessionId != SessionId::None) {
151 LOGW("SessionId [AudioStreamBuilder::setSessionId()] "
152 "is not supported on AAudio streams running on pre-Android P versions.");
153 }
154 }
155 }
156
open()157 Result AudioStreamAAudio::open() {
158 Result result = Result::OK;
159
160 if (mAAudioStream != nullptr) {
161 return Result::ErrorInvalidState;
162 }
163
164 result = AudioStream::open();
165 if (result != Result::OK) {
166 return result;
167 }
168
169 AAudioStreamBuilder *aaudioBuilder;
170 result = static_cast<Result>(mLibLoader->createStreamBuilder(&aaudioBuilder));
171 if (result != Result::OK) {
172 return result;
173 }
174
175 // Do not set INPUT capacity below 4096 because that prevents us from getting a FAST track
176 // when using the Legacy data path.
177 // If the app requests > 4096 then we allow it but we are less likely to get LowLatency.
178 // See internal bug b/80308183 for more details.
179 // Fixed in Q but let's still clip the capacity because high input capacity
180 // does not increase latency.
181 int32_t capacity = mBufferCapacityInFrames;
182 constexpr int kCapacityRequiredForFastLegacyTrack = 4096; // matches value in AudioFinger
183 if (OboeGlobals::areWorkaroundsEnabled()
184 && mDirection == oboe::Direction::Input
185 && capacity != oboe::Unspecified
186 && capacity < kCapacityRequiredForFastLegacyTrack
187 && mPerformanceMode == oboe::PerformanceMode::LowLatency) {
188 capacity = kCapacityRequiredForFastLegacyTrack;
189 LOGD("AudioStreamAAudio.open() capacity changed from %d to %d for lower latency",
190 static_cast<int>(mBufferCapacityInFrames), capacity);
191 }
192 mLibLoader->builder_setBufferCapacityInFrames(aaudioBuilder, capacity);
193
194 mLibLoader->builder_setChannelCount(aaudioBuilder, mChannelCount);
195 mLibLoader->builder_setDeviceId(aaudioBuilder, mDeviceId);
196 mLibLoader->builder_setDirection(aaudioBuilder, static_cast<aaudio_direction_t>(mDirection));
197 mLibLoader->builder_setFormat(aaudioBuilder, static_cast<aaudio_format_t>(mFormat));
198 mLibLoader->builder_setSampleRate(aaudioBuilder, mSampleRate);
199 mLibLoader->builder_setSharingMode(aaudioBuilder,
200 static_cast<aaudio_sharing_mode_t>(mSharingMode));
201 mLibLoader->builder_setPerformanceMode(aaudioBuilder,
202 static_cast<aaudio_performance_mode_t>(mPerformanceMode));
203
204 // These were added in P so we have to check for the function pointer.
205 if (mLibLoader->builder_setUsage != nullptr) {
206 mLibLoader->builder_setUsage(aaudioBuilder,
207 static_cast<aaudio_usage_t>(mUsage));
208 }
209
210 if (mLibLoader->builder_setContentType != nullptr) {
211 mLibLoader->builder_setContentType(aaudioBuilder,
212 static_cast<aaudio_content_type_t>(mContentType));
213 }
214
215 if (mLibLoader->builder_setInputPreset != nullptr) {
216 mLibLoader->builder_setInputPreset(aaudioBuilder,
217 static_cast<aaudio_input_preset_t>(mInputPreset));
218 }
219
220 if (mLibLoader->builder_setSessionId != nullptr) {
221 mLibLoader->builder_setSessionId(aaudioBuilder,
222 static_cast<aaudio_session_id_t>(mSessionId));
223 }
224
225 // TODO get more parameters from the builder?
226
227 if (mStreamCallback != nullptr) {
228 mLibLoader->builder_setDataCallback(aaudioBuilder, oboe_aaudio_data_callback_proc, this);
229 mLibLoader->builder_setFramesPerDataCallback(aaudioBuilder, getFramesPerCallback());
230 // If the data callback is not being used then the write method will return an error
231 // and the app can stop and close the stream.
232 mLibLoader->builder_setErrorCallback(aaudioBuilder, internalErrorCallback, this);
233 }
234
235 // ============= OPEN THE STREAM ================
236 {
237 AAudioStream *stream = nullptr;
238 result = static_cast<Result>(mLibLoader->builder_openStream(aaudioBuilder, &stream));
239 mAAudioStream.store(stream);
240 }
241 if (result != Result::OK) {
242 // Warn developer because ErrorInternal is not very informative.
243 if (result == Result::ErrorInternal && mDirection == Direction::Input) {
244 LOGW("AudioStreamAAudio.open() may have failed due to lack of "
245 "audio recording permission.");
246 }
247 goto error2;
248 }
249
250 // Query and cache the stream properties
251 mDeviceId = mLibLoader->stream_getDeviceId(mAAudioStream);
252 mChannelCount = mLibLoader->stream_getChannelCount(mAAudioStream);
253 mSampleRate = mLibLoader->stream_getSampleRate(mAAudioStream);
254 mFormat = static_cast<AudioFormat>(mLibLoader->stream_getFormat(mAAudioStream));
255 mSharingMode = static_cast<SharingMode>(mLibLoader->stream_getSharingMode(mAAudioStream));
256 mPerformanceMode = static_cast<PerformanceMode>(
257 mLibLoader->stream_getPerformanceMode(mAAudioStream));
258 mBufferCapacityInFrames = mLibLoader->stream_getBufferCapacity(mAAudioStream);
259 mBufferSizeInFrames = mLibLoader->stream_getBufferSize(mAAudioStream);
260
261 // These were added in P so we have to check for the function pointer.
262 if (mLibLoader->stream_getUsage != nullptr) {
263 mUsage = static_cast<Usage>(mLibLoader->stream_getUsage(mAAudioStream));
264 }
265 if (mLibLoader->stream_getContentType != nullptr) {
266 mContentType = static_cast<ContentType>(mLibLoader->stream_getContentType(mAAudioStream));
267 }
268 if (mLibLoader->stream_getInputPreset != nullptr) {
269 mInputPreset = static_cast<InputPreset>(mLibLoader->stream_getInputPreset(mAAudioStream));
270 }
271 if (mLibLoader->stream_getSessionId != nullptr) {
272 mSessionId = static_cast<SessionId>(mLibLoader->stream_getSessionId(mAAudioStream));
273 } else {
274 mSessionId = SessionId::None;
275 }
276
277 LOGD("AudioStreamAAudio.open() format=%d, sampleRate=%d, capacity = %d",
278 static_cast<int>(mFormat), static_cast<int>(mSampleRate),
279 static_cast<int>(mBufferCapacityInFrames));
280
281 error2:
282 mLibLoader->builder_delete(aaudioBuilder);
283 LOGD("AudioStreamAAudio.open: AAudioStream_Open() returned %s",
284 mLibLoader->convertResultToText(static_cast<aaudio_result_t>(result)));
285 return result;
286 }
287
close()288 Result AudioStreamAAudio::close() {
289 // Prevent two threads from closing the stream at the same time and crashing.
290 // This could occur, for example, if an application called close() at the same
291 // time that an onError callback was being executed because of a disconnect.
292 std::lock_guard<std::mutex> lock(mLock);
293
294 AudioStream::close();
295
296 // This will delete the AAudio stream object so we need to null out the pointer.
297 AAudioStream *stream = mAAudioStream.exchange(nullptr);
298 if (stream != nullptr) {
299 if (OboeGlobals::areWorkaroundsEnabled()) {
300 // Make sure we are really stopped. Do it under mLock
301 // so another thread cannot call requestStart() right before the close.
302 requestStop_l(stream);
303 // Sometimes a callback can occur shortly after a stream has been stopped and
304 // even after a close! If the stream has been closed then the callback
305 // can access memory that has been freed. That causes a crash.
306 // This seems to be more likely in Android P or earlier.
307 // But it can also occur in later versions.
308 usleep(kDelayBeforeCloseMillis * 1000);
309 }
310 return static_cast<Result>(mLibLoader->stream_close(stream));
311 } else {
312 return Result::ErrorClosed;
313 }
314 }
315
callOnAudioReady(AAudioStream * stream,void * audioData,int32_t numFrames)316 DataCallbackResult AudioStreamAAudio::callOnAudioReady(AAudioStream *stream,
317 void *audioData,
318 int32_t numFrames) {
319 DataCallbackResult result = fireDataCallback(audioData, numFrames);
320 if (result == DataCallbackResult::Continue) {
321 return result;
322 } else {
323 if (result == DataCallbackResult::Stop) {
324 LOGD("Oboe callback returned DataCallbackResult::Stop");
325 } else {
326 LOGE("Oboe callback returned unexpected value = %d", result);
327 }
328
329 if (getSdkVersion() <= __ANDROID_API_P__) {
330 launchStopThread();
331 if (isMMapUsed()) {
332 return DataCallbackResult::Stop;
333 } else {
334 // Legacy stream <= API_P cannot be restarted after returning Stop.
335 return DataCallbackResult::Continue;
336 }
337 } else {
338 return DataCallbackResult::Stop; // OK >= API_Q
339 }
340 }
341 }
342
requestStart()343 Result AudioStreamAAudio::requestStart() {
344 std::lock_guard<std::mutex> lock(mLock);
345 AAudioStream *stream = mAAudioStream.load();
346 if (stream != nullptr) {
347 // Avoid state machine errors in O_MR1.
348 if (getSdkVersion() <= __ANDROID_API_O_MR1__) {
349 StreamState state = static_cast<StreamState>(mLibLoader->stream_getState(stream));
350 if (state == StreamState::Starting || state == StreamState::Started) {
351 // WARNING: On P, AAudio is returning ErrorInvalidState for Output and OK for Input.
352 return Result::OK;
353 }
354 }
355 if (mStreamCallback != nullptr) { // Was a callback requested?
356 setDataCallbackEnabled(true);
357 }
358 return static_cast<Result>(mLibLoader->stream_requestStart(stream));
359 } else {
360 return Result::ErrorClosed;
361 }
362 }
363
requestPause()364 Result AudioStreamAAudio::requestPause() {
365 std::lock_guard<std::mutex> lock(mLock);
366 AAudioStream *stream = mAAudioStream.load();
367 if (stream != nullptr) {
368 // Avoid state machine errors in O_MR1.
369 if (getSdkVersion() <= __ANDROID_API_O_MR1__) {
370 StreamState state = static_cast<StreamState>(mLibLoader->stream_getState(stream));
371 if (state == StreamState::Pausing || state == StreamState::Paused) {
372 return Result::OK;
373 }
374 }
375 return static_cast<Result>(mLibLoader->stream_requestPause(stream));
376 } else {
377 return Result::ErrorClosed;
378 }
379 }
380
requestFlush()381 Result AudioStreamAAudio::requestFlush() {
382 std::lock_guard<std::mutex> lock(mLock);
383 AAudioStream *stream = mAAudioStream.load();
384 if (stream != nullptr) {
385 // Avoid state machine errors in O_MR1.
386 if (getSdkVersion() <= __ANDROID_API_O_MR1__) {
387 StreamState state = static_cast<StreamState>(mLibLoader->stream_getState(stream));
388 if (state == StreamState::Flushing || state == StreamState::Flushed) {
389 return Result::OK;
390 }
391 }
392 return static_cast<Result>(mLibLoader->stream_requestFlush(stream));
393 } else {
394 return Result::ErrorClosed;
395 }
396 }
397
requestStop()398 Result AudioStreamAAudio::requestStop() {
399 std::lock_guard<std::mutex> lock(mLock);
400 AAudioStream *stream = mAAudioStream.load();
401 if (stream != nullptr) {
402 return requestStop_l(stream);
403 } else {
404 return Result::ErrorClosed;
405 }
406 }
407
408 // Call under mLock
requestStop_l(AAudioStream * stream)409 Result AudioStreamAAudio::requestStop_l(AAudioStream *stream) {
410 // Avoid state machine errors in O_MR1.
411 if (getSdkVersion() <= __ANDROID_API_O_MR1__) {
412 StreamState state = static_cast<StreamState>(mLibLoader->stream_getState(stream));
413 if (state == StreamState::Stopping || state == StreamState::Stopped) {
414 return Result::OK;
415 }
416 }
417 return static_cast<Result>(mLibLoader->stream_requestStop(stream));
418 }
419
write(const void * buffer,int32_t numFrames,int64_t timeoutNanoseconds)420 ResultWithValue<int32_t> AudioStreamAAudio::write(const void *buffer,
421 int32_t numFrames,
422 int64_t timeoutNanoseconds) {
423 AAudioStream *stream = mAAudioStream.load();
424 if (stream != nullptr) {
425 int32_t result = mLibLoader->stream_write(mAAudioStream, buffer,
426 numFrames, timeoutNanoseconds);
427 return ResultWithValue<int32_t>::createBasedOnSign(result);
428 } else {
429 return ResultWithValue<int32_t>(Result::ErrorClosed);
430 }
431 }
432
read(void * buffer,int32_t numFrames,int64_t timeoutNanoseconds)433 ResultWithValue<int32_t> AudioStreamAAudio::read(void *buffer,
434 int32_t numFrames,
435 int64_t timeoutNanoseconds) {
436 AAudioStream *stream = mAAudioStream.load();
437 if (stream != nullptr) {
438 int32_t result = mLibLoader->stream_read(mAAudioStream, buffer,
439 numFrames, timeoutNanoseconds);
440 return ResultWithValue<int32_t>::createBasedOnSign(result);
441 } else {
442 return ResultWithValue<int32_t>(Result::ErrorClosed);
443 }
444 }
445
446
447 // AAudioStream_waitForStateChange() can crash if it is waiting on a stream and that stream
448 // is closed from another thread. We do not want to lock the stream for the duration of the call.
449 // So we call AAudioStream_waitForStateChange() with a timeout of zero so that it will not block.
450 // Then we can do our own sleep with the lock unlocked.
waitForStateChange(StreamState currentState,StreamState * nextState,int64_t timeoutNanoseconds)451 Result AudioStreamAAudio::waitForStateChange(StreamState currentState,
452 StreamState *nextState,
453 int64_t timeoutNanoseconds) {
454 Result oboeResult = Result::ErrorTimeout;
455 int64_t sleepTimeNanos = 20 * kNanosPerMillisecond; // arbitrary
456 aaudio_stream_state_t currentAAudioState = static_cast<aaudio_stream_state_t>(currentState);
457
458 aaudio_result_t result = AAUDIO_OK;
459 int64_t timeLeftNanos = timeoutNanoseconds;
460
461 mLock.lock();
462 while (true) {
463 // Do we still have an AAudio stream? If not then stream must have been closed.
464 AAudioStream *stream = mAAudioStream.load();
465 if (stream == nullptr) {
466 if (nextState != nullptr) {
467 *nextState = StreamState::Closed;
468 }
469 oboeResult = Result::ErrorClosed;
470 break;
471 }
472
473 // Update and query state change with no blocking.
474 aaudio_stream_state_t aaudioNextState;
475 result = mLibLoader->stream_waitForStateChange(
476 mAAudioStream,
477 currentAAudioState,
478 &aaudioNextState,
479 0); // timeout=0 for non-blocking
480 // AAudio will return AAUDIO_ERROR_TIMEOUT if timeout=0 and the state does not change.
481 if (result != AAUDIO_OK && result != AAUDIO_ERROR_TIMEOUT) {
482 oboeResult = static_cast<Result>(result);
483 break;
484 }
485 #if OBOE_FIX_FORCE_STARTING_TO_STARTED
486 if (OboeGlobals::areWorkaroundsEnabled()
487 && aaudioNextState == static_cast<aaudio_stream_state_t >(StreamState::Starting)) {
488 aaudioNextState = static_cast<aaudio_stream_state_t >(StreamState::Started);
489 }
490 #endif // OBOE_FIX_FORCE_STARTING_TO_STARTED
491 if (nextState != nullptr) {
492 *nextState = static_cast<StreamState>(aaudioNextState);
493 }
494 if (currentAAudioState != aaudioNextState) { // state changed?
495 oboeResult = Result::OK;
496 break;
497 }
498
499 // Did we timeout or did user ask for non-blocking?
500 if (timeLeftNanos <= 0) {
501 break;
502 }
503
504 // No change yet so sleep.
505 mLock.unlock(); // Don't sleep while locked.
506 if (sleepTimeNanos > timeLeftNanos) {
507 sleepTimeNanos = timeLeftNanos; // last little bit
508 }
509 AudioClock::sleepForNanos(sleepTimeNanos);
510 timeLeftNanos -= sleepTimeNanos;
511 mLock.lock();
512 }
513
514 mLock.unlock();
515 return oboeResult;
516 }
517
setBufferSizeInFrames(int32_t requestedFrames)518 ResultWithValue<int32_t> AudioStreamAAudio::setBufferSizeInFrames(int32_t requestedFrames) {
519
520 AAudioStream *stream = mAAudioStream.load();
521
522 if (stream != nullptr) {
523 int32_t adjustedFrames = requestedFrames;
524 if (adjustedFrames > mBufferCapacityInFrames) {
525 adjustedFrames = mBufferCapacityInFrames;
526 }
527 adjustedFrames = QuirksManager::getInstance().clipBufferSize(*this, adjustedFrames);
528
529 int32_t newBufferSize = mLibLoader->stream_setBufferSize(mAAudioStream, adjustedFrames);
530
531 // Cache the result if it's valid
532 if (newBufferSize > 0) mBufferSizeInFrames = newBufferSize;
533
534 return ResultWithValue<int32_t>::createBasedOnSign(newBufferSize);
535
536 } else {
537 return ResultWithValue<int32_t>(Result::ErrorClosed);
538 }
539 }
540
getState() const541 StreamState AudioStreamAAudio::getState() const {
542 AAudioStream *stream = mAAudioStream.load();
543 if (stream != nullptr) {
544 aaudio_stream_state_t aaudioState = mLibLoader->stream_getState(stream);
545 #if OBOE_FIX_FORCE_STARTING_TO_STARTED
546 if (OboeGlobals::areWorkaroundsEnabled()
547 && aaudioState == AAUDIO_STREAM_STATE_STARTING) {
548 aaudioState = AAUDIO_STREAM_STATE_STARTED;
549 }
550 #endif // OBOE_FIX_FORCE_STARTING_TO_STARTED
551 return static_cast<StreamState>(aaudioState);
552 } else {
553 return StreamState::Closed;
554 }
555 }
556
getBufferSizeInFrames()557 int32_t AudioStreamAAudio::getBufferSizeInFrames() {
558 AAudioStream *stream = mAAudioStream.load();
559 if (stream != nullptr) {
560 mBufferSizeInFrames = mLibLoader->stream_getBufferSize(stream);
561 }
562 return mBufferSizeInFrames;
563 }
564
getFramesPerBurst()565 int32_t AudioStreamAAudio::getFramesPerBurst() {
566 AAudioStream *stream = mAAudioStream.load();
567 if (stream != nullptr) {
568 mFramesPerBurst = mLibLoader->stream_getFramesPerBurst(stream);
569 }
570 return mFramesPerBurst;
571 }
572
updateFramesRead()573 void AudioStreamAAudio::updateFramesRead() {
574 AAudioStream *stream = mAAudioStream.load();
575 if (stream != nullptr) {
576 mFramesRead = mLibLoader->stream_getFramesRead(stream);
577 }
578 }
579
updateFramesWritten()580 void AudioStreamAAudio::updateFramesWritten() {
581 AAudioStream *stream = mAAudioStream.load();
582 if (stream != nullptr) {
583 mFramesWritten = mLibLoader->stream_getFramesWritten(stream);
584 }
585 }
586
getXRunCount() const587 ResultWithValue<int32_t> AudioStreamAAudio::getXRunCount() const {
588 AAudioStream *stream = mAAudioStream.load();
589 if (stream != nullptr) {
590 return ResultWithValue<int32_t>::createBasedOnSign(mLibLoader->stream_getXRunCount(stream));
591 } else {
592 return ResultWithValue<int32_t>(Result::ErrorNull);
593 }
594 }
595
getTimestamp(clockid_t clockId,int64_t * framePosition,int64_t * timeNanoseconds)596 Result AudioStreamAAudio::getTimestamp(clockid_t clockId,
597 int64_t *framePosition,
598 int64_t *timeNanoseconds) {
599 AAudioStream *stream = mAAudioStream.load();
600 if (stream != nullptr) {
601 if (getState() != StreamState::Started) {
602 return Result::ErrorInvalidState;
603 }
604 return static_cast<Result>(mLibLoader->stream_getTimestamp(stream, clockId,
605 framePosition, timeNanoseconds));
606 } else {
607 return Result::ErrorNull;
608 }
609 }
610
calculateLatencyMillis()611 ResultWithValue<double> AudioStreamAAudio::calculateLatencyMillis() {
612 AAudioStream *stream = mAAudioStream.load();
613 if (stream == nullptr) {
614 return ResultWithValue<double>(Result::ErrorClosed);
615 }
616
617 // Get the time that a known audio frame was presented.
618 int64_t hardwareFrameIndex;
619 int64_t hardwareFrameHardwareTime;
620 auto result = getTimestamp(CLOCK_MONOTONIC,
621 &hardwareFrameIndex,
622 &hardwareFrameHardwareTime);
623 if (result != oboe::Result::OK) {
624 return ResultWithValue<double>(static_cast<Result>(result));
625 }
626
627 // Get counter closest to the app.
628 bool isOutput = (getDirection() == oboe::Direction::Output);
629 int64_t appFrameIndex = isOutput ? getFramesWritten() : getFramesRead();
630
631 // Assume that the next frame will be processed at the current time
632 using namespace std::chrono;
633 int64_t appFrameAppTime =
634 duration_cast<nanoseconds>(steady_clock::now().time_since_epoch()).count();
635
636 // Calculate the number of frames between app and hardware
637 int64_t frameIndexDelta = appFrameIndex - hardwareFrameIndex;
638
639 // Calculate the time which the next frame will be or was presented
640 int64_t frameTimeDelta = (frameIndexDelta * oboe::kNanosPerSecond) / getSampleRate();
641 int64_t appFrameHardwareTime = hardwareFrameHardwareTime + frameTimeDelta;
642
643 // The current latency is the difference in time between when the current frame is at
644 // the app and when it is at the hardware.
645 double latencyNanos = static_cast<double>(isOutput
646 ? (appFrameHardwareTime - appFrameAppTime) // hardware is later
647 : (appFrameAppTime - appFrameHardwareTime)); // hardware is earlier
648 double latencyMillis = latencyNanos / kNanosPerMillisecond;
649
650 return ResultWithValue<double>(latencyMillis);
651 }
652
isMMapUsed()653 bool AudioStreamAAudio::isMMapUsed() {
654 AAudioStream *stream = mAAudioStream.load();
655 if (stream != nullptr) {
656 return mLibLoader->stream_isMMapUsed(stream);
657 } else {
658 return false;
659 }
660 }
661
662 } // namespace oboe
663