• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /**
2  * Copyright 2017 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 
18 #include <inttypes.h>
19 #include <memory>
20 
21 #include <Oscillator.h>
22 
23 #include "HelloOboeEngine.h"
24 #include "SoundGenerator.h"
25 
26 
27 /**
28  * Main audio engine for the HelloOboe sample. It is responsible for:
29  *
30  * - Creating a callback object which is supplied when constructing the audio stream, and will be
31  * called when the stream starts
32  * - Restarting the stream when user-controllable properties (Audio API, channel count etc) are
33  * changed, and when the stream is disconnected (e.g. when headphones are attached)
34  * - Calculating the audio latency of the stream
35  *
36  */
HelloOboeEngine()37 HelloOboeEngine::HelloOboeEngine()
38         : mLatencyCallback(std::make_unique<LatencyTuningCallback>()),
39         mErrorCallback(std::make_unique<DefaultErrorCallback>(*this)) {
40 }
41 
getCurrentOutputLatencyMillis()42 double HelloOboeEngine::getCurrentOutputLatencyMillis() {
43     if (!mIsLatencyDetectionSupported) return -1.0;
44 
45     std::lock_guard<std::mutex> lock(mLock);
46     if (!mStream) return -1.0;
47 
48     // Get the time that a known audio frame was presented for playing
49     auto result = mStream->getTimestamp(CLOCK_MONOTONIC);
50     double outputLatencyMillis = -1;
51     const int64_t kNanosPerMillisecond = 1000000;
52     if (result == oboe::Result::OK) {
53         oboe::FrameTimestamp playedFrame = result.value();
54         // Get the write index for the next audio frame
55         int64_t writeIndex = mStream->getFramesWritten();
56         // Calculate the number of frames between our known frame and the write index
57         int64_t frameIndexDelta = writeIndex - playedFrame.position;
58         // Calculate the time which the next frame will be presented
59         int64_t frameTimeDelta = (frameIndexDelta * oboe::kNanosPerSecond) /  (mStream->getSampleRate());
60         int64_t nextFramePresentationTime = playedFrame.timestamp + frameTimeDelta;
61         // Assume that the next frame will be written at the current time
62         using namespace std::chrono;
63         int64_t nextFrameWriteTime =
64                 duration_cast<nanoseconds>(steady_clock::now().time_since_epoch()).count();
65         // Calculate the latency
66         outputLatencyMillis = static_cast<double>(nextFramePresentationTime - nextFrameWriteTime)
67                          / kNanosPerMillisecond;
68     } else {
69         LOGE("Error calculating latency: %s", oboe::convertToText(result.error()));
70     }
71     return outputLatencyMillis;
72 }
73 
setBufferSizeInBursts(int32_t numBursts)74 void HelloOboeEngine::setBufferSizeInBursts(int32_t numBursts) {
75     std::lock_guard<std::mutex> lock(mLock);
76     if (!mStream) return;
77 
78     mLatencyCallback->setBufferTuneEnabled(numBursts == kBufferSizeAutomatic);
79     auto result = mStream->setBufferSizeInFrames(
80             numBursts * mStream->getFramesPerBurst());
81     if (result) {
82         LOGD("Buffer size successfully changed to %d", result.value());
83     } else {
84         LOGW("Buffer size could not be changed, %d", result.error());
85     }
86 }
87 
setAudioApi(oboe::AudioApi audioApi)88 void HelloOboeEngine::setAudioApi(oboe::AudioApi audioApi) {
89     mAudioApi = audioApi;
90     reopenStream();
91 }
92 
setChannelCount(int channelCount)93 void HelloOboeEngine::setChannelCount(int channelCount) {
94     mChannelCount = channelCount;
95     reopenStream();
96 }
97 
setDeviceId(int32_t deviceId)98 void HelloOboeEngine::setDeviceId(int32_t deviceId) {
99     mDeviceId = deviceId;
100     if (reopenStream() != oboe::Result::OK) {
101         LOGW("Open stream failed, forcing deviceId to Unspecified");
102         mDeviceId = oboe::Unspecified;
103     }
104 }
105 
isLatencyDetectionSupported()106 bool HelloOboeEngine::isLatencyDetectionSupported() {
107     return mIsLatencyDetectionSupported;
108 }
109 
tap(bool isDown)110 void HelloOboeEngine::tap(bool isDown) {
111     mAudioSource->tap(isDown);
112 }
113 
createPlaybackStream()114 oboe::Result HelloOboeEngine::createPlaybackStream() {
115     oboe::AudioStreamBuilder builder;
116     return builder.setSharingMode(oboe::SharingMode::Exclusive)
117         ->setPerformanceMode(oboe::PerformanceMode::LowLatency)
118         ->setFormat(oboe::AudioFormat::Float)
119         ->setDataCallback(mLatencyCallback.get())
120         ->setErrorCallback(mErrorCallback.get())
121         ->setAudioApi(mAudioApi)
122         ->setChannelCount(mChannelCount)
123         ->setDeviceId(mDeviceId)
124         ->openStream(mStream);
125 }
126 
restart()127 void HelloOboeEngine::restart() {
128     // The stream will have already been closed by the error callback.
129     mLatencyCallback->reset();
130     start();
131 }
132 
start()133 oboe::Result HelloOboeEngine::start() {
134     std::lock_guard<std::mutex> lock(mLock);
135 
136     auto result = createPlaybackStream();
137     if (result == oboe::Result::OK){
138         mAudioSource =  std::make_shared<SoundGenerator>(mStream->getSampleRate(),
139                 mStream->getChannelCount());
140         mLatencyCallback->setSource(std::dynamic_pointer_cast<IRenderableAudio>(mAudioSource));
141         mStream->start();
142         mIsLatencyDetectionSupported = (mStream->getTimestamp((CLOCK_MONOTONIC)) !=
143                                         oboe::Result::ErrorUnimplemented);
144 
145         LOGD("Stream opened: AudioAPI = %d, channelCount = %d, deviceID = %d",
146                 mStream->getAudioApi(),
147                 mStream->getChannelCount(),
148                 mStream->getDeviceId());
149     } else {
150         LOGE("Error creating playback stream. Error: %s", oboe::convertToText(result));
151         mIsLatencyDetectionSupported = false;
152     }
153     return result;
154 }
155 
stop()156 void HelloOboeEngine::stop() {
157     // Stop, close and delete in case not already closed.
158     std::lock_guard<std::mutex> lock(mLock);
159     if (mStream) {
160         mStream->stop();
161         mStream->close();
162         mStream.reset();
163     }
164 }
165 
reopenStream()166 oboe::Result HelloOboeEngine::reopenStream() {
167     stop();
168     return start();
169 }
170