Loading include/private/media/AudioTrackShared.h +47 −2 Original line number Diff line number Diff line Loading @@ -26,7 +26,6 @@ #include <utils/RefBase.h> #include <media/nbaio/roundup.h> #include <media/SingleStateQueue.h> #include <private/media/StaticAudioTrackState.h> namespace android { Loading Loading @@ -61,6 +60,29 @@ struct AudioTrackSharedStreaming { volatile uint32_t mUnderrunFrames; // server increments for each unavailable but desired frame }; // Represents a single state of an AudioTrack that was created in static mode (shared memory buffer // supplied by the client). This state needs to be communicated from the client to server. As this // state is too large to be updated atomically without a mutex, and mutexes aren't allowed here, the // state is wrapped by a SingleStateQueue. struct StaticAudioTrackState { // Do not define constructors, destructors, or virtual methods as this is part of a // union in shared memory and they will not get called properly. // These fields should both be size_t, but since they are located in shared memory we // force to 32-bit. The client and server may have different typedefs for size_t. // The state has a sequence counter to indicate whether changes are made to loop or position. // The sequence counter also currently indicates whether loop or position is first depending // on which is greater; it jumps by max(mLoopSequence, mPositionSequence) + 1. uint32_t mLoopStart; uint32_t mLoopEnd; int32_t mLoopCount; uint32_t mLoopSequence; // a sequence counter to indicate changes to loop uint32_t mPosition; uint32_t mPositionSequence; // a sequence counter to indicate changes to position }; typedef SingleStateQueue<StaticAudioTrackState> StaticAudioTrackSingleStateQueue; struct AudioTrackSharedStatic { Loading Loading @@ -314,7 +336,24 @@ public: virtual void flush(); #define MIN_LOOP 16 // minimum length of each loop iteration in frames // setLoop(), setBufferPosition(), and setBufferPositionAndLoop() set the // static buffer position and looping parameters. These commands are not // synchronous (they do not wait or block); instead they take effect at the // next buffer data read from the server side. However, the client side // getters will read a cached version of the position and loop variables // until the setting takes effect. // // setBufferPositionAndLoop() is equivalent to calling, in order, setLoop() and // setBufferPosition(). // // The functions should not be relied upon to do parameter or state checking. // That is done at the AudioTrack level. void setLoop(size_t loopStart, size_t loopEnd, int loopCount); void setBufferPosition(size_t position); void setBufferPositionAndLoop(size_t position, size_t loopStart, size_t loopEnd, int loopCount); size_t getBufferPosition(); virtual size_t getMisalignment() { Loading @@ -327,6 +366,7 @@ public: private: StaticAudioTrackSingleStateQueue::Mutator mMutator; StaticAudioTrackState mState; // last communicated state to server size_t mBufferPosition; // so that getBufferPosition() appears to be synchronous }; Loading Loading @@ -448,6 +488,10 @@ public: virtual uint32_t getUnderrunFrames() const { return 0; } private: status_t updateStateWithLoop(StaticAudioTrackState *localState, const StaticAudioTrackState &update) const; status_t updateStateWithPosition(StaticAudioTrackState *localState, const StaticAudioTrackState &update) const; ssize_t pollPosition(); // poll for state queue update, and return current position StaticAudioTrackSingleStateQueue::Observer mObserver; size_t mPosition; // server's current play position in frames, relative to 0 Loading @@ -460,7 +504,8 @@ private: // can cause a track to appear to have a large number // of frames. INT64_MAX means an infinite loop. bool mFramesReadyIsCalledByMultipleThreads; StaticAudioTrackState mState; StaticAudioTrackState mState; // Server side state. Any updates from client must be // passed by the mObserver SingleStateQueue. }; // Proxy used by AudioFlinger for servicing AudioRecord Loading include/private/media/StaticAudioTrackState.hdeleted 100644 → 0 +0 −39 Original line number Diff line number Diff line /* * Copyright (C) 2012 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 * * 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 * limitations under the License. */ #ifndef STATIC_AUDIO_TRACK_STATE_H #define STATIC_AUDIO_TRACK_STATE_H namespace android { // Represents a single state of an AudioTrack that was created in static mode (shared memory buffer // supplied by the client). This state needs to be communicated from the client to server. As this // state is too large to be updated atomically without a mutex, and mutexes aren't allowed here, the // state is wrapped by a SingleStateQueue. struct StaticAudioTrackState { // do not define constructors, destructors, or virtual methods // These fields should both be size_t, but since they are located in shared memory we // force to 32-bit. The client and server may have different typedefs for size_t. uint32_t mLoopStart; uint32_t mLoopEnd; int mLoopCount; }; } // namespace android #endif // STATIC_AUDIO_TRACK_STATE_H media/libmedia/AudioTrack.cpp +21 −16 Original line number Diff line number Diff line Loading @@ -531,14 +531,13 @@ void AudioTrack::stop() // the playback head position will reset to 0, so if a marker is set, we need // to activate it again mMarkerReached = false; #if 0 // Force flush if a shared buffer is used otherwise audioflinger // will not stop before end of buffer is reached. // It may be needed to make sure that we stop playback, likely in case looping is on. if (mSharedBuffer != 0) { flush_l(); // clear buffer position and loop count. mLoopPeriod = 0; mStaticProxy->setBufferPositionAndLoop(0 /* position */, 0 /* loopStart */, 0 /* loopEnd */, 0 /* loopCount */); } #endif sp<AudioTrackThread> t = mAudioTrackThread; if (t != 0) { Loading Loading @@ -823,12 +822,9 @@ status_t AudioTrack::setPosition(uint32_t position) if (mState == STATE_ACTIVE) { return INVALID_OPERATION; } // After setting the position, use full update period before notification. mNewPosition = updateAndGetPosition_l() + mUpdatePeriod; mLoopPeriod = 0; // FIXME Check whether loops and setting position are incompatible in old code. // If we use setLoop for both purposes we lose the capability to set the position while looping. mStaticProxy->setLoop(position, mFrameCount, 0); mStaticProxy->setBufferPosition(position); return NO_ERROR; } Loading Loading @@ -894,9 +890,13 @@ status_t AudioTrack::reload() } mNewPosition = mUpdatePeriod; mLoopPeriod = 0; // FIXME The new code cannot reload while keeping a loop specified. // Need to check how the old code handled this, and whether it's a significant change. mStaticProxy->setLoop(0, mFrameCount, 0); (void) updateAndGetPosition_l(); mPosition = 0; // The documentation is not clear on the behavior of reload() and the restoration // of loop count. Historically we have not restored loop count, start, end; // rather we just reset the buffer position. However, restoring the last setLoop // information makes sense if one desires to repeat playing a particular sound. mStaticProxy->setBufferPosition(0); return NO_ERROR; } Loading Loading @@ -1865,15 +1865,20 @@ status_t AudioTrack::restoreTrack_l(const char *from) result = createTrack_l(); // take the frames that will be lost by track recreation into account in saved position // For streaming tracks, this is the amount we obtained from the user/client // (not the number actually consumed at the server - those are already lost). (void) updateAndGetPosition_l(); if (mStaticProxy != 0) { mPosition = mReleased; } if (result == NO_ERROR) { // continue playback from last known position, but // don't attempt to restore loop after invalidation; it's difficult and not worthwhile if (mStaticProxy != NULL) { mLoopPeriod = 0; mStaticProxy->setLoop(bufferPosition, mFrameCount, 0); mStaticProxy->setBufferPositionAndLoop(bufferPosition, 0 /* loopStart */, 0 /* loopEnd */, 0 /* loopCount */); } // FIXME How do we simulate the fact that all frames present in the buffer at the time of // track destruction have been played? This is critical for SoundPool implementation Loading media/libmedia/AudioTrackShared.cpp +119 −45 Original line number Diff line number Diff line Loading @@ -31,6 +31,20 @@ size_t clampToSize(T x) { return sizeof(T) > sizeof(size_t) && x > (T) SIZE_MAX ? SIZE_MAX : x < 0 ? 0 : (size_t) x; } // incrementSequence is used to determine the next sequence value // for the loop and position sequence counters. It should return // a value between "other" + 1 and "other" + INT32_MAX, the choice of // which needs to be the "least recently used" sequence value for "self". // In general, this means (new_self) returned is max(self, other) + 1. static uint32_t incrementSequence(uint32_t self, uint32_t other) { int32_t diff = self - other; if (diff >= 0 && diff < INT32_MAX) { return self + 1; // we're already ahead of other. } return other + 1; // we're behind, so move just ahead of other. } audio_track_cblk_t::audio_track_cblk_t() : mServer(0), mFutex(0), mMinimum(0), mVolumeLR(GAIN_MINIFLOAT_PACKED_UNITY), mSampleRate(0), mSendLevel(0), mFlags(0) Loading Loading @@ -487,6 +501,7 @@ StaticAudioTrackClientProxy::StaticAudioTrackClientProxy(audio_track_cblk_t* cbl : AudioTrackClientProxy(cblk, buffers, frameCount, frameSize), mMutator(&cblk->u.mStatic.mSingleStateQueue), mBufferPosition(0) { memset(&mState, 0, sizeof(mState)); } void StaticAudioTrackClientProxy::flush() Loading @@ -501,22 +516,47 @@ void StaticAudioTrackClientProxy::setLoop(size_t loopStart, size_t loopEnd, int // FIXME Should return an error status return; } StaticAudioTrackState newState; newState.mLoopStart = (uint32_t) loopStart; newState.mLoopEnd = (uint32_t) loopEnd; newState.mLoopCount = loopCount; size_t bufferPosition; if (loopCount == 0 || (bufferPosition = getBufferPosition()) >= loopEnd) { bufferPosition = loopStart; mState.mLoopStart = (uint32_t) loopStart; mState.mLoopEnd = (uint32_t) loopEnd; mState.mLoopCount = loopCount; mState.mLoopSequence = incrementSequence(mState.mLoopSequence, mState.mPositionSequence); // set patch-up variables until the mState is acknowledged by the ServerProxy. // observed buffer position and loop count will freeze until then to give the // illusion of a synchronous change. size_t bufferPosition = getBufferPosition(); // preserve behavior to restart at mState.mLoopStart if position exceeds mState.mLoopEnd. if (loopCount != 0 && bufferPosition >= mState.mLoopEnd) { mBufferPosition = mState.mLoopStart; } (void) mMutator.push(mState); } void StaticAudioTrackClientProxy::setBufferPosition(size_t position) { // This can only happen on a 64-bit client if (position > UINT32_MAX) { // FIXME Should return an error status return; } mBufferPosition = bufferPosition; // snapshot buffer position until loop is acknowledged. (void) mMutator.push(newState); mState.mPosition = (uint32_t) position; mState.mPositionSequence = incrementSequence(mState.mPositionSequence, mState.mLoopSequence); mBufferPosition = position; (void) mMutator.push(mState); } void StaticAudioTrackClientProxy::setBufferPositionAndLoop(size_t position, size_t loopStart, size_t loopEnd, int loopCount) { setLoop(loopStart, loopEnd, loopCount); setBufferPosition(position); } size_t StaticAudioTrackClientProxy::getBufferPosition() { size_t bufferPosition; if (mMutator.ack()) { // There is a race condition here as ack may be signaled before // the buffer position in mCblk is updated. Will be fixed in a later CL. bufferPosition = (size_t) mCblk->u.mStatic.mBufferPosition; if (bufferPosition > mFrameCount) { bufferPosition = mFrameCount; Loading Loading @@ -744,9 +784,7 @@ StaticAudioTrackServerProxy::StaticAudioTrackServerProxy(audio_track_cblk_t* cbl mFramesReadySafe(frameCount), mFramesReady(frameCount), mFramesReadyIsCalledByMultipleThreads(false) { mState.mLoopStart = 0; mState.mLoopEnd = 0; mState.mLoopCount = 0; memset(&mState, 0, sizeof(mState)); } void StaticAudioTrackServerProxy::framesReadyIsCalledByMultipleThreads() Loading @@ -763,55 +801,91 @@ size_t StaticAudioTrackServerProxy::framesReady() return mFramesReadySafe; } ssize_t StaticAudioTrackServerProxy::pollPosition() status_t StaticAudioTrackServerProxy::updateStateWithLoop( StaticAudioTrackState *localState, const StaticAudioTrackState &update) const { size_t position = mPosition; StaticAudioTrackState state; if (mObserver.poll(state)) { if (localState->mLoopSequence != update.mLoopSequence) { bool valid = false; size_t loopStart = state.mLoopStart; size_t loopEnd = state.mLoopEnd; if (state.mLoopCount == 0) { if (loopStart > mFrameCount) { loopStart = mFrameCount; } // ignore loopEnd mPosition = position = loopStart; mFramesReady = mFrameCount - mPosition; mState.mLoopCount = 0; const size_t loopStart = update.mLoopStart; const size_t loopEnd = update.mLoopEnd; size_t position = localState->mPosition; if (update.mLoopCount == 0) { valid = true; } else if (state.mLoopCount >= -1) { } else if (update.mLoopCount >= -1) { if (loopStart < loopEnd && loopEnd <= mFrameCount && loopEnd - loopStart >= MIN_LOOP) { // If the current position is greater than the end of the loop // we "wrap" to the loop start. This might cause an audible pop. if (position >= loopEnd) { mPosition = position = loopStart; } if (state.mLoopCount == -1) { mFramesReady = INT64_MAX; } else { // mFramesReady is 64 bits to handle the effective number of frames // that the static audio track contains, including loops. // TODO: Later consider fixing overflow, but does not seem needed now // as will not overflow if loopStart and loopEnd are Java "ints". mFramesReady = int64_t(state.mLoopCount) * (loopEnd - loopStart) + mFrameCount - mPosition; position = loopStart; } mState = state; valid = true; } } if (!valid || mPosition > mFrameCount) { if (!valid || position > mFrameCount) { return NO_INIT; } localState->mPosition = position; localState->mLoopCount = update.mLoopCount; localState->mLoopEnd = loopEnd; localState->mLoopStart = loopStart; localState->mLoopSequence = update.mLoopSequence; } return OK; } status_t StaticAudioTrackServerProxy::updateStateWithPosition( StaticAudioTrackState *localState, const StaticAudioTrackState &update) const { if (localState->mPositionSequence != update.mPositionSequence) { if (update.mPosition > mFrameCount) { return NO_INIT; } else if (localState->mLoopCount != 0 && update.mPosition >= localState->mLoopEnd) { localState->mLoopCount = 0; // disable loop count if position is beyond loop end. } localState->mPosition = update.mPosition; localState->mPositionSequence = update.mPositionSequence; } return OK; } ssize_t StaticAudioTrackServerProxy::pollPosition() { StaticAudioTrackState state; if (mObserver.poll(state)) { StaticAudioTrackState trystate = mState; bool result; const int32_t diffSeq = state.mLoopSequence - state.mPositionSequence; if (diffSeq < 0) { result = updateStateWithLoop(&trystate, state) == OK && updateStateWithPosition(&trystate, state) == OK; } else { result = updateStateWithPosition(&trystate, state) == OK && updateStateWithLoop(&trystate, state) == OK; } if (!result) { // caution: no update occurs so server state will be inconsistent with client state. ALOGE("%s client pushed an invalid state, shutting down", __func__); mIsShutdown = true; return (ssize_t) NO_INIT; } mState = trystate; if (mState.mLoopCount == -1) { mFramesReady = INT64_MAX; } else if (mState.mLoopCount == 0) { mFramesReady = mFrameCount - mState.mPosition; } else if (mState.mLoopCount > 0) { // TODO: Later consider fixing overflow, but does not seem needed now // as will not overflow if loopStart and loopEnd are Java "ints". mFramesReady = int64_t(mState.mLoopCount) * (mState.mLoopEnd - mState.mLoopStart) + mFrameCount - mState.mPosition; } mFramesReadySafe = clampToSize(mFramesReady); // This may overflow, but client is not supposed to rely on it mCblk->u.mStatic.mBufferPosition = (uint32_t) position; mCblk->u.mStatic.mBufferPosition = (uint32_t) mState.mPosition; } return (ssize_t) position; return (ssize_t) mState.mPosition; } status_t StaticAudioTrackServerProxy::obtainBuffer(Buffer* buffer, bool ackFlush __unused) Loading Loading @@ -869,7 +943,7 @@ void StaticAudioTrackServerProxy::releaseBuffer(Buffer* buffer) } mUnreleased -= stepCount; audio_track_cblk_t* cblk = mCblk; size_t position = mPosition; size_t position = mState.mPosition; size_t newPosition = position + stepCount; int32_t setFlags = 0; if (!(position <= newPosition && newPosition <= mFrameCount)) { Loading @@ -887,7 +961,7 @@ void StaticAudioTrackServerProxy::releaseBuffer(Buffer* buffer) if (newPosition == mFrameCount) { setFlags |= CBLK_BUFFER_END; } mPosition = newPosition; mState.mPosition = newPosition; if (mFramesReady != INT64_MAX) { mFramesReady -= stepCount; } Loading @@ -895,7 +969,7 @@ void StaticAudioTrackServerProxy::releaseBuffer(Buffer* buffer) cblk->mServer += stepCount; // This may overflow, but client is not supposed to rely on it cblk->u.mStatic.mBufferPosition = (uint32_t) newPosition; cblk->u.mStatic.mBufferPosition = (uint32_t) mState.mPosition; if (setFlags != 0) { (void) android_atomic_or(setFlags, &cblk->mFlags); // this would be a good place to wake a futex Loading Loading
include/private/media/AudioTrackShared.h +47 −2 Original line number Diff line number Diff line Loading @@ -26,7 +26,6 @@ #include <utils/RefBase.h> #include <media/nbaio/roundup.h> #include <media/SingleStateQueue.h> #include <private/media/StaticAudioTrackState.h> namespace android { Loading Loading @@ -61,6 +60,29 @@ struct AudioTrackSharedStreaming { volatile uint32_t mUnderrunFrames; // server increments for each unavailable but desired frame }; // Represents a single state of an AudioTrack that was created in static mode (shared memory buffer // supplied by the client). This state needs to be communicated from the client to server. As this // state is too large to be updated atomically without a mutex, and mutexes aren't allowed here, the // state is wrapped by a SingleStateQueue. struct StaticAudioTrackState { // Do not define constructors, destructors, or virtual methods as this is part of a // union in shared memory and they will not get called properly. // These fields should both be size_t, but since they are located in shared memory we // force to 32-bit. The client and server may have different typedefs for size_t. // The state has a sequence counter to indicate whether changes are made to loop or position. // The sequence counter also currently indicates whether loop or position is first depending // on which is greater; it jumps by max(mLoopSequence, mPositionSequence) + 1. uint32_t mLoopStart; uint32_t mLoopEnd; int32_t mLoopCount; uint32_t mLoopSequence; // a sequence counter to indicate changes to loop uint32_t mPosition; uint32_t mPositionSequence; // a sequence counter to indicate changes to position }; typedef SingleStateQueue<StaticAudioTrackState> StaticAudioTrackSingleStateQueue; struct AudioTrackSharedStatic { Loading Loading @@ -314,7 +336,24 @@ public: virtual void flush(); #define MIN_LOOP 16 // minimum length of each loop iteration in frames // setLoop(), setBufferPosition(), and setBufferPositionAndLoop() set the // static buffer position and looping parameters. These commands are not // synchronous (they do not wait or block); instead they take effect at the // next buffer data read from the server side. However, the client side // getters will read a cached version of the position and loop variables // until the setting takes effect. // // setBufferPositionAndLoop() is equivalent to calling, in order, setLoop() and // setBufferPosition(). // // The functions should not be relied upon to do parameter or state checking. // That is done at the AudioTrack level. void setLoop(size_t loopStart, size_t loopEnd, int loopCount); void setBufferPosition(size_t position); void setBufferPositionAndLoop(size_t position, size_t loopStart, size_t loopEnd, int loopCount); size_t getBufferPosition(); virtual size_t getMisalignment() { Loading @@ -327,6 +366,7 @@ public: private: StaticAudioTrackSingleStateQueue::Mutator mMutator; StaticAudioTrackState mState; // last communicated state to server size_t mBufferPosition; // so that getBufferPosition() appears to be synchronous }; Loading Loading @@ -448,6 +488,10 @@ public: virtual uint32_t getUnderrunFrames() const { return 0; } private: status_t updateStateWithLoop(StaticAudioTrackState *localState, const StaticAudioTrackState &update) const; status_t updateStateWithPosition(StaticAudioTrackState *localState, const StaticAudioTrackState &update) const; ssize_t pollPosition(); // poll for state queue update, and return current position StaticAudioTrackSingleStateQueue::Observer mObserver; size_t mPosition; // server's current play position in frames, relative to 0 Loading @@ -460,7 +504,8 @@ private: // can cause a track to appear to have a large number // of frames. INT64_MAX means an infinite loop. bool mFramesReadyIsCalledByMultipleThreads; StaticAudioTrackState mState; StaticAudioTrackState mState; // Server side state. Any updates from client must be // passed by the mObserver SingleStateQueue. }; // Proxy used by AudioFlinger for servicing AudioRecord Loading
include/private/media/StaticAudioTrackState.hdeleted 100644 → 0 +0 −39 Original line number Diff line number Diff line /* * Copyright (C) 2012 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 * * 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 * limitations under the License. */ #ifndef STATIC_AUDIO_TRACK_STATE_H #define STATIC_AUDIO_TRACK_STATE_H namespace android { // Represents a single state of an AudioTrack that was created in static mode (shared memory buffer // supplied by the client). This state needs to be communicated from the client to server. As this // state is too large to be updated atomically without a mutex, and mutexes aren't allowed here, the // state is wrapped by a SingleStateQueue. struct StaticAudioTrackState { // do not define constructors, destructors, or virtual methods // These fields should both be size_t, but since they are located in shared memory we // force to 32-bit. The client and server may have different typedefs for size_t. uint32_t mLoopStart; uint32_t mLoopEnd; int mLoopCount; }; } // namespace android #endif // STATIC_AUDIO_TRACK_STATE_H
media/libmedia/AudioTrack.cpp +21 −16 Original line number Diff line number Diff line Loading @@ -531,14 +531,13 @@ void AudioTrack::stop() // the playback head position will reset to 0, so if a marker is set, we need // to activate it again mMarkerReached = false; #if 0 // Force flush if a shared buffer is used otherwise audioflinger // will not stop before end of buffer is reached. // It may be needed to make sure that we stop playback, likely in case looping is on. if (mSharedBuffer != 0) { flush_l(); // clear buffer position and loop count. mLoopPeriod = 0; mStaticProxy->setBufferPositionAndLoop(0 /* position */, 0 /* loopStart */, 0 /* loopEnd */, 0 /* loopCount */); } #endif sp<AudioTrackThread> t = mAudioTrackThread; if (t != 0) { Loading Loading @@ -823,12 +822,9 @@ status_t AudioTrack::setPosition(uint32_t position) if (mState == STATE_ACTIVE) { return INVALID_OPERATION; } // After setting the position, use full update period before notification. mNewPosition = updateAndGetPosition_l() + mUpdatePeriod; mLoopPeriod = 0; // FIXME Check whether loops and setting position are incompatible in old code. // If we use setLoop for both purposes we lose the capability to set the position while looping. mStaticProxy->setLoop(position, mFrameCount, 0); mStaticProxy->setBufferPosition(position); return NO_ERROR; } Loading Loading @@ -894,9 +890,13 @@ status_t AudioTrack::reload() } mNewPosition = mUpdatePeriod; mLoopPeriod = 0; // FIXME The new code cannot reload while keeping a loop specified. // Need to check how the old code handled this, and whether it's a significant change. mStaticProxy->setLoop(0, mFrameCount, 0); (void) updateAndGetPosition_l(); mPosition = 0; // The documentation is not clear on the behavior of reload() and the restoration // of loop count. Historically we have not restored loop count, start, end; // rather we just reset the buffer position. However, restoring the last setLoop // information makes sense if one desires to repeat playing a particular sound. mStaticProxy->setBufferPosition(0); return NO_ERROR; } Loading Loading @@ -1865,15 +1865,20 @@ status_t AudioTrack::restoreTrack_l(const char *from) result = createTrack_l(); // take the frames that will be lost by track recreation into account in saved position // For streaming tracks, this is the amount we obtained from the user/client // (not the number actually consumed at the server - those are already lost). (void) updateAndGetPosition_l(); if (mStaticProxy != 0) { mPosition = mReleased; } if (result == NO_ERROR) { // continue playback from last known position, but // don't attempt to restore loop after invalidation; it's difficult and not worthwhile if (mStaticProxy != NULL) { mLoopPeriod = 0; mStaticProxy->setLoop(bufferPosition, mFrameCount, 0); mStaticProxy->setBufferPositionAndLoop(bufferPosition, 0 /* loopStart */, 0 /* loopEnd */, 0 /* loopCount */); } // FIXME How do we simulate the fact that all frames present in the buffer at the time of // track destruction have been played? This is critical for SoundPool implementation Loading
media/libmedia/AudioTrackShared.cpp +119 −45 Original line number Diff line number Diff line Loading @@ -31,6 +31,20 @@ size_t clampToSize(T x) { return sizeof(T) > sizeof(size_t) && x > (T) SIZE_MAX ? SIZE_MAX : x < 0 ? 0 : (size_t) x; } // incrementSequence is used to determine the next sequence value // for the loop and position sequence counters. It should return // a value between "other" + 1 and "other" + INT32_MAX, the choice of // which needs to be the "least recently used" sequence value for "self". // In general, this means (new_self) returned is max(self, other) + 1. static uint32_t incrementSequence(uint32_t self, uint32_t other) { int32_t diff = self - other; if (diff >= 0 && diff < INT32_MAX) { return self + 1; // we're already ahead of other. } return other + 1; // we're behind, so move just ahead of other. } audio_track_cblk_t::audio_track_cblk_t() : mServer(0), mFutex(0), mMinimum(0), mVolumeLR(GAIN_MINIFLOAT_PACKED_UNITY), mSampleRate(0), mSendLevel(0), mFlags(0) Loading Loading @@ -487,6 +501,7 @@ StaticAudioTrackClientProxy::StaticAudioTrackClientProxy(audio_track_cblk_t* cbl : AudioTrackClientProxy(cblk, buffers, frameCount, frameSize), mMutator(&cblk->u.mStatic.mSingleStateQueue), mBufferPosition(0) { memset(&mState, 0, sizeof(mState)); } void StaticAudioTrackClientProxy::flush() Loading @@ -501,22 +516,47 @@ void StaticAudioTrackClientProxy::setLoop(size_t loopStart, size_t loopEnd, int // FIXME Should return an error status return; } StaticAudioTrackState newState; newState.mLoopStart = (uint32_t) loopStart; newState.mLoopEnd = (uint32_t) loopEnd; newState.mLoopCount = loopCount; size_t bufferPosition; if (loopCount == 0 || (bufferPosition = getBufferPosition()) >= loopEnd) { bufferPosition = loopStart; mState.mLoopStart = (uint32_t) loopStart; mState.mLoopEnd = (uint32_t) loopEnd; mState.mLoopCount = loopCount; mState.mLoopSequence = incrementSequence(mState.mLoopSequence, mState.mPositionSequence); // set patch-up variables until the mState is acknowledged by the ServerProxy. // observed buffer position and loop count will freeze until then to give the // illusion of a synchronous change. size_t bufferPosition = getBufferPosition(); // preserve behavior to restart at mState.mLoopStart if position exceeds mState.mLoopEnd. if (loopCount != 0 && bufferPosition >= mState.mLoopEnd) { mBufferPosition = mState.mLoopStart; } (void) mMutator.push(mState); } void StaticAudioTrackClientProxy::setBufferPosition(size_t position) { // This can only happen on a 64-bit client if (position > UINT32_MAX) { // FIXME Should return an error status return; } mBufferPosition = bufferPosition; // snapshot buffer position until loop is acknowledged. (void) mMutator.push(newState); mState.mPosition = (uint32_t) position; mState.mPositionSequence = incrementSequence(mState.mPositionSequence, mState.mLoopSequence); mBufferPosition = position; (void) mMutator.push(mState); } void StaticAudioTrackClientProxy::setBufferPositionAndLoop(size_t position, size_t loopStart, size_t loopEnd, int loopCount) { setLoop(loopStart, loopEnd, loopCount); setBufferPosition(position); } size_t StaticAudioTrackClientProxy::getBufferPosition() { size_t bufferPosition; if (mMutator.ack()) { // There is a race condition here as ack may be signaled before // the buffer position in mCblk is updated. Will be fixed in a later CL. bufferPosition = (size_t) mCblk->u.mStatic.mBufferPosition; if (bufferPosition > mFrameCount) { bufferPosition = mFrameCount; Loading Loading @@ -744,9 +784,7 @@ StaticAudioTrackServerProxy::StaticAudioTrackServerProxy(audio_track_cblk_t* cbl mFramesReadySafe(frameCount), mFramesReady(frameCount), mFramesReadyIsCalledByMultipleThreads(false) { mState.mLoopStart = 0; mState.mLoopEnd = 0; mState.mLoopCount = 0; memset(&mState, 0, sizeof(mState)); } void StaticAudioTrackServerProxy::framesReadyIsCalledByMultipleThreads() Loading @@ -763,55 +801,91 @@ size_t StaticAudioTrackServerProxy::framesReady() return mFramesReadySafe; } ssize_t StaticAudioTrackServerProxy::pollPosition() status_t StaticAudioTrackServerProxy::updateStateWithLoop( StaticAudioTrackState *localState, const StaticAudioTrackState &update) const { size_t position = mPosition; StaticAudioTrackState state; if (mObserver.poll(state)) { if (localState->mLoopSequence != update.mLoopSequence) { bool valid = false; size_t loopStart = state.mLoopStart; size_t loopEnd = state.mLoopEnd; if (state.mLoopCount == 0) { if (loopStart > mFrameCount) { loopStart = mFrameCount; } // ignore loopEnd mPosition = position = loopStart; mFramesReady = mFrameCount - mPosition; mState.mLoopCount = 0; const size_t loopStart = update.mLoopStart; const size_t loopEnd = update.mLoopEnd; size_t position = localState->mPosition; if (update.mLoopCount == 0) { valid = true; } else if (state.mLoopCount >= -1) { } else if (update.mLoopCount >= -1) { if (loopStart < loopEnd && loopEnd <= mFrameCount && loopEnd - loopStart >= MIN_LOOP) { // If the current position is greater than the end of the loop // we "wrap" to the loop start. This might cause an audible pop. if (position >= loopEnd) { mPosition = position = loopStart; } if (state.mLoopCount == -1) { mFramesReady = INT64_MAX; } else { // mFramesReady is 64 bits to handle the effective number of frames // that the static audio track contains, including loops. // TODO: Later consider fixing overflow, but does not seem needed now // as will not overflow if loopStart and loopEnd are Java "ints". mFramesReady = int64_t(state.mLoopCount) * (loopEnd - loopStart) + mFrameCount - mPosition; position = loopStart; } mState = state; valid = true; } } if (!valid || mPosition > mFrameCount) { if (!valid || position > mFrameCount) { return NO_INIT; } localState->mPosition = position; localState->mLoopCount = update.mLoopCount; localState->mLoopEnd = loopEnd; localState->mLoopStart = loopStart; localState->mLoopSequence = update.mLoopSequence; } return OK; } status_t StaticAudioTrackServerProxy::updateStateWithPosition( StaticAudioTrackState *localState, const StaticAudioTrackState &update) const { if (localState->mPositionSequence != update.mPositionSequence) { if (update.mPosition > mFrameCount) { return NO_INIT; } else if (localState->mLoopCount != 0 && update.mPosition >= localState->mLoopEnd) { localState->mLoopCount = 0; // disable loop count if position is beyond loop end. } localState->mPosition = update.mPosition; localState->mPositionSequence = update.mPositionSequence; } return OK; } ssize_t StaticAudioTrackServerProxy::pollPosition() { StaticAudioTrackState state; if (mObserver.poll(state)) { StaticAudioTrackState trystate = mState; bool result; const int32_t diffSeq = state.mLoopSequence - state.mPositionSequence; if (diffSeq < 0) { result = updateStateWithLoop(&trystate, state) == OK && updateStateWithPosition(&trystate, state) == OK; } else { result = updateStateWithPosition(&trystate, state) == OK && updateStateWithLoop(&trystate, state) == OK; } if (!result) { // caution: no update occurs so server state will be inconsistent with client state. ALOGE("%s client pushed an invalid state, shutting down", __func__); mIsShutdown = true; return (ssize_t) NO_INIT; } mState = trystate; if (mState.mLoopCount == -1) { mFramesReady = INT64_MAX; } else if (mState.mLoopCount == 0) { mFramesReady = mFrameCount - mState.mPosition; } else if (mState.mLoopCount > 0) { // TODO: Later consider fixing overflow, but does not seem needed now // as will not overflow if loopStart and loopEnd are Java "ints". mFramesReady = int64_t(mState.mLoopCount) * (mState.mLoopEnd - mState.mLoopStart) + mFrameCount - mState.mPosition; } mFramesReadySafe = clampToSize(mFramesReady); // This may overflow, but client is not supposed to rely on it mCblk->u.mStatic.mBufferPosition = (uint32_t) position; mCblk->u.mStatic.mBufferPosition = (uint32_t) mState.mPosition; } return (ssize_t) position; return (ssize_t) mState.mPosition; } status_t StaticAudioTrackServerProxy::obtainBuffer(Buffer* buffer, bool ackFlush __unused) Loading Loading @@ -869,7 +943,7 @@ void StaticAudioTrackServerProxy::releaseBuffer(Buffer* buffer) } mUnreleased -= stepCount; audio_track_cblk_t* cblk = mCblk; size_t position = mPosition; size_t position = mState.mPosition; size_t newPosition = position + stepCount; int32_t setFlags = 0; if (!(position <= newPosition && newPosition <= mFrameCount)) { Loading @@ -887,7 +961,7 @@ void StaticAudioTrackServerProxy::releaseBuffer(Buffer* buffer) if (newPosition == mFrameCount) { setFlags |= CBLK_BUFFER_END; } mPosition = newPosition; mState.mPosition = newPosition; if (mFramesReady != INT64_MAX) { mFramesReady -= stepCount; } Loading @@ -895,7 +969,7 @@ void StaticAudioTrackServerProxy::releaseBuffer(Buffer* buffer) cblk->mServer += stepCount; // This may overflow, but client is not supposed to rely on it cblk->u.mStatic.mBufferPosition = (uint32_t) newPosition; cblk->u.mStatic.mBufferPosition = (uint32_t) mState.mPosition; if (setFlags != 0) { (void) android_atomic_or(setFlags, &cblk->mFlags); // this would be a good place to wake a futex Loading