/* //device/extlibs/pv/android/AudioTrack.cpp ** ** 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 ** ** 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. */ //#define LOG_NDEBUG 0 #define LOG_TAG "AudioTrack" #include #include #include #include #include #include #include #include #include #include #include #include #include #define LIKELY( exp ) (__builtin_expect( (exp) != 0, true )) #define UNLIKELY( exp ) (__builtin_expect( (exp) != 0, false )) namespace android { // --------------------------------------------------------------------------- static volatile size_t gFrameCount = 0; size_t AudioTrack::frameCount() { if (gFrameCount) return gFrameCount; const sp& af = AudioSystem::get_audio_flinger(); if (af == 0) return PERMISSION_DENIED; gFrameCount = af->frameCount(); return gFrameCount; } // --------------------------------------------------------------------------- AudioTrack::AudioTrack() : mStatus(NO_INIT) { } AudioTrack::AudioTrack( int streamType, uint32_t sampleRate, int format, int channelCount, int bufferCount, uint32_t flags, callback_t cbf, void* user) : mStatus(NO_INIT) { mStatus = set(streamType, sampleRate, format, channelCount, bufferCount, flags, cbf, user); } AudioTrack::~AudioTrack() { if (mStatus == NO_ERROR) { if (mPosition) { releaseBuffer(&mAudioBuffer); } // obtainBuffer() will give up with an error mAudioTrack->stop(); if (mAudioTrackThread != 0) { mAudioTrackThread->requestExitAndWait(); mAudioTrackThread.clear(); } mAudioTrack.clear(); IPCThreadState::self()->flushCommands(); } } status_t AudioTrack::set( int streamType, uint32_t sampleRate, int format, int channelCount, int bufferCount, uint32_t flags, callback_t cbf, void* user) { if (mAudioFlinger != 0) { LOGE("Track already in use"); return INVALID_OPERATION; } const sp& audioFlinger = AudioSystem::get_audio_flinger(); if (audioFlinger == 0) { LOGE("Could not get audioflinger"); return NO_INIT; } // handle default values first. if (streamType == DEFAULT) { streamType = MUSIC; } if (sampleRate == 0) { sampleRate = audioFlinger->sampleRate(); } // these below should probably come from the audioFlinger too... if (format == 0) { format = AudioSystem::PCM_16_BIT; } if (channelCount == 0) { channelCount = 2; } if (bufferCount == 0) { bufferCount = 2; } // validate parameters if (format != AudioSystem::PCM_16_BIT) { LOGE("Invalid format"); return BAD_VALUE; } if (channelCount != 1 && channelCount != 2) { LOGE("Invalid channel number"); return BAD_VALUE; } if (bufferCount < 2) { LOGE("Invalid buffer count"); return BAD_VALUE; } // create the track sp track = audioFlinger->createTrack(getpid(), streamType, sampleRate, format, channelCount, bufferCount, flags); if (track == 0) { LOGE("AudioFlinger could not create track"); return NO_INIT; } sp cblk = track->getCblk(); if (cblk == 0) { LOGE("Could not get control block"); return NO_INIT; } if (cbf != 0) { mAudioTrackThread = new AudioTrackThread(*this); if (mAudioTrackThread == 0) { LOGE("Could not create callback thread"); return NO_INIT; } } mStatus = NO_ERROR; mAudioFlinger = audioFlinger; mAudioTrack = track; mCblkMemory = cblk; mCblk = static_cast(cblk->pointer()); mCblk->buffers = (char*)mCblk + sizeof(audio_track_cblk_t); 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; mChannelCount = channelCount; mMuted = false; mActive = 0; mReserved = 0; mCbf = cbf; mUserData = user; mLatency = seconds(mFrameCount) / mSampleRate; mPosition = 0; return NO_ERROR; } status_t AudioTrack::initCheck() const { return mStatus; } // ------------------------------------------------------------------------- nsecs_t AudioTrack::latency() const { return mLatency; } int AudioTrack::streamType() const { return mStreamType; } uint32_t AudioTrack::sampleRate() const { return mSampleRate; } int AudioTrack::format() const { return mFormat; } int AudioTrack::channelCount() const { return mChannelCount; } int AudioTrack::bufferCount() const { return mBufferCount; } // ------------------------------------------------------------------------- void AudioTrack::start() { sp t = mAudioTrackThread; LOGV("start"); if (t != 0) { if (t->exitPending()) { if (t->requestExitAndWait() == WOULD_BLOCK) { LOGE("AudioTrack::start called from thread"); return; } } t->mLock.lock(); } if (android_atomic_or(1, &mActive) == 0) { if (t != 0) { t->run("AudioTrackThread", THREAD_PRIORITY_AUDIO_CLIENT); } else { setpriority(PRIO_PROCESS, 0, THREAD_PRIORITY_AUDIO_CLIENT); } mAudioTrack->start(); } if (t != 0) { t->mLock.unlock(); } } void AudioTrack::stop() { sp t = mAudioTrackThread; LOGV("stop"); if (t != 0) { t->mLock.lock(); } 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); } } if (t != 0) { t->mLock.unlock(); } } bool AudioTrack::stopped() const { return !mActive; } void AudioTrack::flush() { LOGV("flush"); if (!mActive) { mCblk->lock.lock(); mAudioTrack->flush(); // Release AudioTrack callback thread in case it was waiting for new buffers // in AudioTrack::obtainBuffer() mCblk->cv.signal(); mCblk->lock.unlock(); } } void AudioTrack::pause() { LOGV("pause"); if (android_atomic_and(~1, &mActive) == 1) { mActive = 0; mAudioTrack->pause(); } } void AudioTrack::mute(bool e) { mAudioTrack->mute(e); mMuted = e; } bool AudioTrack::muted() const { return mMuted; } void AudioTrack::setVolume(float left, float right) { mVolume[LEFT] = left; mVolume[RIGHT] = right; // write must be atomic mCblk->volumeLR = (int32_t(int16_t(left * 0x1000)) << 16) | int16_t(right * 0x1000); } void AudioTrack::getVolume(float* left, float* right) { *left = mVolume[LEFT]; *right = mVolume[RIGHT]; } void AudioTrack::setSampleRate(int rate) { if (rate > MAX_SAMPLE_RATE) rate = MAX_SAMPLE_RATE; mCblk->sampleRate = rate; } uint32_t AudioTrack::getSampleRate() { return uint32_t(mCblk->sampleRate); } // ------------------------------------------------------------------------- status_t AudioTrack::obtainBuffer(Buffer* audioBuffer, bool blocking) { int active; int timeout = 0; status_t result; audio_track_cblk_t* cblk = mCblk; 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; 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; LOGW_IF(u_seq < s_seq, "user doesn't fill buffers fast enough"); if (u_seq > s_seq && u_buf == s_buf) { Mutex::Autolock _l(cblk->lock); goto start_loop_here; while (u_seq > s_seq && u_buf == s_buf) { active = mActive; if (UNLIKELY(!active)) { LOGV("Not active and NO_MORE_BUFFERS"); return NO_MORE_BUFFERS; } if (UNLIKELY(!blocking)) return WOULD_BLOCK; timeout = 0; 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); 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; } } 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); 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); 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); } // ------------------------------------------------------------------------- ssize_t AudioTrack::write(const void* buffer, size_t userSize) { if (ssize_t(userSize) < 0) { // sanity-check. user is most-likely passing an error code. LOGE("AudioTrack::write(buffer=%p, size=%u (%d)", buffer, userSize, userSize); return BAD_VALUE; } LOGV("write %d bytes, mActive=%d", userSize, mActive); ssize_t written = 0; 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); } } size_t capacity = mAudioBuffer.size - mPosition; size_t toWrite = userSize < capacity ? userSize : capacity; memcpy(mAudioBuffer.i8 + mPosition, buffer, toWrite); buffer = static_cast(buffer) + toWrite; mPosition += toWrite; userSize -= toWrite; capacity -= toWrite; written += toWrite; if (capacity == 0) { mPosition = 0; releaseBuffer(&mAudioBuffer); } } while (userSize); return written; } // ------------------------------------------------------------------------- bool AudioTrack::processAudioBuffer(const sp& thread) { Buffer audioBuffer; status_t err = obtainBuffer(&audioBuffer, true); if (err < NO_ERROR) { LOGE("Error obtaining an audio buffer, giving up."); return false; } if (err == status_t(STOPPED)) return false; mCbf(mUserData, audioBuffer); releaseBuffer(&audioBuffer); return true; } status_t AudioTrack::dump(int fd, const Vector& args) const { const size_t SIZE = 256; char buffer[SIZE]; String8 result; 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); result.append(buffer); snprintf(buffer, 255, " sample rate(%d), status(%d), muted(%d), reserved(%d)\n", mSampleRate, mStatus, mMuted, mReserved); result.append(buffer); snprintf(buffer, 255, " active(%d), latency (%lld), position(%d)\n", mActive, mLatency, mPosition); result.append(buffer); ::write(fd, result.string(), result.size()); return NO_ERROR; } // ========================================================================= AudioTrack::AudioTrackThread::AudioTrackThread(AudioTrack& receiver) : Thread(false), mReceiver(receiver) { } bool AudioTrack::AudioTrackThread::threadLoop() { return mReceiver.processAudioBuffer(this); } status_t AudioTrack::AudioTrackThread::readyToRun() { return NO_ERROR; } void AudioTrack::AudioTrackThread::onFirstRef() { } // ========================================================================= audio_track_cblk_t::audio_track_cblk_t() : user(0), server(0), volumeLR(0), buffers(0), size(0) { } uint32_t audio_track_cblk_t::stepUser(int bufferCount) { 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; return u; } bool audio_track_cblk_t::stepServer(int bufferCount) { // the code below simulates lock-with-timeout // we MUST do this to protect the AudioFlinger server // as this lock is shared with the client. status_t err; err = lock.tryLock(); if (err == -EBUSY) { // just wait a bit usleep(1000); err = lock.tryLock(); } if (err != NO_ERROR) { // probably, the client just died. return false; } 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 = s_seq | s_buf; this->server = s; cv.signal(); lock.unlock(); return true; } void* audio_track_cblk_t::buffer(int id) const { return (char*)this->buffers + id * this->size; } // ------------------------------------------------------------------------- }; // namespace android