| Phil Burk | 87c9f64 | 2017-05-17 07:22:39 -0700 | [diff] [blame] | 1 | /* | 
 | 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 |  | 
 | 17 | #define LOG_TAG "AAudio" | 
 | 18 | //#define LOG_NDEBUG 0 | 
 | 19 | #include <utils/Log.h> | 
 | 20 |  | 
 | 21 | #include "client/AudioStreamInternalPlay.h" | 
 | 22 | #include "utility/AudioClock.h" | 
 | 23 |  | 
 | 24 | using android::WrappingBuffer; | 
 | 25 |  | 
 | 26 | using namespace aaudio; | 
 | 27 |  | 
 | 28 | AudioStreamInternalPlay::AudioStreamInternalPlay(AAudioServiceInterface  &serviceInterface, | 
 | 29 |                                                        bool inService) | 
 | 30 |         : AudioStreamInternal(serviceInterface, inService) { | 
 | 31 |  | 
 | 32 | } | 
 | 33 |  | 
 | 34 | AudioStreamInternalPlay::~AudioStreamInternalPlay() {} | 
 | 35 |  | 
 | 36 |  | 
 | 37 | // Write the data, block if needed and timeoutMillis > 0 | 
 | 38 | aaudio_result_t AudioStreamInternalPlay::write(const void *buffer, int32_t numFrames, | 
 | 39 |                                            int64_t timeoutNanoseconds) | 
 | 40 |  | 
 | 41 | { | 
 | 42 |     return processData((void *)buffer, numFrames, timeoutNanoseconds); | 
 | 43 | } | 
 | 44 |  | 
 | 45 | // Write as much data as we can without blocking. | 
 | 46 | aaudio_result_t AudioStreamInternalPlay::processDataNow(void *buffer, int32_t numFrames, | 
 | 47 |                                               int64_t currentNanoTime, int64_t *wakeTimePtr) { | 
 | 48 |     aaudio_result_t result = processCommands(); | 
 | 49 |     if (result != AAUDIO_OK) { | 
 | 50 |         return result; | 
 | 51 |     } | 
 | 52 |  | 
 | 53 |     if (mAudioEndpoint.isFreeRunning()) { | 
 | 54 |         //ALOGD("AudioStreamInternal::processDataNow() - update read counter"); | 
 | 55 |         // Update data queue based on the timing model. | 
 | 56 |         int64_t estimatedReadCounter = mClockModel.convertTimeToPosition(currentNanoTime); | 
 | 57 |         mAudioEndpoint.setDataReadCounter(estimatedReadCounter); | 
 | 58 |     } | 
 | 59 |     // TODO else query from endpoint cuz set by actual reader, maybe | 
 | 60 |  | 
 | 61 |     // If the read index passed the write index then consider it an underrun. | 
 | 62 |     if (mAudioEndpoint.getFullFramesAvailable() < 0) { | 
 | 63 |         mXRunCount++; | 
 | 64 |     } | 
 | 65 |  | 
 | 66 |     // Write some data to the buffer. | 
 | 67 |     //ALOGD("AudioStreamInternal::processDataNow() - writeNowWithConversion(%d)", numFrames); | 
 | 68 |     int32_t framesWritten = writeNowWithConversion(buffer, numFrames); | 
 | 69 |     //ALOGD("AudioStreamInternal::processDataNow() - tried to write %d frames, wrote %d", | 
 | 70 |     //    numFrames, framesWritten); | 
 | 71 |  | 
 | 72 |     // Calculate an ideal time to wake up. | 
 | 73 |     if (wakeTimePtr != nullptr && framesWritten >= 0) { | 
 | 74 |         // By default wake up a few milliseconds from now.  // TODO review | 
 | 75 |         int64_t wakeTime = currentNanoTime + (1 * AAUDIO_NANOS_PER_MILLISECOND); | 
 | 76 |         aaudio_stream_state_t state = getState(); | 
 | 77 |         //ALOGD("AudioStreamInternal::processDataNow() - wakeTime based on %s", | 
 | 78 |         //      AAudio_convertStreamStateToText(state)); | 
 | 79 |         switch (state) { | 
 | 80 |             case AAUDIO_STREAM_STATE_OPEN: | 
 | 81 |             case AAUDIO_STREAM_STATE_STARTING: | 
 | 82 |                 if (framesWritten != 0) { | 
 | 83 |                     // Don't wait to write more data. Just prime the buffer. | 
 | 84 |                     wakeTime = currentNanoTime; | 
 | 85 |                 } | 
 | 86 |                 break; | 
 | 87 |             case AAUDIO_STREAM_STATE_STARTED:   // When do we expect the next read burst to occur? | 
 | 88 |             { | 
 | 89 |                 uint32_t burstSize = mFramesPerBurst; | 
 | 90 |                 if (burstSize < 32) { | 
 | 91 |                     burstSize = 32; // TODO review | 
 | 92 |                 } | 
 | 93 |  | 
 | 94 |                 uint64_t nextReadPosition = mAudioEndpoint.getDataReadCounter() + burstSize; | 
 | 95 |                 wakeTime = mClockModel.convertPositionToTime(nextReadPosition); | 
 | 96 |             } | 
 | 97 |                 break; | 
 | 98 |             default: | 
 | 99 |                 break; | 
 | 100 |         } | 
 | 101 |         *wakeTimePtr = wakeTime; | 
 | 102 |  | 
 | 103 |     } | 
 | 104 | //    ALOGD("AudioStreamInternal::processDataNow finished: now = %llu, read# = %llu, wrote# = %llu", | 
 | 105 | //         (unsigned long long)currentNanoTime, | 
 | 106 | //         (unsigned long long)mAudioEndpoint.getDataReadCounter(), | 
 | 107 | //         (unsigned long long)mAudioEndpoint.getDownDataWriteCounter()); | 
 | 108 |     return framesWritten; | 
 | 109 | } | 
 | 110 |  | 
 | 111 |  | 
 | 112 | aaudio_result_t AudioStreamInternalPlay::writeNowWithConversion(const void *buffer, | 
 | 113 |                                                             int32_t numFrames) { | 
 | 114 |     // ALOGD("AudioStreamInternal::writeNowWithConversion(%p, %d)", | 
 | 115 |     //              buffer, numFrames); | 
 | 116 |     WrappingBuffer wrappingBuffer; | 
 | 117 |     uint8_t *source = (uint8_t *) buffer; | 
 | 118 |     int32_t framesLeft = numFrames; | 
 | 119 |  | 
 | 120 |     mAudioEndpoint.getEmptyFramesAvailable(&wrappingBuffer); | 
 | 121 |  | 
 | 122 |     // Read data in one or two parts. | 
 | 123 |     int partIndex = 0; | 
 | 124 |     while (framesLeft > 0 && partIndex < WrappingBuffer::SIZE) { | 
 | 125 |         int32_t framesToWrite = framesLeft; | 
 | 126 |         int32_t framesAvailable = wrappingBuffer.numFrames[partIndex]; | 
 | 127 |         if (framesAvailable > 0) { | 
 | 128 |             if (framesToWrite > framesAvailable) { | 
 | 129 |                 framesToWrite = framesAvailable; | 
 | 130 |             } | 
 | 131 |             int32_t numBytes = getBytesPerFrame() * framesToWrite; | 
 | 132 |             int32_t numSamples = framesToWrite * getSamplesPerFrame(); | 
 | 133 |             // Data conversion. | 
 | 134 |             float levelFrom; | 
 | 135 |             float levelTo; | 
 | 136 |             bool ramping = mVolumeRamp.nextSegment(framesToWrite * getSamplesPerFrame(), | 
 | 137 |                                                    &levelFrom, &levelTo); | 
 | 138 |             // The formats are validated when the stream is opened so we do not have to | 
 | 139 |             // check for illegal combinations here. | 
 | 140 |             // TODO factor this out into a utility function | 
 | 141 |             if (getFormat() == AAUDIO_FORMAT_PCM_FLOAT) { | 
 | 142 |                 if (mDeviceFormat == AAUDIO_FORMAT_PCM_FLOAT) { | 
 | 143 |                     AAudio_linearRamp( | 
 | 144 |                             (const float *) source, | 
 | 145 |                             (float *) wrappingBuffer.data[partIndex], | 
 | 146 |                             framesToWrite, | 
 | 147 |                             getSamplesPerFrame(), | 
 | 148 |                             levelFrom, | 
 | 149 |                             levelTo); | 
 | 150 |                 } else if (mDeviceFormat == AAUDIO_FORMAT_PCM_I16) { | 
 | 151 |                     if (ramping) { | 
 | 152 |                         AAudioConvert_floatToPcm16( | 
 | 153 |                                 (const float *) source, | 
 | 154 |                                 (int16_t *) wrappingBuffer.data[partIndex], | 
 | 155 |                                 framesToWrite, | 
 | 156 |                                 getSamplesPerFrame(), | 
 | 157 |                                 levelFrom, | 
 | 158 |                                 levelTo); | 
 | 159 |                     } else { | 
 | 160 |                         AAudioConvert_floatToPcm16( | 
 | 161 |                                 (const float *) source, | 
 | 162 |                                 (int16_t *) wrappingBuffer.data[partIndex], | 
 | 163 |                                 numSamples, | 
 | 164 |                                 levelTo); | 
 | 165 |                     } | 
 | 166 |                 } | 
 | 167 |             } else if (getFormat() == AAUDIO_FORMAT_PCM_I16) { | 
 | 168 |                 if (mDeviceFormat == AAUDIO_FORMAT_PCM_FLOAT) { | 
 | 169 |                     if (ramping) { | 
 | 170 |                         AAudioConvert_pcm16ToFloat( | 
 | 171 |                                 (const int16_t *) source, | 
 | 172 |                                 (float *) wrappingBuffer.data[partIndex], | 
 | 173 |                                 framesToWrite, | 
 | 174 |                                 getSamplesPerFrame(), | 
 | 175 |                                 levelFrom, | 
 | 176 |                                 levelTo); | 
 | 177 |                     } else { | 
 | 178 |                         AAudioConvert_pcm16ToFloat( | 
 | 179 |                                 (const int16_t *) source, | 
 | 180 |                                 (float *) wrappingBuffer.data[partIndex], | 
 | 181 |                                 numSamples, | 
 | 182 |                                 levelTo); | 
 | 183 |                     } | 
 | 184 |                 } else if (mDeviceFormat == AAUDIO_FORMAT_PCM_I16) { | 
 | 185 |                     AAudio_linearRamp( | 
 | 186 |                             (const int16_t *) source, | 
 | 187 |                             (int16_t *) wrappingBuffer.data[partIndex], | 
 | 188 |                             framesToWrite, | 
 | 189 |                             getSamplesPerFrame(), | 
 | 190 |                             levelFrom, | 
 | 191 |                             levelTo); | 
 | 192 |                 } | 
 | 193 |             } | 
 | 194 |             source += numBytes; | 
 | 195 |             framesLeft -= framesToWrite; | 
 | 196 |         } else { | 
 | 197 |             break; | 
 | 198 |         } | 
 | 199 |         partIndex++; | 
 | 200 |     } | 
 | 201 |     int32_t framesWritten = numFrames - framesLeft; | 
 | 202 |     mAudioEndpoint.advanceWriteIndex(framesWritten); | 
 | 203 |  | 
 | 204 |     if (framesWritten > 0) { | 
 | 205 |         incrementFramesWritten(framesWritten); | 
 | 206 |     } | 
 | 207 |     // ALOGD("AudioStreamInternal::writeNowWithConversion() returns %d", framesWritten); | 
 | 208 |     return framesWritten; | 
 | 209 | } | 
 | 210 |  | 
 | 211 |  | 
 | 212 | int64_t AudioStreamInternalPlay::getFramesRead() | 
 | 213 | { | 
 | 214 |     int64_t framesRead = | 
 | 215 |             mClockModel.convertTimeToPosition(AudioClock::getNanoseconds()) | 
 | 216 |             + mFramesOffsetFromService; | 
 | 217 |     // Prevent retrograde motion. | 
 | 218 |     if (framesRead < mLastFramesRead) { | 
 | 219 |         framesRead = mLastFramesRead; | 
 | 220 |     } else { | 
 | 221 |         mLastFramesRead = framesRead; | 
 | 222 |     } | 
 | 223 |     ALOGD("AudioStreamInternal::getFramesRead() returns %lld", (long long)framesRead); | 
 | 224 |     return framesRead; | 
 | 225 | } | 
 | 226 |  | 
 | 227 | int64_t AudioStreamInternalPlay::getFramesWritten() | 
 | 228 | { | 
 | 229 |     int64_t getFramesWritten = mAudioEndpoint.getDataWriteCounter() | 
 | 230 |                                + mFramesOffsetFromService; | 
 | 231 |     ALOGD("AudioStreamInternal::getFramesWritten() returns %lld", (long long)getFramesWritten); | 
 | 232 |     return getFramesWritten; | 
 | 233 | } | 
 | 234 |  | 
 | 235 |  | 
 | 236 | // Render audio in the application callback and then write the data to the stream. | 
 | 237 | void *AudioStreamInternalPlay::callbackLoop() { | 
 | 238 |     aaudio_result_t result = AAUDIO_OK; | 
 | 239 |     aaudio_data_callback_result_t callbackResult = AAUDIO_CALLBACK_RESULT_CONTINUE; | 
 | 240 |     AAudioStream_dataCallback appCallback = getDataCallbackProc(); | 
 | 241 |     if (appCallback == nullptr) return NULL; | 
 | 242 |  | 
 | 243 |     // result might be a frame count | 
 | 244 |     while (mCallbackEnabled.load() && isActive() && (result >= 0)) { | 
 | 245 |         // Call application using the AAudio callback interface. | 
 | 246 |         callbackResult = (*appCallback)( | 
 | 247 |                 (AAudioStream *) this, | 
 | 248 |                 getDataCallbackUserData(), | 
 | 249 |                 mCallbackBuffer, | 
 | 250 |                 mCallbackFrames); | 
 | 251 |  | 
 | 252 |         if (callbackResult == AAUDIO_CALLBACK_RESULT_CONTINUE) { | 
 | 253 |             // Write audio data to stream. | 
 | 254 |             int64_t timeoutNanos = calculateReasonableTimeout(mCallbackFrames); | 
 | 255 |  | 
 | 256 |             // This is a BLOCKING WRITE! | 
 | 257 |             result = write(mCallbackBuffer, mCallbackFrames, timeoutNanos); | 
 | 258 |             if ((result != mCallbackFrames)) { | 
 | 259 |                 ALOGE("AudioStreamInternalPlay(): callbackLoop: write() returned %d", result); | 
 | 260 |                 if (result >= 0) { | 
 | 261 |                     // Only wrote some of the frames requested. Must have timed out. | 
 | 262 |                     result = AAUDIO_ERROR_TIMEOUT; | 
 | 263 |                 } | 
 | 264 |                 AAudioStream_errorCallback errorCallback = getErrorCallbackProc(); | 
 | 265 |                 if (errorCallback != nullptr) { | 
 | 266 |                     (*errorCallback)( | 
 | 267 |                             (AAudioStream *) this, | 
 | 268 |                             getErrorCallbackUserData(), | 
 | 269 |                             result); | 
 | 270 |                 } | 
 | 271 |                 break; | 
 | 272 |             } | 
 | 273 |         } else if (callbackResult == AAUDIO_CALLBACK_RESULT_STOP) { | 
 | 274 |             ALOGD("AudioStreamInternalPlay(): callback returned AAUDIO_CALLBACK_RESULT_STOP"); | 
 | 275 |             break; | 
 | 276 |         } | 
 | 277 |     } | 
 | 278 |  | 
 | 279 |     ALOGD("AudioStreamInternalPlay(): callbackLoop() exiting, result = %d, isActive() = %d", | 
 | 280 |           result, (int) isActive()); | 
 | 281 |     return NULL; | 
 | 282 | } |