blob: 1b185774f107c4ca1ba2eeff0fd9b2bf415c0783 [file] [log] [blame]
Phil Burk87c9f642017-05-17 07:22:39 -07001/*
2 * Copyright (C) 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
Phil Burkec89b2e2017-06-20 15:05:06 -070017#define LOG_TAG (mInService ? "AAudioService" : "AAudio")
Phil Burk87c9f642017-05-17 07:22:39 -070018//#define LOG_NDEBUG 0
19#include <utils/Log.h>
20
21#include "client/AudioStreamInternalPlay.h"
22#include "utility/AudioClock.h"
23
24using android::WrappingBuffer;
25
26using namespace aaudio;
27
28AudioStreamInternalPlay::AudioStreamInternalPlay(AAudioServiceInterface &serviceInterface,
29 bool inService)
30 : AudioStreamInternal(serviceInterface, inService) {
31
32}
33
34AudioStreamInternalPlay::~AudioStreamInternalPlay() {}
35
36
Phil Burkb336e892017-07-05 15:35:43 -070037aaudio_result_t AudioStreamInternalPlay::requestPauseInternal()
38{
39 if (mServiceStreamHandle == AAUDIO_HANDLE_INVALID) {
40 ALOGE("AudioStreamInternal::requestPauseInternal() mServiceStreamHandle invalid = 0x%08X",
41 mServiceStreamHandle);
42 return AAUDIO_ERROR_INVALID_STATE;
43 }
44
45 mClockModel.stop(AudioClock::getNanoseconds());
46 setState(AAUDIO_STREAM_STATE_PAUSING);
47 return AAudioConvert_androidToAAudioResult(pauseWithStatus());
48}
49
50aaudio_result_t AudioStreamInternalPlay::requestPause()
51{
52 aaudio_result_t result = stopCallback();
53 if (result != AAUDIO_OK) {
54 return result;
55 }
56 result = requestPauseInternal();
57 return result;
58}
59
60aaudio_result_t AudioStreamInternalPlay::requestFlush() {
61 if (mServiceStreamHandle == AAUDIO_HANDLE_INVALID) {
62 ALOGE("AudioStreamInternal::requestFlush() mServiceStreamHandle invalid = 0x%08X",
63 mServiceStreamHandle);
64 return AAUDIO_ERROR_INVALID_STATE;
65 }
66
67 setState(AAUDIO_STREAM_STATE_FLUSHING);
68 return mServiceInterface.flushStream(mServiceStreamHandle);
69}
70
71void AudioStreamInternalPlay::onFlushFromServer() {
72 int64_t readCounter = mAudioEndpoint.getDataReadCounter();
73 int64_t writeCounter = mAudioEndpoint.getDataWriteCounter();
74
75 // Bump offset so caller does not see the retrograde motion in getFramesRead().
76 int64_t framesFlushed = writeCounter - readCounter;
77 mFramesOffsetFromService += framesFlushed;
78 ALOGD("AudioStreamInternal::onFlushFromServer() readN = %lld, writeN = %lld, offset = %lld",
79 (long long)readCounter, (long long)writeCounter, (long long)mFramesOffsetFromService);
80
81 // Flush written frames by forcing writeCounter to readCounter.
82 // This is because we cannot move the read counter in the hardware.
83 mAudioEndpoint.setDataWriteCounter(readCounter);
84}
85
Phil Burk87c9f642017-05-17 07:22:39 -070086// Write the data, block if needed and timeoutMillis > 0
87aaudio_result_t AudioStreamInternalPlay::write(const void *buffer, int32_t numFrames,
88 int64_t timeoutNanoseconds)
89
90{
91 return processData((void *)buffer, numFrames, timeoutNanoseconds);
92}
93
94// Write as much data as we can without blocking.
95aaudio_result_t AudioStreamInternalPlay::processDataNow(void *buffer, int32_t numFrames,
96 int64_t currentNanoTime, int64_t *wakeTimePtr) {
97 aaudio_result_t result = processCommands();
98 if (result != AAUDIO_OK) {
99 return result;
100 }
101
102 if (mAudioEndpoint.isFreeRunning()) {
Phil Burk87c9f642017-05-17 07:22:39 -0700103 // Update data queue based on the timing model.
104 int64_t estimatedReadCounter = mClockModel.convertTimeToPosition(currentNanoTime);
Phil Burkec89b2e2017-06-20 15:05:06 -0700105 // ALOGD("AudioStreamInternal::processDataNow() - estimatedReadCounter = %d", (int)estimatedReadCounter);
Phil Burk87c9f642017-05-17 07:22:39 -0700106 mAudioEndpoint.setDataReadCounter(estimatedReadCounter);
107 }
Phil Burk87c9f642017-05-17 07:22:39 -0700108
109 // If the read index passed the write index then consider it an underrun.
110 if (mAudioEndpoint.getFullFramesAvailable() < 0) {
Phil Burkec89b2e2017-06-20 15:05:06 -0700111 ALOGV("AudioStreamInternal::processDataNow() - XRun! write = %d, read = %d",
112 (int)mAudioEndpoint.getDataWriteCounter(),
113 (int)mAudioEndpoint.getDataReadCounter());
Phil Burk87c9f642017-05-17 07:22:39 -0700114 mXRunCount++;
115 }
116
117 // Write some data to the buffer.
118 //ALOGD("AudioStreamInternal::processDataNow() - writeNowWithConversion(%d)", numFrames);
119 int32_t framesWritten = writeNowWithConversion(buffer, numFrames);
120 //ALOGD("AudioStreamInternal::processDataNow() - tried to write %d frames, wrote %d",
121 // numFrames, framesWritten);
122
123 // Calculate an ideal time to wake up.
124 if (wakeTimePtr != nullptr && framesWritten >= 0) {
125 // By default wake up a few milliseconds from now. // TODO review
126 int64_t wakeTime = currentNanoTime + (1 * AAUDIO_NANOS_PER_MILLISECOND);
127 aaudio_stream_state_t state = getState();
128 //ALOGD("AudioStreamInternal::processDataNow() - wakeTime based on %s",
129 // AAudio_convertStreamStateToText(state));
130 switch (state) {
131 case AAUDIO_STREAM_STATE_OPEN:
132 case AAUDIO_STREAM_STATE_STARTING:
133 if (framesWritten != 0) {
134 // Don't wait to write more data. Just prime the buffer.
135 wakeTime = currentNanoTime;
136 }
137 break;
138 case AAUDIO_STREAM_STATE_STARTED: // When do we expect the next read burst to occur?
139 {
140 uint32_t burstSize = mFramesPerBurst;
141 if (burstSize < 32) {
142 burstSize = 32; // TODO review
143 }
144
145 uint64_t nextReadPosition = mAudioEndpoint.getDataReadCounter() + burstSize;
146 wakeTime = mClockModel.convertPositionToTime(nextReadPosition);
147 }
148 break;
149 default:
150 break;
151 }
152 *wakeTimePtr = wakeTime;
153
154 }
155// ALOGD("AudioStreamInternal::processDataNow finished: now = %llu, read# = %llu, wrote# = %llu",
156// (unsigned long long)currentNanoTime,
157// (unsigned long long)mAudioEndpoint.getDataReadCounter(),
158// (unsigned long long)mAudioEndpoint.getDownDataWriteCounter());
159 return framesWritten;
160}
161
162
163aaudio_result_t AudioStreamInternalPlay::writeNowWithConversion(const void *buffer,
164 int32_t numFrames) {
165 // ALOGD("AudioStreamInternal::writeNowWithConversion(%p, %d)",
166 // buffer, numFrames);
167 WrappingBuffer wrappingBuffer;
168 uint8_t *source = (uint8_t *) buffer;
169 int32_t framesLeft = numFrames;
170
171 mAudioEndpoint.getEmptyFramesAvailable(&wrappingBuffer);
172
173 // Read data in one or two parts.
174 int partIndex = 0;
175 while (framesLeft > 0 && partIndex < WrappingBuffer::SIZE) {
176 int32_t framesToWrite = framesLeft;
177 int32_t framesAvailable = wrappingBuffer.numFrames[partIndex];
178 if (framesAvailable > 0) {
179 if (framesToWrite > framesAvailable) {
180 framesToWrite = framesAvailable;
181 }
182 int32_t numBytes = getBytesPerFrame() * framesToWrite;
183 int32_t numSamples = framesToWrite * getSamplesPerFrame();
184 // Data conversion.
185 float levelFrom;
186 float levelTo;
187 bool ramping = mVolumeRamp.nextSegment(framesToWrite * getSamplesPerFrame(),
188 &levelFrom, &levelTo);
189 // The formats are validated when the stream is opened so we do not have to
190 // check for illegal combinations here.
191 // TODO factor this out into a utility function
192 if (getFormat() == AAUDIO_FORMAT_PCM_FLOAT) {
193 if (mDeviceFormat == AAUDIO_FORMAT_PCM_FLOAT) {
194 AAudio_linearRamp(
195 (const float *) source,
196 (float *) wrappingBuffer.data[partIndex],
197 framesToWrite,
198 getSamplesPerFrame(),
199 levelFrom,
200 levelTo);
201 } else if (mDeviceFormat == AAUDIO_FORMAT_PCM_I16) {
202 if (ramping) {
203 AAudioConvert_floatToPcm16(
204 (const float *) source,
205 (int16_t *) wrappingBuffer.data[partIndex],
206 framesToWrite,
207 getSamplesPerFrame(),
208 levelFrom,
209 levelTo);
210 } else {
211 AAudioConvert_floatToPcm16(
212 (const float *) source,
213 (int16_t *) wrappingBuffer.data[partIndex],
214 numSamples,
215 levelTo);
216 }
217 }
218 } else if (getFormat() == AAUDIO_FORMAT_PCM_I16) {
219 if (mDeviceFormat == AAUDIO_FORMAT_PCM_FLOAT) {
220 if (ramping) {
221 AAudioConvert_pcm16ToFloat(
222 (const int16_t *) source,
223 (float *) wrappingBuffer.data[partIndex],
224 framesToWrite,
225 getSamplesPerFrame(),
226 levelFrom,
227 levelTo);
228 } else {
229 AAudioConvert_pcm16ToFloat(
230 (const int16_t *) source,
231 (float *) wrappingBuffer.data[partIndex],
232 numSamples,
233 levelTo);
234 }
235 } else if (mDeviceFormat == AAUDIO_FORMAT_PCM_I16) {
236 AAudio_linearRamp(
237 (const int16_t *) source,
238 (int16_t *) wrappingBuffer.data[partIndex],
239 framesToWrite,
240 getSamplesPerFrame(),
241 levelFrom,
242 levelTo);
243 }
244 }
245 source += numBytes;
246 framesLeft -= framesToWrite;
247 } else {
248 break;
249 }
250 partIndex++;
251 }
252 int32_t framesWritten = numFrames - framesLeft;
253 mAudioEndpoint.advanceWriteIndex(framesWritten);
254
Phil Burk87c9f642017-05-17 07:22:39 -0700255 // ALOGD("AudioStreamInternal::writeNowWithConversion() returns %d", framesWritten);
256 return framesWritten;
257}
258
259
260int64_t AudioStreamInternalPlay::getFramesRead()
261{
Phil Burkec89b2e2017-06-20 15:05:06 -0700262 int64_t framesReadHardware;
263 if (isActive()) {
264 framesReadHardware = mClockModel.convertTimeToPosition(AudioClock::getNanoseconds());
265 } else {
266 framesReadHardware = mAudioEndpoint.getDataReadCounter();
267 }
268 int64_t framesRead = framesReadHardware + mFramesOffsetFromService;
Phil Burk87c9f642017-05-17 07:22:39 -0700269 // Prevent retrograde motion.
270 if (framesRead < mLastFramesRead) {
271 framesRead = mLastFramesRead;
272 } else {
273 mLastFramesRead = framesRead;
274 }
Phil Burkec89b2e2017-06-20 15:05:06 -0700275 //ALOGD("AudioStreamInternalPlay::getFramesRead() returns %lld", (long long)framesRead);
Phil Burk87c9f642017-05-17 07:22:39 -0700276 return framesRead;
277}
278
279int64_t AudioStreamInternalPlay::getFramesWritten()
280{
Phil Burkec89b2e2017-06-20 15:05:06 -0700281 int64_t framesWritten = mAudioEndpoint.getDataWriteCounter()
Phil Burk87c9f642017-05-17 07:22:39 -0700282 + mFramesOffsetFromService;
Phil Burkec89b2e2017-06-20 15:05:06 -0700283 //ALOGD("AudioStreamInternalPlay::getFramesWritten() returns %lld", (long long)framesWritten);
284 return framesWritten;
Phil Burk87c9f642017-05-17 07:22:39 -0700285}
286
287
288// Render audio in the application callback and then write the data to the stream.
289void *AudioStreamInternalPlay::callbackLoop() {
290 aaudio_result_t result = AAUDIO_OK;
291 aaudio_data_callback_result_t callbackResult = AAUDIO_CALLBACK_RESULT_CONTINUE;
292 AAudioStream_dataCallback appCallback = getDataCallbackProc();
293 if (appCallback == nullptr) return NULL;
294
295 // result might be a frame count
296 while (mCallbackEnabled.load() && isActive() && (result >= 0)) {
297 // Call application using the AAudio callback interface.
298 callbackResult = (*appCallback)(
299 (AAudioStream *) this,
300 getDataCallbackUserData(),
301 mCallbackBuffer,
302 mCallbackFrames);
303
304 if (callbackResult == AAUDIO_CALLBACK_RESULT_CONTINUE) {
305 // Write audio data to stream.
306 int64_t timeoutNanos = calculateReasonableTimeout(mCallbackFrames);
307
308 // This is a BLOCKING WRITE!
309 result = write(mCallbackBuffer, mCallbackFrames, timeoutNanos);
310 if ((result != mCallbackFrames)) {
311 ALOGE("AudioStreamInternalPlay(): callbackLoop: write() returned %d", result);
312 if (result >= 0) {
313 // Only wrote some of the frames requested. Must have timed out.
314 result = AAUDIO_ERROR_TIMEOUT;
315 }
316 AAudioStream_errorCallback errorCallback = getErrorCallbackProc();
317 if (errorCallback != nullptr) {
318 (*errorCallback)(
319 (AAudioStream *) this,
320 getErrorCallbackUserData(),
321 result);
322 }
323 break;
324 }
325 } else if (callbackResult == AAUDIO_CALLBACK_RESULT_STOP) {
326 ALOGD("AudioStreamInternalPlay(): callback returned AAUDIO_CALLBACK_RESULT_STOP");
327 break;
328 }
329 }
330
331 ALOGD("AudioStreamInternalPlay(): callbackLoop() exiting, result = %d, isActive() = %d",
332 result, (int) isActive());
333 return NULL;
334}