diff options
author | The Android Open Source Project <initial-contribution@android.com> | 2008-12-17 18:05:43 -0800 |
---|---|---|
committer | The Android Open Source Project <initial-contribution@android.com> | 2008-12-17 18:05:43 -0800 |
commit | f013e1afd1e68af5e3b868c26a653bbfb39538f8 (patch) | |
tree | 7ad6c8fd9c7b55f4b4017171dec1cb760bbd26bf /media/libmedia/AudioTrack.cpp | |
parent | e70cfafe580c6f2994c4827cd8a534aabf3eb05c (diff) | |
download | frameworks_base-f013e1afd1e68af5e3b868c26a653bbfb39538f8.zip frameworks_base-f013e1afd1e68af5e3b868c26a653bbfb39538f8.tar.gz frameworks_base-f013e1afd1e68af5e3b868c26a653bbfb39538f8.tar.bz2 |
Code drop from //branches/cupcake/...@124589
Diffstat (limited to 'media/libmedia/AudioTrack.cpp')
-rw-r--r-- | media/libmedia/AudioTrack.cpp | 684 |
1 files changed, 542 insertions, 142 deletions
diff --git a/media/libmedia/AudioTrack.cpp b/media/libmedia/AudioTrack.cpp index 298170a..d4f2e5a 100644 --- a/media/libmedia/AudioTrack.cpp +++ b/media/libmedia/AudioTrack.cpp @@ -2,16 +2,16 @@ ** ** Copyright 2007, The Android Open Source Project ** -** Licensed under the Apache License, Version 2.0 (the "License"); -** you may not use this file except in compliance with the License. -** You may obtain a copy of the License at +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at ** -** http://www.apache.org/licenses/LICENSE-2.0 +** http://www.apache.org/licenses/LICENSE-2.0 ** -** Unless required by applicable law or agreed to in writing, software -** distributed under the License is distributed on an "AS IS" BASIS, -** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -** See the License for the specific language governing permissions and +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and ** limitations under the License. */ @@ -21,6 +21,7 @@ #include <stdint.h> #include <sys/types.h> +#include <limits.h> #include <sched.h> #include <sys/resource.h> @@ -44,22 +45,25 @@ namespace android { // --------------------------------------------------------------------------- -static volatile size_t gFrameCount = 0; - -size_t AudioTrack::frameCount() +AudioTrack::AudioTrack() + : mStatus(NO_INIT) { - if (gFrameCount) return gFrameCount; - const sp<IAudioFlinger>& af = AudioSystem::get_audio_flinger(); - if (af == 0) return PERMISSION_DENIED; - gFrameCount = af->frameCount(); - return gFrameCount; } -// --------------------------------------------------------------------------- - -AudioTrack::AudioTrack() +AudioTrack::AudioTrack( + int streamType, + uint32_t sampleRate, + int format, + int channelCount, + int frameCount, + uint32_t flags, + callback_t cbf, + void* user, + int notificationFrames) : mStatus(NO_INIT) { + mStatus = set(streamType, sampleRate, format, channelCount, + frameCount, flags, cbf, user, notificationFrames, 0); } AudioTrack::AudioTrack( @@ -67,24 +71,28 @@ AudioTrack::AudioTrack( uint32_t sampleRate, int format, int channelCount, - int bufferCount, + const sp<IMemory>& sharedBuffer, uint32_t flags, - callback_t cbf, void* user) + callback_t cbf, + void* user, + int notificationFrames) : mStatus(NO_INIT) { mStatus = set(streamType, sampleRate, format, channelCount, - bufferCount, flags, cbf, user); + 0, flags, cbf, user, notificationFrames, sharedBuffer); } AudioTrack::~AudioTrack() { + LOGV_IF(mSharedBuffer != 0, "Destructor sharedBuffer: %p", mSharedBuffer->pointer()); + if (mStatus == NO_ERROR) { - if (mPosition) { - releaseBuffer(&mAudioBuffer); - } - // obtainBuffer() will give up with an error - mAudioTrack->stop(); + // Make sure that callback function exits in the case where + // it is looping on buffer full condition in obtainBuffer(). + // Otherwise the callback thread will never exit. + stop(); if (mAudioTrackThread != 0) { + mCblk->cv.signal(); mAudioTrackThread->requestExitAndWait(); mAudioTrackThread.clear(); } @@ -98,11 +106,17 @@ status_t AudioTrack::set( uint32_t sampleRate, int format, int channelCount, - int bufferCount, + int frameCount, uint32_t flags, - callback_t cbf, void* user) + callback_t cbf, + void* user, + int notificationFrames, + const sp<IMemory>& sharedBuffer, + bool threadCanCallJava) { + LOGV_IF(sharedBuffer != 0, "sharedBuffer: %p, size: %d", sharedBuffer->pointer(), sharedBuffer->size()); + if (mAudioFlinger != 0) { LOGE("Track already in use"); return INVALID_OPERATION; @@ -113,13 +127,26 @@ status_t AudioTrack::set( LOGE("Could not get audioflinger"); return NO_INIT; } + int afSampleRate; + if (AudioSystem::getOutputSamplingRate(&afSampleRate) != NO_ERROR) { + return NO_INIT; + } + int afFrameCount; + if (AudioSystem::getOutputFrameCount(&afFrameCount) != NO_ERROR) { + return NO_INIT; + } + uint32_t afLatency; + if (AudioSystem::getOutputLatency(&afLatency) != NO_ERROR) { + return NO_INIT; + } + // handle default values first. if (streamType == DEFAULT) { streamType = MUSIC; } if (sampleRate == 0) { - sampleRate = audioFlinger->sampleRate(); + sampleRate = afSampleRate; } // these below should probably come from the audioFlinger too... if (format == 0) { @@ -128,12 +155,10 @@ status_t AudioTrack::set( if (channelCount == 0) { channelCount = 2; } - if (bufferCount == 0) { - bufferCount = 2; - } // validate parameters - if (format != AudioSystem::PCM_16_BIT) { + if (((format != AudioSystem::PCM_8_BIT) || mSharedBuffer != 0) && + (format != AudioSystem::PCM_16_BIT)) { LOGE("Invalid format"); return BAD_VALUE; } @@ -141,17 +166,51 @@ status_t AudioTrack::set( LOGE("Invalid channel number"); return BAD_VALUE; } - if (bufferCount < 2) { - LOGE("Invalid buffer count"); - return BAD_VALUE; + + // Ensure that buffer depth covers at least audio hardware latency + uint32_t minBufCount = afLatency / ((1000 * afFrameCount)/afSampleRate); + // When playing from shared buffer, playback will start even if last audioflinger + // block is partly filled. + if (sharedBuffer != 0 && minBufCount > 1) { + minBufCount--; + } + + int minFrameCount = (afFrameCount*sampleRate*minBufCount)/afSampleRate; + + if (sharedBuffer == 0) { + if (frameCount == 0) { + frameCount = minFrameCount; + } + if (notificationFrames == 0) { + notificationFrames = frameCount/2; + } + // Make sure that application is notified with sufficient margin + // before underrun + if (notificationFrames > frameCount/2) { + notificationFrames = frameCount/2; + } + } else { + // Ensure that buffer alignment matches channelcount + if (((uint32_t)sharedBuffer->pointer() & (channelCount | 1)) != 0) { + LOGE("Invalid buffer alignement: address %p, channelCount %d", sharedBuffer->pointer(), channelCount); + return BAD_VALUE; + } + frameCount = sharedBuffer->size()/channelCount/sizeof(int16_t); + } + + if (frameCount < minFrameCount) { + LOGE("Invalid buffer size: minFrameCount %d, frameCount %d", minFrameCount, frameCount); + return BAD_VALUE; } // create the track + status_t status; sp<IAudioTrack> track = audioFlinger->createTrack(getpid(), - streamType, sampleRate, format, channelCount, bufferCount, flags); + streamType, sampleRate, format, channelCount, frameCount, flags, sharedBuffer, &status); + if (track == 0) { - LOGE("AudioFlinger could not create track"); - return NO_INIT; + LOGE("AudioFlinger could not create track, status: %d", status); + return status; } sp<IMemory> cblk = track->getCblk(); if (cblk == 0) { @@ -159,7 +218,7 @@ status_t AudioTrack::set( return NO_INIT; } if (cbf != 0) { - mAudioTrackThread = new AudioTrackThread(*this); + mAudioTrackThread = new AudioTrackThread(*this, threadCanCallJava); if (mAudioTrackThread == 0) { LOGE("Could not create callback thread"); return NO_INIT; @@ -172,23 +231,34 @@ status_t AudioTrack::set( mAudioTrack = track; mCblkMemory = cblk; mCblk = static_cast<audio_track_cblk_t*>(cblk->pointer()); - mCblk->buffers = (char*)mCblk + sizeof(audio_track_cblk_t); + if (sharedBuffer == 0) { + mCblk->buffers = (char*)mCblk + sizeof(audio_track_cblk_t); + } else { + mCblk->buffers = sharedBuffer->pointer(); + } + mCblk->out = 1; mCblk->volume[0] = mCblk->volume[1] = 0x1000; mVolume[LEFT] = 1.0f; mVolume[RIGHT] = 1.0f; mSampleRate = sampleRate; - mFrameCount = audioFlinger->frameCount(); mStreamType = streamType; mFormat = format; - mBufferCount = bufferCount; + // Update buffer size in case it has been limited by AudioFlinger during track creation + mFrameCount = mCblk->frameCount; mChannelCount = channelCount; + mSharedBuffer = sharedBuffer; mMuted = false; mActive = 0; - mReserved = 0; mCbf = cbf; + mNotificationFrames = notificationFrames; + mRemainingFrames = notificationFrames; mUserData = user; - mLatency = seconds(mFrameCount) / mSampleRate; - mPosition = 0; + mLatency = afLatency + (1000*mFrameCount) / mSampleRate; + mLoopCount = 0; + mMarkerPosition = 0; + mNewPosition = 0; + mUpdatePeriod = 0; + return NO_ERROR; } @@ -199,7 +269,7 @@ status_t AudioTrack::initCheck() const // ------------------------------------------------------------------------- -nsecs_t AudioTrack::latency() const +uint32_t AudioTrack::latency() const { return mLatency; } @@ -224,9 +294,19 @@ int AudioTrack::channelCount() const return mChannelCount; } -int AudioTrack::bufferCount() const +uint32_t AudioTrack::frameCount() const +{ + return mFrameCount; +} + +int AudioTrack::frameSize() const { - return mBufferCount; + return channelCount()*((format() == AudioSystem::PCM_8_BIT) ? sizeof(uint8_t) : sizeof(int16_t)); +} + +sp<IMemory>& AudioTrack::sharedBuffer() +{ + return mSharedBuffer; } // ------------------------------------------------------------------------- @@ -247,6 +327,12 @@ void AudioTrack::start() } if (android_atomic_or(1, &mActive) == 0) { + if (mSharedBuffer != 0) { + // Force buffer full condition as data is already present in shared memory + mCblk->user = mFrameCount; + mCblk->flowControlFlag = 0; + } + mNewPosition = mCblk->server + mUpdatePeriod; if (t != 0) { t->run("AudioTrackThread", THREAD_PRIORITY_AUDIO_CLIENT); } else { @@ -270,15 +356,20 @@ void AudioTrack::stop() } if (android_atomic_and(~1, &mActive) == 1) { - if (mPosition) { - releaseBuffer(&mAudioBuffer); - } mAudioTrack->stop(); - if (t != 0) { - t->requestExit(); - } else { - setpriority(PRIO_PROCESS, 0, ANDROID_PRIORITY_NORMAL); - } + // Cancel loops (If we are in the middle of a loop, playback + // would not stop until loopCount reaches 0). + setLoop(0, 0, 0); + // Force flush if a shared buffer is used otherwise audioflinger + // will not stop before end of buffer is reached. + if (mSharedBuffer != 0) { + flush(); + } + if (t != 0) { + t->requestExit(); + } else { + setpriority(PRIO_PROCESS, 0, ANDROID_PRIORITY_NORMAL); + } } if (t != 0) { @@ -294,6 +385,7 @@ bool AudioTrack::stopped() const void AudioTrack::flush() { LOGV("flush"); + if (!mActive) { mCblk->lock.lock(); mAudioTrack->flush(); @@ -341,7 +433,16 @@ void AudioTrack::getVolume(float* left, float* right) void AudioTrack::setSampleRate(int rate) { + int afSamplingRate; + + if (AudioSystem::getOutputSamplingRate(&afSamplingRate) != NO_ERROR) { + return; + } + // Resampler implementation limits input sampling rate to 2 x output sampling rate. + if (rate > afSamplingRate*2) rate = afSamplingRate*2; + if (rate > MAX_SAMPLE_RATE) rate = MAX_SAMPLE_RATE; + mCblk->sampleRate = rate; } @@ -350,6 +451,129 @@ uint32_t AudioTrack::getSampleRate() return uint32_t(mCblk->sampleRate); } +status_t AudioTrack::setLoop(uint32_t loopStart, uint32_t loopEnd, int loopCount) +{ + audio_track_cblk_t* cblk = mCblk; + + + Mutex::Autolock _l(cblk->lock); + + if (loopCount == 0) { + cblk->loopStart = UINT_MAX; + cblk->loopEnd = UINT_MAX; + cblk->loopCount = 0; + mLoopCount = 0; + return NO_ERROR; + } + + if (loopStart >= loopEnd || + loopStart < cblk->user || + loopEnd - loopStart > mFrameCount) { + LOGW("setLoop invalid value: loopStart %d, loopEnd %d, loopCount %d, framecount %d, user %d", loopStart, loopEnd, loopCount, mFrameCount, cblk->user); + return BAD_VALUE; + } + // TODO handle shared buffer here: limit loop end to framecount + + cblk->loopStart = loopStart; + cblk->loopEnd = loopEnd; + cblk->loopCount = loopCount; + mLoopCount = loopCount; + + return NO_ERROR; +} + +status_t AudioTrack::getLoop(uint32_t *loopStart, uint32_t *loopEnd, int *loopCount) +{ + if (loopStart != 0) { + *loopStart = mCblk->loopStart; + } + if (loopEnd != 0) { + *loopEnd = mCblk->loopEnd; + } + if (loopCount != 0) { + if (mCblk->loopCount < 0) { + *loopCount = -1; + } else { + *loopCount = mCblk->loopCount; + } + } + + return NO_ERROR; +} + +status_t AudioTrack::setMarkerPosition(uint32_t marker) +{ + if (mCbf == 0) return INVALID_OPERATION; + + mMarkerPosition = marker; + + return NO_ERROR; +} + +status_t AudioTrack::getMarkerPosition(uint32_t *marker) +{ + if (marker == 0) return BAD_VALUE; + + *marker = mMarkerPosition; + + return NO_ERROR; +} + +status_t AudioTrack::setPositionUpdatePeriod(uint32_t updatePeriod) +{ + if (mCbf == 0) return INVALID_OPERATION; + + uint32_t curPosition; + getPosition(&curPosition); + mNewPosition = curPosition + updatePeriod; + mUpdatePeriod = updatePeriod; + + return NO_ERROR; +} + +status_t AudioTrack::getPositionUpdatePeriod(uint32_t *updatePeriod) +{ + if (updatePeriod == 0) return BAD_VALUE; + + *updatePeriod = mUpdatePeriod; + + return NO_ERROR; +} + +status_t AudioTrack::setPosition(uint32_t position) +{ + Mutex::Autolock _l(mCblk->lock); + + if (!stopped()) return INVALID_OPERATION; + + if (position > mCblk->user) return BAD_VALUE; + + mCblk->server = position; + mCblk->forceReady = 1; + + return NO_ERROR; +} + +status_t AudioTrack::getPosition(uint32_t *position) +{ + if (position == 0) return BAD_VALUE; + + *position = mCblk->server; + + return NO_ERROR; +} + +status_t AudioTrack::reload() +{ + if (!stopped()) return INVALID_OPERATION; + + flush(); + + mCblk->stepUser(mFrameCount); + + return NO_ERROR; +} + // ------------------------------------------------------------------------- status_t AudioTrack::obtainBuffer(Buffer* audioBuffer, bool blocking) @@ -358,21 +582,17 @@ status_t AudioTrack::obtainBuffer(Buffer* audioBuffer, bool blocking) int timeout = 0; status_t result; audio_track_cblk_t* cblk = mCblk; + uint32_t framesReq = audioBuffer->frameCount; - uint32_t u = cblk->user; - uint32_t u_seq = u & audio_track_cblk_t::SEQUENCE_MASK; - uint32_t u_buf = u & audio_track_cblk_t::BUFFER_MASK; + audioBuffer->frameCount = 0; + audioBuffer->size = 0; - uint32_t s = cblk->server; - uint32_t s_seq = s & audio_track_cblk_t::SEQUENCE_MASK; - uint32_t s_buf = s & audio_track_cblk_t::BUFFER_MASK; + uint32_t framesAvail = cblk->framesAvailable(); - LOGW_IF(u_seq < s_seq, "user doesn't fill buffers fast enough"); - - if (u_seq > s_seq && u_buf == s_buf) { + if (framesAvail == 0) { Mutex::Autolock _l(cblk->lock); goto start_loop_here; - while (u_seq > s_seq && u_buf == s_buf) { + while (framesAvail == 0) { active = mActive; if (UNLIKELY(!active)) { LOGV("Not active and NO_MORE_BUFFERS"); @@ -384,89 +604,101 @@ status_t AudioTrack::obtainBuffer(Buffer* audioBuffer, bool blocking) result = cblk->cv.waitRelative(cblk->lock, seconds(1)); if (__builtin_expect(result!=NO_ERROR, false)) { LOGW( "obtainBuffer timed out (is the CPU pegged?) " - "user=%08x, server=%08x", u, s); + "user=%08x, server=%08x", cblk->user, cblk->server); mAudioTrack->start(); // FIXME: Wake up audioflinger timeout = 1; } - // Read user count in case a flush has reset while we where waiting on cv. - u = cblk->user; - u_seq = u & audio_track_cblk_t::SEQUENCE_MASK; - u_buf = u & audio_track_cblk_t::BUFFER_MASK; - // read the server count again start_loop_here: - s = cblk->server; - s_seq = s & audio_track_cblk_t::SEQUENCE_MASK; - s_buf = s & audio_track_cblk_t::BUFFER_MASK; + framesAvail = cblk->framesAvailable_l(); } } + if (framesReq > framesAvail) { + framesReq = framesAvail; + } + + uint32_t u = cblk->user; + uint32_t bufferEnd = cblk->userBase + cblk->frameCount; + + if (u + framesReq > bufferEnd) { + framesReq = bufferEnd - u; + } + LOGW_IF(timeout, "*** SERIOUS WARNING *** obtainBuffer() timed out " "but didn't need to be locked. We recovered, but " - "this shouldn't happen (user=%08x, server=%08x)", u, s); + "this shouldn't happen (user=%08x, server=%08x)", cblk->user, cblk->server); audioBuffer->flags = mMuted ? Buffer::MUTE : 0; audioBuffer->channelCount= mChannelCount; - audioBuffer->format = mFormat; - audioBuffer->frameCount = mFrameCount; - audioBuffer->size = cblk->size; - audioBuffer->raw = (int8_t *)cblk->buffer(u_buf); + audioBuffer->format = AudioSystem::PCM_16_BIT; + audioBuffer->frameCount = framesReq; + audioBuffer->size = framesReq*mChannelCount*sizeof(int16_t); + audioBuffer->raw = (int8_t *)cblk->buffer(u); active = mActive; return active ? status_t(NO_ERROR) : status_t(STOPPED); } void AudioTrack::releaseBuffer(Buffer* audioBuffer) { - // next buffer... - if (UNLIKELY(mPosition)) { - // clean the remaining part of the buffer - size_t capacity = mAudioBuffer.size - mPosition; - memset(mAudioBuffer.i8 + mPosition, 0, capacity); - mPosition = 0; - } audio_track_cblk_t* cblk = mCblk; - cblk->stepUser(mBufferCount); + cblk->stepUser(audioBuffer->frameCount); } // ------------------------------------------------------------------------- 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)", + LOGE("AudioTrack::write(buffer=%p, size=%u (%d)", buffer, userSize, userSize); return BAD_VALUE; } LOGV("write %d bytes, mActive=%d", userSize, mActive); + ssize_t written = 0; + const int8_t *src = (const int8_t *)buffer; + Buffer audioBuffer; + do { - if (mPosition == 0) { - status_t err = obtainBuffer(&mAudioBuffer, true); - if (err < 0) { - // out of buffers, return #bytes written - if (err == status_t(NO_MORE_BUFFERS)) - break; - return ssize_t(err); - } + audioBuffer.frameCount = userSize/mChannelCount; + if (mFormat == AudioSystem::PCM_16_BIT) { + audioBuffer.frameCount >>= 1; } - size_t capacity = mAudioBuffer.size - mPosition; - size_t toWrite = userSize < capacity ? userSize : capacity; + status_t err = obtainBuffer(&audioBuffer, true); + if (err < 0) { + // out of buffers, return #bytes written + if (err == status_t(NO_MORE_BUFFERS)) + break; + return ssize_t(err); + } - memcpy(mAudioBuffer.i8 + mPosition, buffer, toWrite); - buffer = static_cast<const int8_t*>(buffer) + toWrite; - mPosition += toWrite; + size_t toWrite; + if (mFormat == AudioSystem::PCM_8_BIT) { + // 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; - capacity -= toWrite; written += toWrite; - if (capacity == 0) { - mPosition = 0; - releaseBuffer(&mAudioBuffer); - } + releaseBuffer(&audioBuffer); } while (userSize); return written; @@ -477,16 +709,115 @@ ssize_t AudioTrack::write(const void* buffer, size_t userSize) bool AudioTrack::processAudioBuffer(const sp<AudioTrackThread>& thread) { Buffer audioBuffer; + uint32_t frames; + size_t writtenSize = 0; + + // Manage underrun callback + if (mActive && (mCblk->framesReady() == 0)) { + LOGV("Underrun user: %x, server: %x, flowControlFlag %d", mCblk->user, mCblk->server, mCblk->flowControlFlag); + if (mCblk->flowControlFlag == 0) { + mCbf(EVENT_UNDERRUN, mUserData, 0); + if (mCblk->server == mCblk->frameCount) { + mCbf(EVENT_BUFFER_END, mUserData, 0); + } + mCblk->flowControlFlag = 1; + if (mSharedBuffer != 0) return false; + } + } + + // Manage loop end callback + while (mLoopCount > mCblk->loopCount) { + int loopCount = -1; + mLoopCount--; + if (mLoopCount >= 0) loopCount = mLoopCount; + + mCbf(EVENT_LOOP_END, mUserData, (void *)&loopCount); + } - status_t err = obtainBuffer(&audioBuffer, true); - if (err < NO_ERROR) { - LOGE("Error obtaining an audio buffer, giving up."); - return false; + // Manage marker callback + if(mMarkerPosition > 0) { + if (mCblk->server >= mMarkerPosition) { + mCbf(EVENT_MARKER, mUserData, (void *)&mMarkerPosition); + mMarkerPosition = 0; + } } - if (err == status_t(STOPPED)) return false; - mCbf(mUserData, audioBuffer); - releaseBuffer(&audioBuffer); + // Manage new position callback + if(mUpdatePeriod > 0) { + while (mCblk->server >= mNewPosition) { + mCbf(EVENT_NEW_POS, mUserData, (void *)&mNewPosition); + mNewPosition += mUpdatePeriod; + } + } + + // If Shared buffer is used, no data is requested from client. + if (mSharedBuffer != 0) { + frames = 0; + } else { + frames = mRemainingFrames; + } + + 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; + + // Divide buffer size by 2 to take into account the expansion + // due to 8 to 16 bit conversion: the callback must fill only half + // of the destination buffer + if (mFormat == AudioSystem::PCM_8_BIT) { + audioBuffer.size >>= 1; + } + + size_t reqSize = audioBuffer.size; + mCbf(EVENT_MORE_DATA, mUserData, &audioBuffer); + writtenSize = audioBuffer.size; + + // Sanity check on returned size + if (ssize_t(writtenSize) <= 0) break; + if (writtenSize > reqSize) writtenSize = reqSize; + + if (mFormat == AudioSystem::PCM_8_BIT) { + // 8 to 16 bit conversion + const int8_t *src = audioBuffer.i8 + writtenSize-1; + int count = writtenSize; + int16_t *dst = audioBuffer.i16 + writtenSize-1; + while(count--) { + *dst-- = (int16_t)(*src--^0x80) << 8; + } + writtenSize <<= 1; + } + + audioBuffer.size = writtenSize; + audioBuffer.frameCount = writtenSize/mChannelCount/sizeof(int16_t); + frames -= audioBuffer.frameCount; + + releaseBuffer(&audioBuffer); + } + while (frames); + + // If no data was written, it is likely that obtainBuffer() did + // not find room in PCM buffer: we release the processor for + // a few millisecond before polling again for available room. + if (writtenSize == 0) { + usleep(5000); + } + + if (frames == 0) { + mRemainingFrames = mNotificationFrames; + } else { + mRemainingFrames = frames; + } return true; } @@ -500,11 +831,11 @@ status_t AudioTrack::dump(int fd, const Vector<String16>& args) const result.append(" AudioTrack::dump\n"); snprintf(buffer, 255, " stream type(%d), left - right volume(%f, %f)\n", mStreamType, mVolume[0], mVolume[1]); result.append(buffer); - snprintf(buffer, 255, " format(%d), channel count(%d), frame count(%d), buffer count(%d)\n", mFormat, mChannelCount, mFrameCount, mBufferCount); + snprintf(buffer, 255, " format(%d), channel count(%d), frame count(%d)\n", mFormat, mChannelCount, mFrameCount); result.append(buffer); - snprintf(buffer, 255, " sample rate(%d), status(%d), muted(%d), reserved(%d)\n", mSampleRate, mStatus, mMuted, mReserved); + snprintf(buffer, 255, " sample rate(%d), status(%d), muted(%d)\n", mSampleRate, mStatus, mMuted); result.append(buffer); - snprintf(buffer, 255, " active(%d), latency (%lld), position(%d)\n", mActive, mLatency, mPosition); + snprintf(buffer, 255, " active(%d), latency (%d)\n", mActive, mLatency); result.append(buffer); ::write(fd, result.string(), result.size()); return NO_ERROR; @@ -512,8 +843,8 @@ status_t AudioTrack::dump(int fd, const Vector<String16>& args) const // ========================================================================= -AudioTrack::AudioTrackThread::AudioTrackThread(AudioTrack& receiver) - : Thread(false), mReceiver(receiver) +AudioTrack::AudioTrackThread::AudioTrackThread(AudioTrack& receiver, bool bCanCallJava) + : Thread(bCanCallJava), mReceiver(receiver) { } @@ -534,25 +865,35 @@ void AudioTrack::AudioTrackThread::onFirstRef() // ========================================================================= audio_track_cblk_t::audio_track_cblk_t() - : user(0), server(0), volumeLR(0), buffers(0), size(0) + : user(0), server(0), userBase(0), serverBase(0), buffers(0), frameCount(0), + loopStart(UINT_MAX), loopEnd(UINT_MAX), loopCount(0), volumeLR(0), flowControlFlag(1), forceReady(0) { } -uint32_t audio_track_cblk_t::stepUser(int bufferCount) +uint32_t audio_track_cblk_t::stepUser(uint32_t frameCount) { uint32_t u = this->user; - uint32_t u_seq = u & audio_track_cblk_t::SEQUENCE_MASK; - uint32_t u_buf = u & audio_track_cblk_t::BUFFER_MASK; - if (++u_buf >= uint32_t(bufferCount)) { - u_seq += 0x100; - u_buf = 0; - } - u = u_seq | u_buf; - this->user = u; + + u += frameCount; + // Ensure that user is never ahead of server for AudioRecord + if (!out && u > this->server) { + LOGW("stepServer occured after track reset"); + u = this->server; + } + + if (u >= userBase + this->frameCount) { + userBase += this->frameCount; + } + + this->user = u; + + // Clear flow control error condition as new data has been written/read to/from buffer. + flowControlFlag = 0; + return u; } -bool audio_track_cblk_t::stepServer(int bufferCount) +bool audio_track_cblk_t::stepServer(uint32_t frameCount) { // the code below simulates lock-with-timeout // we MUST do this to protect the AudioFlinger server @@ -570,24 +911,83 @@ bool audio_track_cblk_t::stepServer(int bufferCount) } uint32_t s = this->server; - uint32_t s_seq = s & audio_track_cblk_t::SEQUENCE_MASK; - uint32_t s_buf = s & audio_track_cblk_t::BUFFER_MASK; - s_buf++; - if (s_buf >= uint32_t(bufferCount)) { - s_seq += 0x100; - s_buf = 0; + + s += frameCount; + // It is possible that we receive a flush() + // while the mixer is processing a block: in this case, + // stepServer() is called After the flush() has reset u & s and + // we have s > u + if (out && s > this->user) { + LOGW("stepServer occured after track reset"); + s = this->user; } - s = s_seq | s_buf; - this->server = s; + if (s >= loopEnd) { + LOGW_IF(s > loopEnd, "stepServer: s %u > loopEnd %u", s, loopEnd); + s = loopStart; + if (--loopCount == 0) { + loopEnd = UINT_MAX; + loopStart = UINT_MAX; + } + } + if (s >= serverBase + this->frameCount) { + serverBase += this->frameCount; + } + + this->server = s; + cv.signal(); lock.unlock(); return true; } -void* audio_track_cblk_t::buffer(int id) const +void* audio_track_cblk_t::buffer(uint32_t offset) const { - return (char*)this->buffers + id * this->size; + return (int16_t *)this->buffers + (offset-userBase)*this->channels; +} + +uint32_t audio_track_cblk_t::framesAvailable() +{ + Mutex::Autolock _l(lock); + return framesAvailable_l(); +} + +uint32_t audio_track_cblk_t::framesAvailable_l() +{ + uint32_t u = this->user; + uint32_t s = this->server; + + if (out) { + if (u < loopEnd) { + return s + frameCount - u; + } else { + uint32_t limit = (s < loopStart) ? s : loopStart; + return limit + frameCount - u; + } + } else { + return frameCount + u - s; + } +} + +uint32_t audio_track_cblk_t::framesReady() +{ + uint32_t u = this->user; + uint32_t s = this->server; + + if (out) { + if (u < loopEnd) { + return u - s; + } else { + Mutex::Autolock _l(lock); + if (loopCount >= 0) { + return (loopEnd - loopStart)*loopCount + u - s; + } else { + return UINT_MAX; + } + } + } else { + return s - u; + } } // ------------------------------------------------------------------------- |