ssize_t FMRadioSource::readAt(off64_t offset, void *data, size_t size) { Buffer audioBuffer; if (!mStarted) { status_t err = mAudioRecord->start(AudioSystem::SYNC_EVENT_NONE, 0); if (err == OK) { mStarted = true; } else { ALOGE("Failed to start audio source"); return 0; } } // acquire a strong reference on the IAudioRecord and IMemory so that they cannot be destroyed // while we are accessing the cblk sp<IAudioRecord> audioRecord = mAudioRecord; sp<IMemory> iMem = mCblkMemory; audio_track_cblk_t* cblk = mCblk; audioBuffer.frameCount = size / cblk->frameSize; status_t err = obtainBuffer(&audioBuffer); if (err != NO_ERROR) { ALOGE("Error obtaining an audio buffer, giving up (err:%d).", err); return 0; } memcpy(data, audioBuffer.data, audioBuffer.size); mCblk->stepUser(audioBuffer.frameCount); return audioBuffer.size; }
status_t AudioRecord::obtainBuffer(Buffer* audioBuffer, int32_t waitCount) { if (audioBuffer == NULL) { return BAD_VALUE; } if (mTransfer != TRANSFER_OBTAIN) { audioBuffer->frameCount = 0; audioBuffer->size = 0; audioBuffer->raw = NULL; return INVALID_OPERATION; } const struct timespec *requested; struct timespec timeout; if (waitCount == -1) { requested = &ClientProxy::kForever; } else if (waitCount == 0) { requested = &ClientProxy::kNonBlocking; } else if (waitCount > 0) { long long ms = WAIT_PERIOD_MS * (long long) waitCount; timeout.tv_sec = ms / 1000; timeout.tv_nsec = (int) (ms % 1000) * 1000000; requested = &timeout; } else { ALOGE("%s invalid waitCount %d", __func__, waitCount); requested = NULL; } return obtainBuffer(audioBuffer, requested); }
ssize_t AudioTrack::write(const void* buffer, size_t userSize) { if (mSharedBuffer != 0) return INVALID_OPERATION; if (ssize_t(userSize) < 0) { // sanity-check. user is most-likely passing an error code. LOGE("AudioTrack::write(buffer=%p, size=%u (%d)", buffer, userSize, userSize); return BAD_VALUE; } LOGV("write %p: %d bytes, mActive=%d", this, userSize, mActive); // acquire a strong reference on the IMemory and IAudioTrack so that they cannot be destroyed // while we are accessing the cblk mLock.lock(); sp <IAudioTrack> audioTrack = mAudioTrack; sp <IMemory> iMem = mCblkMemory; mLock.unlock(); ssize_t written = 0; const int8_t *src = (const int8_t *)buffer; Buffer audioBuffer; size_t frameSz = (size_t)frameSize(); do { audioBuffer.frameCount = userSize/frameSz; // Calling obtainBuffer() with a negative wait count causes // an (almost) infinite wait time. status_t err = obtainBuffer(&audioBuffer, -1); if (err < 0) { // out of buffers, return #bytes written if (err == status_t(NO_MORE_BUFFERS)) break; return ssize_t(err); } size_t toWrite; if (mFormat == AUDIO_FORMAT_PCM_8_BIT && !(mFlags & AUDIO_POLICY_OUTPUT_FLAG_DIRECT)) { // Divide capacity by 2 to take expansion into account toWrite = audioBuffer.size>>1; // 8 to 16 bit conversion int count = toWrite; int16_t *dst = (int16_t *)(audioBuffer.i8); while(count--) { *dst++ = (int16_t)(*src++^0x80) << 8; } } else { toWrite = audioBuffer.size; memcpy(audioBuffer.i8, src, toWrite); src += toWrite; } userSize -= toWrite; written += toWrite; releaseBuffer(&audioBuffer); } while (userSize >= frameSz);
ssize_t AudioTrack::write(const void* buffer, size_t userSize) { if (mSharedBuffer != 0) return INVALID_OPERATION; if (ssize_t(userSize) < 0) { // sanity-check. user is most-likely passing an error code. LOGE("AudioTrack::write(buffer=%p, size=%u (%d)", buffer, userSize, userSize); return BAD_VALUE; } LOGV("write %p: %d bytes, mActive=%d", this, userSize, mActive); ssize_t written = 0; const int8_t *src = (const int8_t *)buffer; Buffer audioBuffer; do { audioBuffer.frameCount = userSize/frameSize(); // Calling obtainBuffer() with a negative wait count causes // an (almost) infinite wait time. status_t err = obtainBuffer(&audioBuffer, -1); if (err < 0) { // out of buffers, return #bytes written if (err == status_t(NO_MORE_BUFFERS)) break; return ssize_t(err); } size_t toWrite; if (mFormat == AudioSystem::PCM_8_BIT && !(mFlags & AudioSystem::OUTPUT_FLAG_DIRECT)) { // Divide capacity by 2 to take expansion into account toWrite = audioBuffer.size>>1; // 8 to 16 bit conversion int count = toWrite; int16_t *dst = (int16_t *)(audioBuffer.i8); while(count--) { *dst++ = (int16_t)(*src++^0x80) << 8; } } else { toWrite = audioBuffer.size; memcpy(audioBuffer.i8, src, toWrite); src += toWrite; } userSize -= toWrite; written += toWrite; releaseBuffer(&audioBuffer); } while (userSize);
ssize_t AudioRecord::read(void* buffer, size_t userSize) { ssize_t read = 0; Buffer audioBuffer; int8_t *dst = static_cast<int8_t*>(buffer); if (ssize_t(userSize) < 0) { // sanity-check. user is most-likely passing an error code. LOGE("AudioRecord::read(buffer=%p, size=%u (%d)", buffer, userSize, userSize); return BAD_VALUE; } mLock.lock(); // acquire a strong reference on the IAudioRecord and IMemory so that they cannot be destroyed // while we are accessing the cblk sp <IAudioRecord> audioRecord = mAudioRecord; sp <IMemory> iMem = mCblkMemory; mLock.unlock(); do { audioBuffer.frameCount = userSize/frameSize(); // By using a wait count corresponding to twice the timeout period in // obtainBuffer() we give a chance to recover once for a read timeout // (if media_server crashed for instance) before returning a length of // 0 bytes read to the client status_t err = obtainBuffer(&audioBuffer, ((2 * MAX_RUN_TIMEOUT_MS) / WAIT_PERIOD_MS)); if (err < 0) { // out of buffers, return #bytes written if (err == status_t(NO_MORE_BUFFERS)) break; if (err == status_t(TIMED_OUT)) err = 0; return ssize_t(err); } size_t bytesRead = audioBuffer.size; memcpy(dst, audioBuffer.i8, bytesRead); dst += bytesRead; userSize -= bytesRead; read += bytesRead; releaseBuffer(&audioBuffer); } while (userSize); return read; }
ssize_t AudioRecord::read(void* buffer, size_t userSize, bool blocking) { if (mTransfer != TRANSFER_SYNC) { return INVALID_OPERATION; } if (ssize_t(userSize) < 0 || (buffer == NULL && userSize != 0)) { // sanity-check. user is most-likely passing an error code, and it would // make the return value ambiguous (actualSize vs error). ALOGE("AudioRecord::read(buffer=%p, size=%zu (%zu)", buffer, userSize, userSize); return BAD_VALUE; } ssize_t read = 0; Buffer audioBuffer; while (userSize >= mFrameSize) { audioBuffer.frameCount = userSize / mFrameSize; status_t err = obtainBuffer(&audioBuffer, blocking ? &ClientProxy::kForever : &ClientProxy::kNonBlocking); if (err < 0) { if (read > 0) { break; } if (err == TIMED_OUT || err == -EINTR) { err = WOULD_BLOCK; } return ssize_t(err); } size_t bytesRead = audioBuffer.size; memcpy(buffer, audioBuffer.i8, bytesRead); buffer = ((char *) buffer) + bytesRead; userSize -= bytesRead; read += bytesRead; releaseBuffer(&audioBuffer); } if (read > 0) { mFramesRead += read / mFrameSize; // mFramesReadTime = systemTime(SYSTEM_TIME_MONOTONIC); // not provided at this time. } return read; }
ssize_t AudioRecord::read(void* buffer, size_t userSize) { ssize_t read = 0; Buffer audioBuffer; int8_t *dst = static_cast<int8_t*>(buffer); if (ssize_t(userSize) < 0) { // sanity-check. user is most-likely passing an error code. LOGE("AudioRecord::read(buffer=%p, size=%u (%d)", buffer, userSize, userSize); return BAD_VALUE; } LOGV("read size: %d", userSize); do { audioBuffer.frameCount = userSize/mChannelCount/sizeof(int16_t); // Calling obtainBuffer() with a negative wait count causes // an (almost) infinite wait time. status_t err = obtainBuffer(&audioBuffer, -1); if (err < 0) { // out of buffers, return #bytes written if (err == status_t(NO_MORE_BUFFERS)) break; return ssize_t(err); } size_t bytesRead = audioBuffer.size; memcpy(dst, audioBuffer.i8, bytesRead); dst += bytesRead; userSize -= bytesRead; read += bytesRead; releaseBuffer(&audioBuffer); } while (userSize); return read; }
ssize_t AudioRecord::read(void* buffer, size_t userSize) { if (mTransfer != TRANSFER_SYNC) { return INVALID_OPERATION; } if (ssize_t(userSize) < 0 || (buffer == NULL && userSize != 0)) { // sanity-check. user is most-likely passing an error code, and it would // make the return value ambiguous (actualSize vs error). ALOGE("AudioRecord::read(buffer=%p, size=%zu (%zu)", buffer, userSize, userSize); return BAD_VALUE; } ssize_t read = 0; Buffer audioBuffer; while (userSize >= mFrameSize) { audioBuffer.frameCount = userSize / mFrameSize; status_t err = obtainBuffer(&audioBuffer, &ClientProxy::kForever); if (err < 0) { if (read > 0) { break; } return ssize_t(err); } size_t bytesRead = audioBuffer.size; memcpy(buffer, audioBuffer.i8, bytesRead); buffer = ((char *) buffer) + bytesRead; userSize -= bytesRead; read += bytesRead; releaseBuffer(&audioBuffer); } return read; }
bool AudioRecord::processAudioBuffer(const sp<ClientRecordThread>& thread) { Buffer audioBuffer; uint32_t frames = mRemainingFrames; size_t readSize = 0; // Manage marker callback if (mMarkerPosition > 0) { if (mCblk->user >= mMarkerPosition) { mCbf(EVENT_MARKER, mUserData, (void *)&mMarkerPosition); mMarkerPosition = 0; } } // Manage new position callback if (mUpdatePeriod > 0) { while (mCblk->user >= mNewPosition) { mCbf(EVENT_NEW_POS, mUserData, (void *)&mNewPosition); mNewPosition += mUpdatePeriod; } } do { audioBuffer.frameCount = frames; status_t err = obtainBuffer(&audioBuffer, false); if (err < NO_ERROR) { if (err != WOULD_BLOCK) { LOGE("Error obtaining an audio buffer, giving up."); return false; } } if (err == status_t(STOPPED)) return false; if (audioBuffer.size == 0) break; size_t reqSize = audioBuffer.size; mCbf(EVENT_MORE_DATA, mUserData, &audioBuffer); readSize = audioBuffer.size; // Sanity check on returned size if (ssize_t(readSize) <= 0) break; if (readSize > reqSize) readSize = reqSize; audioBuffer.size = readSize; audioBuffer.frameCount = readSize/mChannelCount/sizeof(int16_t); frames -= audioBuffer.frameCount; releaseBuffer(&audioBuffer); } while (frames); // Manage overrun callback if (mActive && (mCblk->framesAvailable_l() == 0)) { LOGV("Overrun user: %x, server: %x, flowControlFlag %d", mCblk->user, mCblk->server, mCblk->flowControlFlag); if (mCblk->flowControlFlag == 0) { mCbf(EVENT_OVERRUN, mUserData, 0); mCblk->flowControlFlag = 1; } } // If no data was read, it is likely that obtainBuffer() did // not find available data in PCM buffer: we release the processor for // a few millisecond before polling again for available data. if (readSize == 0) { usleep(5000); } if (frames == 0) { mRemainingFrames = mNotificationFrames; } else { mRemainingFrames = frames; } return true; }
bool AudioRecord::processAudioBuffer(const sp<ClientRecordThread>& thread) { Buffer audioBuffer; uint32_t frames = mRemainingFrames; size_t readSize; // Manage marker callback if (!mMarkerReached && (mMarkerPosition > 0)) { if (mCblk->user >= mMarkerPosition) { mCbf(EVENT_MARKER, mUserData, (void *)&mMarkerPosition); mMarkerReached = true; } } // Manage new position callback if (mUpdatePeriod > 0) { while (mCblk->user >= mNewPosition) { mCbf(EVENT_NEW_POS, mUserData, (void *)&mNewPosition); mNewPosition += mUpdatePeriod; } } do { audioBuffer.frameCount = frames; // Calling obtainBuffer() with a wait count of 1 // limits wait time to WAIT_PERIOD_MS. This prevents from being // stuck here not being able to handle timed events (position, markers). status_t err = obtainBuffer(&audioBuffer, 1); if (err < NO_ERROR) { if (err != TIMED_OUT) { LOGE_IF(err != status_t(NO_MORE_BUFFERS), "Error obtaining an audio buffer, giving up."); return false; } break; } if (err == status_t(STOPPED)) return false; size_t reqSize = audioBuffer.size; mCbf(EVENT_MORE_DATA, mUserData, &audioBuffer); readSize = audioBuffer.size; // Sanity check on returned size if (ssize_t(readSize) <= 0) { // The callback is done filling buffers // Keep this thread going to handle timed events and // still try to get more data in intervals of WAIT_PERIOD_MS // but don't just loop and block the CPU, so wait usleep(WAIT_PERIOD_MS*1000); break; } if (readSize > reqSize) readSize = reqSize; audioBuffer.size = readSize; audioBuffer.frameCount = readSize/frameSize(); frames -= audioBuffer.frameCount; releaseBuffer(&audioBuffer); } while (frames); // Manage overrun callback if (mActive && (mCblk->framesAvailable_l() == 0)) { LOGV("Overrun user: %x, server: %x, flowControlFlag %d", mCblk->user, mCblk->server, mCblk->flowControlFlag); if (mCblk->flowControlFlag == 0) { mCbf(EVENT_OVERRUN, mUserData, 0); mCblk->flowControlFlag = 1; } } if (frames == 0) { mRemainingFrames = mNotificationFrames; } else { mRemainingFrames = frames; } return true; }
nsecs_t AudioRecord::processAudioBuffer() { mLock.lock(); if (mAwaitBoost) { mAwaitBoost = false; mLock.unlock(); static const int32_t kMaxTries = 5; int32_t tryCounter = kMaxTries; uint32_t pollUs = 10000; do { int policy = sched_getscheduler(0); if (policy == SCHED_FIFO || policy == SCHED_RR) { break; } usleep(pollUs); pollUs <<= 1; } while (tryCounter-- > 0); if (tryCounter < 0) { ALOGE("did not receive expected priority boost on time"); } // Run again immediately return 0; } // Can only reference mCblk while locked int32_t flags = android_atomic_and(~CBLK_OVERRUN, &mCblk->mFlags); // Check for track invalidation if (flags & CBLK_INVALID) { (void) restoreRecord_l("processAudioBuffer"); mLock.unlock(); // Run again immediately, but with a new IAudioRecord return 0; } bool active = mActive; // Manage overrun callback, must be done under lock to avoid race with releaseBuffer() bool newOverrun = false; if (flags & CBLK_OVERRUN) { if (!mInOverrun) { mInOverrun = true; newOverrun = true; } } // Get current position of server size_t position = mProxy->getPosition(); // Manage marker callback bool markerReached = false; size_t markerPosition = mMarkerPosition; // FIXME fails for wraparound, need 64 bits #ifdef MTK_AOSP_ENHANCEMENT if (!mMarkerReached && (markerPosition > 0) && (position >= markerPosition) && mActive) { #else if (!mMarkerReached && (markerPosition > 0) && (position >= markerPosition)) { #endif mMarkerReached = markerReached = true; } // Determine the number of new position callback(s) that will be needed, while locked size_t newPosCount = 0; size_t newPosition = mNewPosition; uint32_t updatePeriod = mUpdatePeriod; // FIXME fails for wraparound, need 64 bits if (updatePeriod > 0 && position >= newPosition) { newPosCount = ((position - newPosition) / updatePeriod) + 1; mNewPosition += updatePeriod * newPosCount; } // Cache other fields that will be needed soon uint32_t notificationFrames = mNotificationFramesAct; if (mRefreshRemaining) { mRefreshRemaining = false; mRemainingFrames = notificationFrames; mRetryOnPartialBuffer = false; } size_t misalignment = mProxy->getMisalignment(); uint32_t sequence = mSequence; // These fields don't need to be cached, because they are assigned only by set(): // mTransfer, mCbf, mUserData, mSampleRate, mFrameSize mLock.unlock(); // perform callbacks while unlocked if (newOverrun) { mCbf(EVENT_OVERRUN, mUserData, NULL); } if (markerReached) { mCbf(EVENT_MARKER, mUserData, &markerPosition); } while (newPosCount > 0) { size_t temp = newPosition; mCbf(EVENT_NEW_POS, mUserData, &temp); newPosition += updatePeriod; newPosCount--; } if (mObservedSequence != sequence) { mObservedSequence = sequence; mCbf(EVENT_NEW_IAUDIORECORD, mUserData, NULL); } // if inactive, then don't run me again until re-started if (!active) { return NS_INACTIVE; } // Compute the estimated time until the next timed event (position, markers) uint32_t minFrames = ~0; if (!markerReached && position < markerPosition) { minFrames = markerPosition - position; } if (updatePeriod > 0 && updatePeriod < minFrames) { minFrames = updatePeriod; } // If > 0, poll periodically to recover from a stuck server. A good value is 2. static const uint32_t kPoll = 0; if (kPoll > 0 && mTransfer == TRANSFER_CALLBACK && kPoll * notificationFrames < minFrames) { minFrames = kPoll * notificationFrames; } // Convert frame units to time units nsecs_t ns = NS_WHENEVER; if (minFrames != (uint32_t) ~0) { // This "fudge factor" avoids soaking CPU, and compensates for late progress by server static const nsecs_t kFudgeNs = 10000000LL; // 10 ms ns = ((minFrames * 1000000000LL) / mSampleRate) + kFudgeNs; } // If not supplying data by EVENT_MORE_DATA, then we're done if (mTransfer != TRANSFER_CALLBACK) { return ns; } struct timespec timeout; const struct timespec *requested = &ClientProxy::kForever; if (ns != NS_WHENEVER) { timeout.tv_sec = ns / 1000000000LL; timeout.tv_nsec = ns % 1000000000LL; ALOGV("timeout %ld.%03d", timeout.tv_sec, (int) timeout.tv_nsec / 1000000); requested = &timeout; } while (mRemainingFrames > 0) { Buffer audioBuffer; audioBuffer.frameCount = mRemainingFrames; size_t nonContig; status_t err = obtainBuffer(&audioBuffer, requested, NULL, &nonContig); LOG_ALWAYS_FATAL_IF((err != NO_ERROR) != (audioBuffer.frameCount == 0), "obtainBuffer() err=%d frameCount=%zu", err, audioBuffer.frameCount); requested = &ClientProxy::kNonBlocking; size_t avail = audioBuffer.frameCount + nonContig; ALOGV("obtainBuffer(%u) returned %zu = %zu + %zu err %d", mRemainingFrames, avail, audioBuffer.frameCount, nonContig, err); if (err != NO_ERROR) { /* #ifdef MTK_AOSP_ENHANCEMENT if (err != TIMED_OUT) mCbf(EVENT_WAIT_TIEMOUT, mUserData, 0); #endif */ if (err == TIMED_OUT || err == WOULD_BLOCK || err == -EINTR) { break; } ALOGE("Error %d obtaining an audio buffer, giving up.", err); return NS_NEVER; } if (mRetryOnPartialBuffer) { mRetryOnPartialBuffer = false; if (avail < mRemainingFrames) { int64_t myns = ((mRemainingFrames - avail) * 1100000000LL) / mSampleRate; if (ns < 0 || myns < ns) { ns = myns; } return ns; } } size_t reqSize = audioBuffer.size; mCbf(EVENT_MORE_DATA, mUserData, &audioBuffer); size_t readSize = audioBuffer.size; // Sanity check on returned size if (ssize_t(readSize) < 0 || readSize > reqSize) { ALOGE("EVENT_MORE_DATA requested %zu bytes but callback returned %zd bytes", reqSize, ssize_t(readSize)); return NS_NEVER; } if (readSize == 0) { // The callback is done consuming buffers // Keep this thread going to handle timed events and // still try to provide more data in intervals of WAIT_PERIOD_MS // but don't just loop and block the CPU, so wait return WAIT_PERIOD_MS * 1000000LL; } size_t releasedFrames = readSize / mFrameSize; audioBuffer.frameCount = releasedFrames; mRemainingFrames -= releasedFrames; if (misalignment >= releasedFrames) { misalignment -= releasedFrames; } else { misalignment = 0; } releaseBuffer(&audioBuffer); // FIXME here is where we would repeat EVENT_MORE_DATA again on same advanced buffer // if callback doesn't like to accept the full chunk if (readSize < reqSize) { continue; } // There could be enough non-contiguous frames available to satisfy the remaining request if (mRemainingFrames <= nonContig) { continue; } #if 0 // This heuristic tries to collapse a series of EVENT_MORE_DATA that would total to a // sum <= notificationFrames. It replaces that series by at most two EVENT_MORE_DATA // that total to a sum == notificationFrames. if (0 < misalignment && misalignment <= mRemainingFrames) { mRemainingFrames = misalignment; return (mRemainingFrames * 1100000000LL) / mSampleRate; } #endif } mRemainingFrames = notificationFrames; mRetryOnPartialBuffer = true; // A lot has transpired since ns was calculated, so run again immediately and re-calculate return 0; } status_t AudioRecord::restoreRecord_l(const char *from) { ALOGW("dead IAudioRecord, creating a new one from %s()", from); ++mSequence; status_t result; // if the new IAudioRecord is created, openRecord_l() will modify the // following member variables: mAudioRecord, mCblkMemory, mCblk, mBufferMemory. // It will also delete the strong references on previous IAudioRecord and IMemory size_t position = mProxy->getPosition(); mNewPosition = position + mUpdatePeriod; result = openRecord_l(position); if (result == NO_ERROR) { if (mActive) { // callback thread or sync event hasn't changed // FIXME this fails if we have a new AudioFlinger instance result = mAudioRecord->start(AudioSystem::SYNC_EVENT_SAME, 0); } } if (result != NO_ERROR) { ALOGW("restoreRecord_l() failed status %d", result); mActive = false; } return result; }
bool AudioRecord::processAudioBuffer(const sp<ClientRecordThread>& thread) { Buffer audioBuffer; uint32_t frames = mRemainingFrames; size_t readSize; mLock.lock(); // acquire a strong reference on the IAudioRecord and IMemory so that they cannot be destroyed // while we are accessing the cblk sp <IAudioRecord> audioRecord = mAudioRecord; sp <IMemory> iMem = mCblkMemory; audio_track_cblk_t* cblk = mCblk; mLock.unlock(); // Manage marker callback if (!mMarkerReached && (mMarkerPosition > 0)) { if (cblk->user >= mMarkerPosition) { mCbf(EVENT_MARKER, mUserData, (void *)&mMarkerPosition); mMarkerReached = true; } } // Manage new position callback if (mUpdatePeriod > 0) { while (cblk->user >= mNewPosition) { mCbf(EVENT_NEW_POS, mUserData, (void *)&mNewPosition); mNewPosition += mUpdatePeriod; } } do { audioBuffer.frameCount = frames; // Calling obtainBuffer() with a wait count of 1 // limits wait time to WAIT_PERIOD_MS. This prevents from being // stuck here not being able to handle timed events (position, markers). /*m@nufront start: modify for recording overflow*/ /*status_t err = obtainBuffer(&audioBuffer, 1);*/ status_t err = obtainBuffer(&audioBuffer, 5); if (err < NO_ERROR) { if (err != TIMED_OUT) { LOGE_IF(err != status_t(NO_MORE_BUFFERS), "Error obtaining an audio buffer, giving up."); return false; } break; } if (err == status_t(STOPPED)) return false; size_t reqSize = audioBuffer.size; mCbf(EVENT_MORE_DATA, mUserData, &audioBuffer); readSize = audioBuffer.size; // Sanity check on returned size if (ssize_t(readSize) <= 0) { // The callback is done filling buffers // Keep this thread going to handle timed events and // still try to get more data in intervals of WAIT_PERIOD_MS // but don't just loop and block the CPU, so wait usleep(WAIT_PERIOD_MS*1000); break; } if (readSize > reqSize) readSize = reqSize; audioBuffer.size = readSize; audioBuffer.frameCount = readSize/frameSize(); frames -= audioBuffer.frameCount; releaseBuffer(&audioBuffer); } while (frames); // Manage overrun callback if (mActive && (cblk->framesAvailable() == 0)) { LOGV("Overrun user: %x, server: %x, flags %04x", cblk->user, cblk->server, cblk->flags); if (!(android_atomic_or(CBLK_UNDERRUN_ON, &cblk->flags) & CBLK_UNDERRUN_MSK)) { mCbf(EVENT_OVERRUN, mUserData, 0); } } if (frames == 0) { mRemainingFrames = mNotificationFrames; } else { mRemainingFrames = frames; } return true; }