namespace android {
class audio_track_cblk_t;
+class AudioRecordClientProxy;
// ----------------------------------------------------------------------------
uint32_t mUpdatePeriod; // in ms
// constant after constructor or set()
+ uint32_t mSampleRate;
size_t mFrameCount;
audio_format_t mFormat;
uint8_t mChannelCount;
int mPreviousPriority; // before start()
SchedPolicy mPreviousSchedulingGroup;
+ AudioRecordClientProxy* mProxy;
};
}; // namespace android
// ----------------------------------------------------------------------------
class audio_track_cblk_t;
+class AudioTrackClientProxy;
// ----------------------------------------------------------------------------
float mVolume[2];
float mSendLevel;
+ uint32_t mSampleRate;
size_t mFrameCount; // corresponds to current IAudioTrack
size_t mReqFrameCount; // frame count to request the next time a new
// IAudioTrack is needed
bool mIsTimed;
int mPreviousPriority; // before start()
SchedPolicy mPreviousSchedulingGroup;
+ AudioTrackClientProxy* mProxy;
};
class TimedAudioTrack : public AudioTrack
#include <sys/types.h>
#include <utils/threads.h>
+#include <utils/Log.h>
namespace android {
#define CBLK_INVALID 0x04 // track buffer invalidated by AudioFlinger, need to re-create
#define CBLK_DISABLED 0x08 // track disabled by AudioFlinger due to underrun, need to re-start
+struct AudioTrackSharedStreaming {
+ // similar to NBAIO MonoPipe
+ volatile int32_t mFront;
+ volatile int32_t mRear;
+};
+
+// future
+struct AudioTrackSharedStatic {
+ int mReserved;
+};
+
+// ----------------------------------------------------------------------------
+
// Important: do not add any virtual methods, including ~
struct audio_track_cblk_t
{
+ friend class Proxy;
+ friend class AudioTrackClientProxy;
+ friend class AudioRecordClientProxy;
+ friend class ServerProxy;
// The data members are grouped so that members accessed frequently and in the same context
// are in the same line of data cache.
// For AudioTrack only, not used by AudioRecord.
private:
uint32_t mVolumeLR;
-public:
- uint32_t sampleRate;
+ uint32_t mSampleRate; // AudioTrack only: client's requested sample rate in Hz
+ // or 0 == default. Write-only client, read-only server.
uint8_t mPad2; // unused
+public:
// read-only for client, server writes once at initialization and is then read-only
uint8_t mName; // normal tracks: track name, fast tracks: track index
// Cache line boundary (32 bytes)
+#if 0
+ union {
+ AudioTrackSharedStreaming mStreaming;
+ AudioTrackSharedStatic mStatic;
+ int mAlign[8];
+ } u;
+
+ // Cache line boundary (32 bytes)
+#endif
+
// Since the control block is always located in shared memory, this constructor
// is only used for placement new(). It is never used for regular new() or stack.
audio_track_cblk_t();
- // called by client only, where client includes regular
- // AudioTrack and AudioFlinger::PlaybackThread::OutputTrack
- uint32_t stepUserIn(size_t stepCount, size_t frameCount) { return stepUser(stepCount, frameCount, false); }
- uint32_t stepUserOut(size_t stepCount, size_t frameCount) { return stepUser(stepCount, frameCount, true); }
-
- bool stepServer(size_t stepCount, size_t frameCount, bool isOut);
-
+private:
// if there is a shared buffer, "buffers" is the value of pointer() for the shared
// buffer, otherwise "buffers" points immediately after the control block
- void* buffer(void *buffers, uint32_t frameSize, uint32_t offset) const;
-
- uint32_t framesAvailableIn(size_t frameCount)
- { return framesAvailable(frameCount, false); }
- uint32_t framesAvailableOut(size_t frameCount)
- { return framesAvailable(frameCount, true); }
- uint32_t framesAvailableIn_l(size_t frameCount)
- { return framesAvailable_l(frameCount, false); }
- uint32_t framesAvailableOut_l(size_t frameCount)
- { return framesAvailable_l(frameCount, true); }
- uint32_t framesReadyIn() { return framesReady(false); }
- uint32_t framesReadyOut() { return framesReady(true); }
+ void* buffer(void *buffers, uint32_t frameSize, size_t offset) const;
bool tryLock();
- // No barriers on the following operations, so the ordering of loads/stores
- // with respect to other parameters is UNPREDICTABLE. That's considered safe.
-
- // for AudioTrack client only, caller must limit to 0.0 <= sendLevel <= 1.0
- void setSendLevel(float sendLevel) {
- mSendLevel = uint16_t(sendLevel * 0x1000);
- }
-
- // for AudioFlinger only; the return value must be validated by the caller
- uint16_t getSendLevel_U4_12() const {
- return mSendLevel;
- }
-
- // for AudioTrack client only, caller must limit to 0 <= volumeLR <= 0x10001000
- void setVolumeLR(uint32_t volumeLR) {
- mVolumeLR = volumeLR;
- }
-
- // for AudioFlinger only; the return value must be validated by the caller
- uint32_t getVolumeLR() const {
- return mVolumeLR;
- }
-
-private:
// isOut == true means AudioTrack, isOut == false means AudioRecord
+ bool stepServer(size_t stepCount, size_t frameCount, bool isOut);
uint32_t stepUser(size_t stepCount, size_t frameCount, bool isOut);
uint32_t framesAvailable(size_t frameCount, bool isOut);
uint32_t framesAvailable_l(size_t frameCount, bool isOut);
uint32_t framesReady(bool isOut);
};
+// ----------------------------------------------------------------------------
+
+// Proxy for shared memory control block, to isolate callers from needing to know the details.
+// There is exactly one ClientProxy and one ServerProxy per shared memory control block.
+// The proxies are located in normal memory, and are not multi-thread safe within a given side.
+class Proxy {
+protected:
+ Proxy(audio_track_cblk_t* cblk, void *buffers, size_t frameCount, size_t frameSize)
+ : mCblk(cblk), mBuffers(buffers), mFrameCount(frameCount), mFrameSize(frameSize) { }
+ virtual ~Proxy() { }
+
+public:
+ void* buffer(size_t offset) const {
+ return mCblk->buffer(mBuffers, mFrameSize, offset);
+ }
+
+protected:
+ // These refer to shared memory, and are virtual addresses with respect to the current process.
+ // They may have different virtual addresses within the other process.
+ audio_track_cblk_t* const mCblk; // the control block
+ void* const mBuffers; // starting address of buffers
+
+ const size_t mFrameCount; // not necessarily a power of 2
+ const size_t mFrameSize; // in bytes
+#if 0
+ const size_t mFrameCountP2; // mFrameCount rounded to power of 2, streaming mode
+#endif
+
+};
+
+// ----------------------------------------------------------------------------
+
+// Proxy seen by AudioTrack client and AudioRecord client
+class ClientProxy : public Proxy {
+protected:
+ ClientProxy(audio_track_cblk_t* cblk, void *buffers, size_t frameCount, size_t frameSize)
+ : Proxy(cblk, buffers, frameCount, frameSize) { }
+ virtual ~ClientProxy() { }
+};
+
+// ----------------------------------------------------------------------------
+
+// Proxy used by AudioTrack client, which also includes AudioFlinger::PlaybackThread::OutputTrack
+class AudioTrackClientProxy : public ClientProxy {
+public:
+ AudioTrackClientProxy(audio_track_cblk_t* cblk, void *buffers, size_t frameCount, size_t frameSize)
+ : ClientProxy(cblk, buffers, frameCount, frameSize) { }
+ virtual ~AudioTrackClientProxy() { }
+
+ // No barriers on the following operations, so the ordering of loads/stores
+ // with respect to other parameters is UNPREDICTABLE. That's considered safe.
+
+ // caller must limit to 0.0 <= sendLevel <= 1.0
+ void setSendLevel(float sendLevel) {
+ mCblk->mSendLevel = uint16_t(sendLevel * 0x1000);
+ }
+
+ // caller must limit to 0 <= volumeLR <= 0x10001000
+ void setVolumeLR(uint32_t volumeLR) {
+ mCblk->mVolumeLR = volumeLR;
+ }
+
+ void setSampleRate(uint32_t sampleRate) {
+ mCblk->mSampleRate = sampleRate;
+ }
+
+ // called by:
+ // PlaybackThread::OutputTrack::write
+ // AudioTrack::createTrack_l
+ // AudioTrack::releaseBuffer
+ // AudioTrack::reload
+ // AudioTrack::restoreTrack_l (2 places)
+ size_t stepUser(size_t stepCount) {
+ return mCblk->stepUser(stepCount, mFrameCount, true /*isOut*/);
+ }
+
+ // called by AudioTrack::obtainBuffer and AudioTrack::processBuffer
+ size_t framesAvailable() {
+ return mCblk->framesAvailable(mFrameCount, true /*isOut*/);
+ }
+
+ // called by AudioTrack::obtainBuffer and PlaybackThread::OutputTrack::obtainBuffer
+ // FIXME remove this API since it assumes a lock that should be invisible to caller
+ size_t framesAvailable_l() {
+ return mCblk->framesAvailable_l(mFrameCount, true /*isOut*/);
+ }
+
+};
+
+// ----------------------------------------------------------------------------
+
+// Proxy used by AudioRecord client
+class AudioRecordClientProxy : public ClientProxy {
+public:
+ AudioRecordClientProxy(audio_track_cblk_t* cblk, void *buffers, size_t frameCount, size_t frameSize)
+ : ClientProxy(cblk, buffers, frameCount, frameSize) { }
+ ~AudioRecordClientProxy() { }
+
+ // called by AudioRecord::releaseBuffer
+ size_t stepUser(size_t stepCount) {
+ return mCblk->stepUser(stepCount, mFrameCount, false /*isOut*/);
+ }
+
+ // called by AudioRecord::processBuffer
+ size_t framesAvailable() {
+ return mCblk->framesAvailable(mFrameCount, false /*isOut*/);
+ }
+
+ // called by AudioRecord::obtainBuffer
+ size_t framesReady() {
+ return mCblk->framesReady(false /*isOut*/);
+ }
+
+};
+
+// ----------------------------------------------------------------------------
+
+// Proxy used by AudioFlinger server
+class ServerProxy : public Proxy {
+public:
+ ServerProxy(audio_track_cblk_t* cblk, void *buffers, size_t frameCount, size_t frameSize, bool isOut)
+ : Proxy(cblk, buffers, frameCount, frameSize), mIsOut(isOut) { }
+ virtual ~ServerProxy() { }
+
+ // for AudioTrack and AudioRecord
+ bool step(size_t stepCount) { return mCblk->stepServer(stepCount, mFrameCount, mIsOut); }
+
+ // return value of these methods must be validated by the caller
+ uint32_t getSampleRate() const { return mCblk->mSampleRate; }
+ uint16_t getSendLevel_U4_12() const { return mCblk->mSendLevel; }
+ uint32_t getVolumeLR() const { return mCblk->mVolumeLR; }
+
+ // for AudioTrack only
+ size_t framesReady() {
+ ALOG_ASSERT(mIsOut);
+ return mCblk->framesReady(true);
+ }
+
+ // for AudioRecord only, called by RecordThread::RecordTrack::getNextBuffer
+ // FIXME remove this API since it assumes a lock that should be invisible to caller
+ size_t framesAvailableIn_l() {
+ ALOG_ASSERT(!mIsOut);
+ return mCblk->framesAvailable_l(mFrameCount, false);
+ }
+
+private:
+ const bool mIsOut; // true for AudioTrack, false for AudioRecord
+
+};
// ----------------------------------------------------------------------------
AudioRecord::AudioRecord()
: mStatus(NO_INIT), mSessionId(0),
- mPreviousPriority(ANDROID_PRIORITY_NORMAL), mPreviousSchedulingGroup(SP_DEFAULT)
+ mPreviousPriority(ANDROID_PRIORITY_NORMAL), mPreviousSchedulingGroup(SP_DEFAULT),
+ mProxy(NULL)
{
}
int notificationFrames,
int sessionId)
: mStatus(NO_INIT), mSessionId(0),
- mPreviousPriority(ANDROID_PRIORITY_NORMAL), mPreviousSchedulingGroup(SP_DEFAULT)
+ mPreviousPriority(ANDROID_PRIORITY_NORMAL),
+ mPreviousSchedulingGroup(SP_DEFAULT),
+ mProxy(NULL)
{
mStatus = set(inputSource, sampleRate, format, channelMask,
frameCount, cbf, user, notificationFrames, sessionId);
IPCThreadState::self()->flushCommands();
AudioSystem::releaseAudioSessionId(mSessionId);
}
+ delete mProxy;
}
status_t AudioRecord::set(
if (sampleRate == 0) {
sampleRate = DEFAULT_SAMPLE_RATE;
}
+ mSampleRate = sampleRate;
+
// these below should probably come from the audioFlinger too...
if (format == AUDIO_FORMAT_DEFAULT) {
format = AUDIO_FORMAT_PCM_16_BIT;
uint32_t channelCount = popcount(channelMask);
mChannelCount = channelCount;
+ if (audio_is_linear_pcm(mFormat)) {
+ mFrameSize = channelCount * audio_bytes_per_sample(format);
+ } else {
+ mFrameSize = sizeof(uint8_t);
+ }
+
if (sessionId == 0 ) {
mSessionId = AudioSystem::newAudioSessionId();
} else {
// Update buffer size in case it has been limited by AudioFlinger during track creation
mFrameCount = mCblk->frameCount_;
- if (audio_is_linear_pcm(mFormat)) {
- mFrameSize = channelCount * audio_bytes_per_sample(format);
- } else {
- mFrameSize = sizeof(uint8_t);
- }
-
mActive = false;
mCbf = cbf;
mNotificationFrames = notificationFrames;
uint32_t AudioRecord::getSampleRate() const
{
- return mCblk->sampleRate;
+ return mSampleRate;
}
status_t AudioRecord::setMarkerPosition(uint32_t marker)
mBuffers = (char*)cblk + sizeof(audio_track_cblk_t);
cblk->bufferTimeoutMs = MAX_RUN_TIMEOUT_MS;
cblk->waitTimeMs = 0;
+
+ // update proxy
+ delete mProxy;
+ mProxy = new AudioRecordClientProxy(cblk, mBuffers, frameCount, mFrameSize);
+
return NO_ERROR;
}
status_t AudioRecord::obtainBuffer(Buffer* audioBuffer, int32_t waitCount)
{
+ ALOG_ASSERT(mStatus == NO_ERROR && mProxy != NULL);
+
AutoMutex lock(mLock);
bool active;
status_t result = NO_ERROR;
audioBuffer->frameCount = 0;
audioBuffer->size = 0;
- uint32_t framesReady = cblk->framesReadyIn();
+ size_t framesReady = mProxy->framesReady();
if (framesReady == 0) {
cblk->lock.lock();
}
// read the server count again
start_loop_here:
- framesReady = cblk->framesReadyIn();
+ framesReady = mProxy->framesReady();
}
cblk->lock.unlock();
}
audioBuffer->frameCount = framesReq;
audioBuffer->size = framesReq * mFrameSize;
- audioBuffer->raw = cblk->buffer(mBuffers, mFrameSize, u);
+ audioBuffer->raw = mProxy->buffer(u);
active = mActive;
return active ? status_t(NO_ERROR) : status_t(STOPPED);
}
void AudioRecord::releaseBuffer(Buffer* audioBuffer)
{
+ ALOG_ASSERT(mStatus == NO_ERROR && mProxy != NULL);
+
AutoMutex lock(mLock);
- mCblk->stepUserIn(audioBuffer->frameCount, mFrameCount);
+ (void) mProxy->stepUser(audioBuffer->frameCount);
}
audio_io_handle_t AudioRecord::getInput() const
audio_io_handle_t AudioRecord::getInput_l()
{
mInput = AudioSystem::getInput(mInputSource,
- mCblk->sampleRate,
+ mSampleRate,
mFormat,
mChannelMask,
mSessionId);
// Manage overrun callback
- if (active && (cblk->framesAvailableIn(mFrameCount) == 0)) {
+ if (active && (mProxy->framesAvailable() == 0)) {
// The value of active is stale, but we are almost sure to be active here because
// otherwise we would have exited when obtainBuffer returned STOPPED earlier.
ALOGV("Overrun user: %x, server: %x, flags %04x", cblk->user, cblk->server, cblk->flags);
// if the new IAudioRecord is created, openRecord_l() will modify the
// following member variables: mAudioRecord, mCblkMemory and mCblk.
// It will also delete the strong references on previous IAudioRecord and IMemory
- result = openRecord_l(cblk->sampleRate, mFormat, mFrameCount, getInput_l());
+ result = openRecord_l(mSampleRate, mFormat, mFrameCount, getInput_l());
if (result == NO_ERROR) {
newCblk = mCblk;
// callback thread or sync event hasn't changed
: mStatus(NO_INIT),
mIsTimed(false),
mPreviousPriority(ANDROID_PRIORITY_NORMAL),
- mPreviousSchedulingGroup(SP_DEFAULT)
+ mPreviousSchedulingGroup(SP_DEFAULT),
+ mProxy(NULL)
{
}
: mStatus(NO_INIT),
mIsTimed(false),
mPreviousPriority(ANDROID_PRIORITY_NORMAL),
- mPreviousSchedulingGroup(SP_DEFAULT)
+ mPreviousSchedulingGroup(SP_DEFAULT),
+ mProxy(NULL)
{
mStatus = set(streamType, sampleRate, format, channelMask,
frameCount, flags, cbf, user, notificationFrames,
: mStatus(NO_INIT),
mIsTimed(false),
mPreviousPriority(ANDROID_PRIORITY_NORMAL),
- mPreviousSchedulingGroup(SP_DEFAULT)
+ mPreviousSchedulingGroup(SP_DEFAULT),
+ mProxy(NULL)
{
if (sharedBuffer == 0) {
ALOGE("sharedBuffer must be non-0");
IPCThreadState::self()->flushCommands();
AudioSystem::releaseAudioSessionId(mSessionId);
}
+ delete mProxy;
}
status_t AudioTrack::set(
}
sampleRate = afSampleRate;
}
+ mSampleRate = sampleRate;
// these below should probably come from the audioFlinger too...
if (format == AUDIO_FORMAT_DEFAULT) {
uint32_t channelCount = popcount(channelMask);
mChannelCount = channelCount;
+ if (audio_is_linear_pcm(format)) {
+ mFrameSize = channelCount * audio_bytes_per_sample(format);
+ mFrameSizeAF = channelCount * sizeof(int16_t);
+ } else {
+ mFrameSize = sizeof(uint8_t);
+ mFrameSizeAF = sizeof(uint8_t);
+ }
+
audio_io_handle_t output = AudioSystem::getOutput(
streamType,
sampleRate, format, channelMask,
mStreamType = streamType;
mFormat = format;
- if (audio_is_linear_pcm(format)) {
- mFrameSize = channelCount * audio_bytes_per_sample(format);
- mFrameSizeAF = channelCount * sizeof(int16_t);
- } else {
- mFrameSize = sizeof(uint8_t);
- mFrameSizeAF = sizeof(uint8_t);
- }
-
mSharedBuffer = sharedBuffer;
mActive = false;
mUserData = user;
status_t AudioTrack::setVolume(float left, float right)
{
+ if (mStatus != NO_ERROR) {
+ return mStatus;
+ }
+ ALOG_ASSERT(mProxy != NULL);
+
if (left < 0.0f || left > 1.0f || right < 0.0f || right > 1.0f) {
return BAD_VALUE;
}
mVolume[LEFT] = left;
mVolume[RIGHT] = right;
- mCblk->setVolumeLR((uint32_t(uint16_t(right * 0x1000)) << 16) | uint16_t(left * 0x1000));
+ mProxy->setVolumeLR((uint32_t(uint16_t(right * 0x1000)) << 16) | uint16_t(left * 0x1000));
return NO_ERROR;
}
status_t AudioTrack::setAuxEffectSendLevel(float level)
{
ALOGV("setAuxEffectSendLevel(%f)", level);
+
+ if (mStatus != NO_ERROR) {
+ return mStatus;
+ }
+ ALOG_ASSERT(mProxy != NULL);
+
if (level < 0.0f || level > 1.0f) {
return BAD_VALUE;
}
AutoMutex lock(mLock);
mSendLevel = level;
-
- mCblk->setSendLevel(level);
+ mProxy->setSendLevel(level);
return NO_ERROR;
}
}
AutoMutex lock(mLock);
- mCblk->sampleRate = rate;
+ mSampleRate = rate;
+ mProxy->setSampleRate(rate);
+
return NO_ERROR;
}
}
AutoMutex lock(mLock);
- return mCblk->sampleRate;
+ return mSampleRate;
}
status_t AudioTrack::setLoop(uint32_t loopStart, uint32_t loopEnd, int loopCount)
status_t AudioTrack::reload()
{
+ if (mStatus != NO_ERROR) {
+ return mStatus;
+ }
+ ALOG_ASSERT(mProxy != NULL);
+
if (mSharedBuffer == 0 || mIsTimed) {
return INVALID_OPERATION;
}
flush_l();
- audio_track_cblk_t* cblk = mCblk;
- cblk->stepUserOut(mFrameCount, mFrameCount);
+ (void) mProxy->stepUser(mFrameCount);
return NO_ERROR;
}
audio_io_handle_t AudioTrack::getOutput_l()
{
return AudioSystem::getOutput(mStreamType,
- mCblk->sampleRate, mFormat, mChannelMask, mFlags);
+ mSampleRate, mFormat, mChannelMask, mFlags);
}
status_t AudioTrack::attachAuxEffect(int effectId)
mBuffers = (char*)cblk + sizeof(audio_track_cblk_t);
} else {
mBuffers = sharedBuffer->pointer();
- // Force buffer full condition as data is already present in shared memory
- cblk->stepUserOut(frameCount, frameCount);
}
- cblk->setVolumeLR((uint32_t(uint16_t(mVolume[RIGHT] * 0x1000)) << 16) |
- uint16_t(mVolume[LEFT] * 0x1000));
- cblk->setSendLevel(mSendLevel);
mAudioTrack->attachAuxEffect(mAuxEffectId);
cblk->bufferTimeoutMs = MAX_STARTUP_TIMEOUT_MS;
cblk->waitTimeMs = 0;
if (frameCount > mReqFrameCount) {
mReqFrameCount = frameCount;
}
+
+ // update proxy
+ delete mProxy;
+ mProxy = new AudioTrackClientProxy(cblk, mBuffers, frameCount, mFrameSizeAF);
+ mProxy->setVolumeLR((uint32_t(uint16_t(mVolume[RIGHT] * 0x1000)) << 16) |
+ uint16_t(mVolume[LEFT] * 0x1000));
+ mProxy->setSendLevel(mSendLevel);
+ mProxy->setSampleRate(mSampleRate);
+ if (sharedBuffer != 0) {
+ // Force buffer full condition as data is already present in shared memory
+ mProxy->stepUser(frameCount);
+ }
+
return NO_ERROR;
}
status_t AudioTrack::obtainBuffer(Buffer* audioBuffer, int32_t waitCount)
{
+ ALOG_ASSERT(mStatus == NO_ERROR && mProxy != NULL);
+
AutoMutex lock(mLock);
bool active;
status_t result = NO_ERROR;
audioBuffer->frameCount = 0;
audioBuffer->size = 0;
- uint32_t framesAvail = cblk->framesAvailableOut(mFrameCount);
+ size_t framesAvail = mProxy->framesAvailable();
cblk->lock.lock();
if (cblk->flags & CBLK_INVALID) {
}
// read the server count again
start_loop_here:
- framesAvail = cblk->framesAvailableOut_l(mFrameCount);
+ framesAvail = mProxy->framesAvailable_l();
}
cblk->lock.unlock();
}
audioBuffer->frameCount = framesReq;
audioBuffer->size = framesReq * mFrameSizeAF;
- audioBuffer->raw = cblk->buffer(mBuffers, mFrameSizeAF, u);
+ audioBuffer->raw = mProxy->buffer(u);
active = mActive;
return active ? status_t(NO_ERROR) : status_t(STOPPED);
}
void AudioTrack::releaseBuffer(Buffer* audioBuffer)
{
+ ALOG_ASSERT(mStatus == NO_ERROR && mProxy != NULL);
+
AutoMutex lock(mLock);
audio_track_cblk_t* cblk = mCblk;
- cblk->stepUserOut(audioBuffer->frameCount, mFrameCount);
+ (void) mProxy->stepUser(audioBuffer->frameCount);
if (audioBuffer->frameCount > 0) {
// restart track if it was disabled by audioflinger due to previous underrun
if (mActive && (cblk->flags & CBLK_DISABLED)) {
// so all cblk references might still refer to old shared memory, but that should be benign
// Manage underrun callback
- if (active && (cblk->framesAvailableOut(mFrameCount) == mFrameCount)) {
+ if (active && (mProxy->framesAvailable() == mFrameCount)) {
ALOGV("Underrun user: %x, server: %x, flags %04x", cblk->user, cblk->server, cblk->flags);
if (!(android_atomic_or(CBLK_UNDERRUN, &cblk->flags) & CBLK_UNDERRUN)) {
mCbf(EVENT_UNDERRUN, mUserData, 0);
// following member variables: mAudioTrack, mCblkMemory and mCblk.
// It will also delete the strong references on previous IAudioTrack and IMemory
result = createTrack_l(mStreamType,
- cblk->sampleRate,
+ mSampleRate,
mFormat,
mReqFrameCount, // so that frame count never goes down
mFlags,
// restore loop: this is not guaranteed to succeed if new frame count is not
// compatible with loop length
setLoop_l(cblk->loopStart, cblk->loopEnd, cblk->loopCount);
+ size_t frames = 0;
if (!fromStart) {
newCblk->bufferTimeoutMs = MAX_RUN_TIMEOUT_MS;
// Make sure that a client relying on callback events indicating underrun or
// the actual amount of audio frames played (e.g SoundPool) receives them.
if (mSharedBuffer == 0) {
- uint32_t frames = 0;
if (user > server) {
frames = ((user - server) > mFrameCount) ?
mFrameCount : (user - server);
}
// restart playback even if buffer is not completely filled.
android_atomic_or(CBLK_FORCEREADY, &newCblk->flags);
- // stepUser() clears CBLK_UNDERRUN flag enabling underrun callbacks to
- // the client
- newCblk->stepUserOut(frames, mFrameCount);
}
}
if (mSharedBuffer != 0) {
- newCblk->stepUserOut(mFrameCount, mFrameCount);
+ frames = mFrameCount;
+ }
+ if (frames > 0) {
+ // stepUser() clears CBLK_UNDERRUN flag enabling underrun callbacks to
+ // the client
+ mProxy->stepUser(frames);
}
if (mActive) {
result = mAudioTrack->start();
char buffer[SIZE];
String8 result;
- audio_track_cblk_t* cblk = mCblk;
result.append(" AudioTrack::dump\n");
snprintf(buffer, 255, " stream type(%d), left - right volume(%f, %f)\n", mStreamType,
mVolume[0], mVolume[1]);
snprintf(buffer, 255, " format(%d), channel count(%d), frame count(%d)\n", mFormat,
mChannelCount, mFrameCount);
result.append(buffer);
- snprintf(buffer, 255, " sample rate(%u), status(%d)\n",
- (cblk == 0) ? 0 : cblk->sampleRate, mStatus);
+ snprintf(buffer, 255, " sample rate(%u), status(%d)\n", mSampleRate, mStatus);
result.append(buffer);
snprintf(buffer, 255, " active(%d), latency (%d)\n", mActive, mLatency);
result.append(buffer);
: lock(Mutex::SHARED), cv(Condition::SHARED), user(0), server(0),
userBase(0), serverBase(0), frameCount_(0),
loopStart(UINT_MAX), loopEnd(UINT_MAX), loopCount(0), mVolumeLR(0x10001000),
- mSendLevel(0), flags(0)
+ mSampleRate(0), mSendLevel(0), flags(0)
{
}
class AudioBuffer;
class AudioResampler;
class FastMixer;
+class ServerProxy;
// ----------------------------------------------------------------------------
bool isInvalid() const { return mIsInvalid; }
virtual bool isTimedTrack() const { return false; }
bool isFastTrack() const { return (mFlags & IAudioFlinger::TRACK_FAST) != 0; }
- virtual bool isOut() const;
protected:
AudioBufferProvider::Buffer mOutBuffer;
bool mActive;
DuplicatingThread* const mSourceThread; // for waitTimeMs() in write()
- void* mBuffers; // starting address of buffers in plain memory
+ AudioTrackClientProxy* mClientProxy;
}; // end of OutputTrack
static void appendDumpHeader(String8& result);
void dump(char* buffer, size_t size);
- virtual bool isOut() const;
-
private:
friend class AudioFlinger; // for mState
// read original volumes with volume control
float typeVolume = mStreamTypes[track->streamType()].volume;
float v = masterVolume * typeVolume;
- uint32_t vlr = cblk->getVolumeLR();
+ ServerProxy *proxy = track->mServerProxy;
+ uint32_t vlr = proxy->getVolumeLR();
vl = vlr & 0xFFFF;
vr = vlr >> 16;
// track volumes come from shared memory, so can't be trusted and must be clamped
// assuming master volume and stream type volume each go up to 1.0,
// vl and vr are now in 8.24 format
- uint16_t sendLevel = cblk->getSendLevel_U4_12();
+ uint16_t sendLevel = proxy->getSendLevel_U4_12();
// send level comes from shared memory and so may be corrupt
if (sendLevel > MAX_GAIN_INT) {
ALOGV("Track send level out of range: %04X", sendLevel);
name,
AudioMixer::TRACK,
AudioMixer::CHANNEL_MASK, (void *)track->channelMask());
+ // limit track sample rate to 2 x output sample rate, which changes at re-configuration
+ uint32_t maxSampleRate = mSampleRate * 2;
+ uint32_t reqSampleRate = track->mServerProxy->getSampleRate();
+ if (reqSampleRate == 0) {
+ reqSampleRate = mSampleRate;
+ } else if (reqSampleRate > maxSampleRate) {
+ reqSampleRate = maxSampleRate;
+ }
mAudioMixer->setParameter(
name,
AudioMixer::RESAMPLE,
AudioMixer::SAMPLE_RATE,
- (void *)(cblk->sampleRate));
+ (void *)reqSampleRate);
mAudioMixer->setParameter(
name,
AudioMixer::TRACK,
break;
}
mTracks[i]->mName = name;
- // limit track sample rate to 2 x new output sample rate
- if (mTracks[i]->mCblk->sampleRate > 2 * sampleRate()) {
- mTracks[i]->mCblk->sampleRate = 2 * sampleRate();
- }
}
sendIoConfigEvent_l(AudioSystem::OUTPUT_CONFIG_CHANGED);
}
} else {
float typeVolume = mStreamTypes[track->streamType()].volume;
float v = mMasterVolume * typeVolume;
- uint32_t vlr = cblk->getVolumeLR();
+ uint32_t vlr = track->mServerProxy->getVolumeLR();
float v_clamped = v * (vlr & 0xFFFF);
if (v_clamped > MAX_GAIN) {
v_clamped = MAX_GAIN;
audio_channel_mask_t channelMask,
size_t frameCount,
const sp<IMemory>& sharedBuffer,
- int sessionId);
+ int sessionId,
+ bool isOut);
virtual ~TrackBase();
virtual status_t start(AudioSystem::sync_event_t event,
bool step(); // mStepCount is an implicit input
void reset();
- virtual bool isOut() const = 0; // true for Track and TimedTrack, false for RecordTrack,
+ bool isOut() const { return mIsOut; }
+ // true for Track and TimedTrack, false for RecordTrack,
// this could be a track type if needed later
const wp<ThreadBase> mThread;
sp<IMemory> mCblkMemory;
audio_track_cblk_t* mCblk;
void* mBuffer; // start of track buffer, typically in shared memory
+ // except for OutputTrack when it is in local memory
void* mBufferEnd; // &mBuffer[mFrameCount * frameSize], where frameSize
// is based on mChannelCount and 16-bit samples
uint32_t mStepCount; // saves AudioBufferProvider::Buffer::frameCount as of
bool mStepServerFailed;
const int mSessionId;
Vector < sp<SyncEvent> >mSyncEvents;
+ const bool mIsOut;
+ ServerProxy* mServerProxy;
};
audio_channel_mask_t channelMask,
size_t frameCount,
const sp<IMemory>& sharedBuffer,
- int sessionId)
+ int sessionId,
+ bool isOut)
: RefBase(),
mThread(thread),
mClient(client),
mChannelCount * audio_bytes_per_sample(format) : sizeof(int8_t)),
mFrameCount(frameCount),
mStepServerFailed(false),
- mSessionId(sessionId)
+ mSessionId(sessionId),
+ mIsOut(isOut),
+ mServerProxy(NULL)
{
// client == 0 implies sharedBuffer == 0
ALOG_ASSERT(!(client == 0 && sharedBuffer != 0));
return;
}
} else {
- mCblk = (audio_track_cblk_t *)(new uint8_t[size]);
+ // this syntax avoids calling the audio_track_cblk_t constructor twice
+ mCblk = (audio_track_cblk_t *) new uint8_t[size];
// assume mCblk != NULL
}
new(mCblk) audio_track_cblk_t();
// clear all buffers
mCblk->frameCount_ = frameCount;
- mCblk->sampleRate = sampleRate;
// uncomment the following lines to quickly test 32-bit wraparound
// mCblk->user = 0xffff0000;
// mCblk->server = 0xffff0000;
mBuffer = sharedBuffer->pointer();
}
mBufferEnd = (uint8_t *)mBuffer + bufferSize;
+ mServerProxy = new ServerProxy(mCblk, mBuffer, frameCount, mFrameSize, isOut);
}
}
AudioFlinger::ThreadBase::TrackBase::~TrackBase()
{
+ // delete the proxy before deleting the shared memory it refers to, to avoid dangling reference
+ delete mServerProxy;
if (mCblk != NULL) {
if (mClient == 0) {
delete mCblk;
}
bool AudioFlinger::ThreadBase::TrackBase::step() {
- bool result;
- audio_track_cblk_t* cblk = this->cblk();
-
- result = cblk->stepServer(mStepCount, mFrameCount, isOut());
+ bool result = mServerProxy->step(mStepCount);
if (!result) {
ALOGV("stepServer failed acquiring cblk mutex");
mStepServerFailed = true;
}
uint32_t AudioFlinger::ThreadBase::TrackBase::sampleRate() const {
- return mCblk->sampleRate;
+ return mServerProxy->getSampleRate();
}
void* AudioFlinger::ThreadBase::TrackBase::getBuffer(uint32_t offset, uint32_t frames) const {
int sessionId,
IAudioFlinger::track_flags_t flags)
: TrackBase(thread, client, sampleRate, format, channelMask, frameCount, sharedBuffer,
- sessionId),
+ sessionId, true /*isOut*/),
mFillingUpStatus(FS_INVALID),
// mRetryCount initialized later when needed
mSharedBuffer(sharedBuffer),
void AudioFlinger::PlaybackThread::Track::dump(char* buffer, size_t size)
{
- uint32_t vlr = mCblk->getVolumeLR();
+ uint32_t vlr = mServerProxy->getVolumeLR();
if (isFastTrack()) {
sprintf(buffer, " F %2d", mFastIndex);
} else {
mFrameCount,
stateChar,
mFillingUpStatus,
- mCblk->sampleRate,
+ mServerProxy->getSampleRate(),
20.0 * log10((vlr & 0xFFFF) / 4096.0),
20.0 * log10((vlr >> 16) / 4096.0),
mCblk->server,
}
// FIXME Same as above
- framesReady = cblk->framesReadyOut();
+ framesReady = mServerProxy->framesReady();
if (CC_LIKELY(framesReady)) {
uint32_t s = cblk->server;
// the tryLock() could block for up to 1 ms, and a sequence of these could delay fast mixer.
// FIXME Replace AudioTrackShared control block implementation by a non-blocking FIFO queue.
size_t AudioFlinger::PlaybackThread::Track::framesReady() const {
- return mCblk->framesReadyOut();
+ return mServerProxy->framesReady();
}
// Don't call for fast tracks; the framesReady() could result in priority inversion
{
// called by FastMixer, so not allowed to take any locks, block, or do I/O including logs
ALOG_ASSERT(isFastTrack() && (mCblk != NULL));
- uint32_t vlr = mCblk->getVolumeLR();
+ uint32_t vlr = mServerProxy->getVolumeLR();
uint32_t vl = vlr & 0xFFFF;
uint32_t vr = vlr >> 16;
// track volumes come from shared memory, so can't be trusted and must be clamped
return NO_ERROR;
}
-bool AudioFlinger::PlaybackThread::Track::isOut() const
-{
- return true;
-}
-
void AudioFlinger::PlaybackThread::Track::invalidate()
{
// FIXME should use proxy
size_t frameCount)
: Track(playbackThread, NULL, AUDIO_STREAM_CNT, sampleRate, format, channelMask, frameCount,
NULL, 0, IAudioFlinger::TRACK_DEFAULT),
- mActive(false), mSourceThread(sourceThread), mBuffers(NULL)
+ mActive(false), mSourceThread(sourceThread), mClientProxy(NULL)
{
if (mCblk != NULL) {
- mBuffers = (char*)mCblk + sizeof(audio_track_cblk_t);
mOutBuffer.frameCount = 0;
playbackThread->mTracks.add(this);
- ALOGV("OutputTrack constructor mCblk %p, mBuffer %p, mBuffers %p, " \
- "mCblk->frameCount %d, mCblk->sampleRate %u, mChannelMask 0x%08x mBufferEnd %p",
- mCblk, mBuffer, mBuffers,
- mCblk->frameCount, mCblk->sampleRate, mChannelMask, mBufferEnd);
+ ALOGV("OutputTrack constructor mCblk %p, mBuffer %p, "
+ "mCblk->frameCount_ %u, mChannelMask 0x%08x mBufferEnd %p",
+ mCblk, mBuffer,
+ mCblk->frameCount_, mChannelMask, mBufferEnd);
+ // since client and server are in the same process,
+ // the buffer has the same virtual address on both sides
+ mClientProxy = new AudioTrackClientProxy(mCblk, mBuffer, mFrameCount, mFrameSize);
} else {
ALOGW("Error creating output track on thread %p", playbackThread);
}
AudioFlinger::PlaybackThread::OutputTrack::~OutputTrack()
{
clearBufferQueue();
+ delete mClientProxy;
+ // superclass destructor will now delete the server proxy and shared memory both refer to
}
status_t AudioFlinger::PlaybackThread::OutputTrack::start(AudioSystem::sync_event_t event,
uint32_t outFrames = pInBuffer->frameCount > mOutBuffer.frameCount ? mOutBuffer.frameCount :
pInBuffer->frameCount;
memcpy(mOutBuffer.raw, pInBuffer->raw, outFrames * channelCount * sizeof(int16_t));
- mCblk->stepUserOut(outFrames, mFrameCount);
+ mClientProxy->stepUser(outFrames);
pInBuffer->frameCount -= outFrames;
pInBuffer->i16 += outFrames * channelCount;
mOutBuffer.frameCount -= outFrames;
status_t AudioFlinger::PlaybackThread::OutputTrack::obtainBuffer(
AudioBufferProvider::Buffer* buffer, uint32_t waitTimeMs)
{
- int active;
- status_t result;
audio_track_cblk_t* cblk = mCblk;
uint32_t framesReq = buffer->frameCount;
ALOGVV("OutputTrack::obtainBuffer user %d, server %d", cblk->user, cblk->server);
buffer->frameCount = 0;
- uint32_t framesAvail = cblk->framesAvailableOut(mFrameCount);
-
-
- if (framesAvail == 0) {
+ size_t framesAvail;
+ {
Mutex::Autolock _l(cblk->lock);
- goto start_loop_here;
- while (framesAvail == 0) {
- active = mActive;
- if (CC_UNLIKELY(!active)) {
+
+ // read the server count again
+ while (!(framesAvail = mClientProxy->framesAvailable_l())) {
+ if (CC_UNLIKELY(!mActive)) {
ALOGV("Not active and NO_MORE_BUFFERS");
return NO_MORE_BUFFERS;
}
- result = cblk->cv.waitRelative(cblk->lock, milliseconds(waitTimeMs));
+ status_t result = cblk->cv.waitRelative(cblk->lock, milliseconds(waitTimeMs));
if (result != NO_ERROR) {
return NO_MORE_BUFFERS;
}
- // read the server count again
- start_loop_here:
- framesAvail = cblk->framesAvailableOut_l(mFrameCount);
}
}
-// if (framesAvail < framesReq) {
-// return NO_MORE_BUFFERS;
-// }
-
if (framesReq > framesAvail) {
framesReq = framesAvail;
}
}
buffer->frameCount = framesReq;
- buffer->raw = cblk->buffer(mBuffers, mFrameSize, u);
+ buffer->raw = mClientProxy->buffer(u);
return NO_ERROR;
}
size_t frameCount,
int sessionId)
: TrackBase(thread, client, sampleRate, format,
- channelMask, frameCount, 0 /*sharedBuffer*/, sessionId),
+ channelMask, frameCount, 0 /*sharedBuffer*/, sessionId, false /*isOut*/),
mOverflow(false)
{
ALOGV("RecordTrack constructor, size %d", (int)mBufferEnd - (int)mBuffer);
}
// FIXME lock is not actually held, so overrun is possible
- framesAvail = cblk->framesAvailableIn_l(mFrameCount);
+ framesAvail = mServerProxy->framesAvailableIn_l();
if (CC_LIKELY(framesAvail)) {
uint32_t s = cblk->server;
/*static*/ void AudioFlinger::RecordThread::RecordTrack::appendDumpHeader(String8& result)
{
- result.append(" Clien Fmt Chn mask Session Step S SRate Serv User FrameCount\n");
+ result.append(" Clien Fmt Chn mask Session Step S Serv User FrameCount\n");
}
void AudioFlinger::RecordThread::RecordTrack::dump(char* buffer, size_t size)
{
- snprintf(buffer, size, " %05d %03u 0x%08x %05d %04u %01d %05u %08x %08x %05d\n",
+ snprintf(buffer, size, " %05d %03u 0x%08x %05d %04u %01d %08x %08x %05d\n",
(mClient == 0) ? getpid_cached : mClient->pid(),
mFormat,
mChannelMask,
mSessionId,
mStepCount,
mState,
- mCblk->sampleRate,
mCblk->server,
mCblk->user,
mFrameCount);
}
-bool AudioFlinger::RecordThread::RecordTrack::isOut() const
-{
- return false;
-}
-
}; // namespace android