diff options
Diffstat (limited to 'services/audioflinger')
-rw-r--r-- | services/audioflinger/AudioFlinger.cpp | 10 | ||||
-rw-r--r-- | services/audioflinger/AudioResamplerDyn.cpp | 56 | ||||
-rw-r--r-- | services/audioflinger/AudioWatchdog.cpp | 2 | ||||
-rw-r--r-- | services/audioflinger/FastMixer.cpp | 63 | ||||
-rw-r--r-- | services/audioflinger/StateQueue.cpp | 4 | ||||
-rw-r--r-- | services/audioflinger/Threads.cpp | 73 | ||||
-rw-r--r-- | services/audioflinger/tests/Android.mk | 31 | ||||
-rwxr-xr-x | services/audioflinger/tests/build_and_run_all_unit_tests.sh | 22 | ||||
-rw-r--r-- | services/audioflinger/tests/resampler_tests.cpp | 471 | ||||
-rwxr-xr-x | services/audioflinger/tests/run_all_unit_tests.sh | 11 |
10 files changed, 646 insertions, 97 deletions
diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp index a4cad4e..527fd65 100644 --- a/services/audioflinger/AudioFlinger.cpp +++ b/services/audioflinger/AudioFlinger.cpp @@ -431,7 +431,7 @@ status_t AudioFlinger::dump(int fd, const Vector<String16>& args) if (mLogMemoryDealer != 0) { sp<IBinder> binder = defaultServiceManager()->getService(String16("media.log")); if (binder != 0) { - fdprintf(fd, "\nmedia.log:\n"); + dprintf(fd, "\nmedia.log:\n"); Vector<String16> args; binder->dump(fd, args); } @@ -1266,7 +1266,7 @@ AudioFlinger::Client::Client(const sp<AudioFlinger>& audioFlinger, pid_t pid) : RefBase(), mAudioFlinger(audioFlinger), // FIXME should be a "k" constant not hard-coded, in .h or ro. property, see 4 lines below - mMemoryDealer(new MemoryDealer(4 * 1024*1024, "AudioFlinger::Client")), + mMemoryDealer(new MemoryDealer(1024*1024, "AudioFlinger::Client")), mPid(pid), mTimedTrackCount(0) { @@ -2609,7 +2609,7 @@ void AudioFlinger::dumpTee(int fd, const sp<NBAIO_Source>& source, audio_io_hand } } else { if (fd >= 0) { - fdprintf(fd, "unable to rotate tees in %s: %s\n", teePath, strerror(errno)); + dprintf(fd, "unable to rotate tees in %s: %s\n", teePath, strerror(errno)); } } char teeTime[16]; @@ -2663,11 +2663,11 @@ void AudioFlinger::dumpTee(int fd, const sp<NBAIO_Source>& source, audio_io_hand write(teeFd, &temp, sizeof(temp)); close(teeFd); if (fd >= 0) { - fdprintf(fd, "tee copied to %s\n", teePath); + dprintf(fd, "tee copied to %s\n", teePath); } } else { if (fd >= 0) { - fdprintf(fd, "unable to create tee %s: %s\n", teePath, strerror(errno)); + dprintf(fd, "unable to create tee %s: %s\n", teePath, strerror(errno)); } } } diff --git a/services/audioflinger/AudioResamplerDyn.cpp b/services/audioflinger/AudioResamplerDyn.cpp index a4446a4..318eb57 100644 --- a/services/audioflinger/AudioResamplerDyn.cpp +++ b/services/audioflinger/AudioResamplerDyn.cpp @@ -460,9 +460,15 @@ void AudioResamplerDyn<TC, TI, TO>::resample(TO* out, size_t outFrameCount, const uint32_t phaseIncrement = mPhaseIncrement; size_t outputIndex = 0; size_t outputSampleCount = outFrameCount * 2; // stereo output - size_t inFrameCount = getInFrameCountRequired(outFrameCount) + (phaseFraction != 0); - ALOG_ASSERT(0 < inFrameCount && inFrameCount < (1U << 31)); const uint32_t phaseWrapLimit = c.mL << c.mShift; + size_t inFrameCount = (phaseIncrement * (uint64_t)outFrameCount + phaseFraction) + / phaseWrapLimit; + // sanity check that inFrameCount is in signed 32 bit integer range. + ALOG_ASSERT(0 <= inFrameCount && inFrameCount < (1U << 31)); + + //ALOGV("inFrameCount:%d outFrameCount:%d" + // " phaseIncrement:%u phaseFraction:%u phaseWrapLimit:%u", + // inFrameCount, outFrameCount, phaseIncrement, phaseFraction, phaseWrapLimit); // NOTE: be very careful when modifying the code here. register // pressure is very high and a small change might cause the compiler @@ -472,10 +478,17 @@ void AudioResamplerDyn<TC, TI, TO>::resample(TO* out, size_t outFrameCount, // the following logic is a bit convoluted to keep the main processing loop // as tight as possible with register allocation. while (outputIndex < outputSampleCount) { - // buffer is empty, fetch a new one - while (mBuffer.frameCount == 0) { + //ALOGV("LOOP: inFrameCount:%d outputIndex:%d outFrameCount:%d" + // " phaseFraction:%u phaseWrapLimit:%u", + // inFrameCount, outputIndex, outFrameCount, phaseFraction, phaseWrapLimit); + + // check inputIndex overflow + ALOG_ASSERT(inputIndex <= mBuffer.frameCount, "inputIndex%d > frameCount%d", + inputIndex, mBuffer.frameCount); + // Buffer is empty, fetch a new one if necessary (inFrameCount > 0). + // We may not fetch a new buffer if the existing data is sufficient. + while (mBuffer.frameCount == 0 && inFrameCount > 0) { mBuffer.frameCount = inFrameCount; - ALOG_ASSERT(inFrameCount > 0); provider->getNextBuffer(&mBuffer, calculateOutputPTS(outputIndex / 2)); if (mBuffer.raw == NULL) { @@ -486,9 +499,9 @@ void AudioResamplerDyn<TC, TI, TO>::resample(TO* out, size_t outFrameCount, mInBuffer.template readAdvance<CHANNELS>( impulse, c.mHalfNumCoefs, reinterpret_cast<TI*>(mBuffer.raw), inputIndex); + inputIndex++; phaseFraction -= phaseWrapLimit; while (phaseFraction >= phaseWrapLimit) { - inputIndex++; if (inputIndex >= mBuffer.frameCount) { inputIndex = 0; provider->releaseBuffer(&mBuffer); @@ -497,6 +510,7 @@ void AudioResamplerDyn<TC, TI, TO>::resample(TO* out, size_t outFrameCount, mInBuffer.template readAdvance<CHANNELS>( impulse, c.mHalfNumCoefs, reinterpret_cast<TI*>(mBuffer.raw), inputIndex); + inputIndex++; phaseFraction -= phaseWrapLimit; } } @@ -507,9 +521,6 @@ void AudioResamplerDyn<TC, TI, TO>::resample(TO* out, size_t outFrameCount, const int halfNumCoefs = c.mHalfNumCoefs; const TO* const volumeSimd = mVolumeSimd; - // reread the last input in. - mInBuffer.template readAgain<CHANNELS>(impulse, halfNumCoefs, in, inputIndex); - // main processing loop while (CC_LIKELY(outputIndex < outputSampleCount)) { // caution: fir() is inlined and may be large. @@ -518,6 +529,10 @@ void AudioResamplerDyn<TC, TI, TO>::resample(TO* out, size_t outFrameCount, // from the input samples in impulse[-halfNumCoefs+1]... impulse[halfNumCoefs] // from the polyphase filter of (phaseFraction / phaseWrapLimit) in coefs. // + //ALOGV("LOOP2: inFrameCount:%d outputIndex:%d outFrameCount:%d" + // " phaseFraction:%u phaseWrapLimit:%u", + // inFrameCount, outputIndex, outFrameCount, phaseFraction, phaseWrapLimit); + ALOG_ASSERT(phaseFraction < phaseWrapLimit); fir<CHANNELS, LOCKED, STRIDE>( &out[outputIndex], phaseFraction, phaseWrapLimit, @@ -527,17 +542,20 @@ void AudioResamplerDyn<TC, TI, TO>::resample(TO* out, size_t outFrameCount, phaseFraction += phaseIncrement; while (phaseFraction >= phaseWrapLimit) { - inputIndex++; if (inputIndex >= frameCount) { goto done; // need a new buffer } mInBuffer.template readAdvance<CHANNELS>(impulse, halfNumCoefs, in, inputIndex); + inputIndex++; phaseFraction -= phaseWrapLimit; } } done: - // often arrives here when input buffer runs out - if (inputIndex >= frameCount) { + // We arrive here when we're finished or when the input buffer runs out. + // Regardless we need to release the input buffer if we've acquired it. + if (inputIndex > 0) { // we've acquired a buffer (alternatively could check frameCount) + ALOG_ASSERT(inputIndex == frameCount, "inputIndex(%d) != frameCount(%d)", + inputIndex, frameCount); // must have been fully read. inputIndex = 0; provider->releaseBuffer(&mBuffer); ALOG_ASSERT(mBuffer.frameCount == 0); @@ -545,14 +563,12 @@ done: } resample_exit: - // Release frames to avoid the count being inaccurate for pts timing. - // TODO: Avoid this extra check by making fetch count exact. This is tricky - // due to the overfetching mechanism which loads unnecessarily when - // mBuffer.frameCount == 0. - if (inputIndex) { - mBuffer.frameCount = inputIndex; - provider->releaseBuffer(&mBuffer); - } + // inputIndex must be zero in all three cases: + // (1) the buffer never was been acquired; (2) the buffer was + // released at "done:"; or (3) getNextBuffer() failed. + ALOG_ASSERT(inputIndex == 0, "Releasing: inputindex:%d frameCount:%d phaseFraction:%u", + inputIndex, mBuffer.frameCount, phaseFraction); + ALOG_ASSERT(mBuffer.frameCount == 0); // there must be no frames in the buffer mInBuffer.setImpulse(impulse); mPhaseFraction = phaseFraction; } diff --git a/services/audioflinger/AudioWatchdog.cpp b/services/audioflinger/AudioWatchdog.cpp index 93d185e..877e776 100644 --- a/services/audioflinger/AudioWatchdog.cpp +++ b/services/audioflinger/AudioWatchdog.cpp @@ -34,7 +34,7 @@ void AudioWatchdogDump::dump(int fd) } else { strcpy(buf, "N/A\n"); } - fdprintf(fd, "Watchdog: underruns=%u, logs=%u, most recent underrun log at %s", + dprintf(fd, "Watchdog: underruns=%u, logs=%u, most recent underrun log at %s", mUnderruns, mLogs, buf); } diff --git a/services/audioflinger/FastMixer.cpp b/services/audioflinger/FastMixer.cpp index c9a3f10..c486630 100644 --- a/services/audioflinger/FastMixer.cpp +++ b/services/audioflinger/FastMixer.cpp @@ -26,7 +26,6 @@ #define ATRACE_TAG ATRACE_TAG_AUDIO #include "Configuration.h" -#include <sys/atomics.h> #include <time.h> #include <utils/Log.h> #include <utils/Trace.h> @@ -492,7 +491,7 @@ static int compare_uint32_t(const void *pa, const void *pb) void FastMixerDumpState::dump(int fd) const { if (mCommand == FastMixerState::INITIAL) { - fdprintf(fd, " FastMixer not initialized\n"); + dprintf(fd, " FastMixer not initialized\n"); return; } #define COMMAND_MAX 32 @@ -526,10 +525,10 @@ void FastMixerDumpState::dump(int fd) const double measuredWarmupMs = (mMeasuredWarmupTs.tv_sec * 1000.0) + (mMeasuredWarmupTs.tv_nsec / 1000000.0); double mixPeriodSec = (double) mFrameCount / (double) mSampleRate; - fdprintf(fd, " FastMixer command=%s writeSequence=%u framesWritten=%u\n" - " numTracks=%u writeErrors=%u underruns=%u overruns=%u\n" - " sampleRate=%u frameCount=%zu measuredWarmup=%.3g ms, warmupCycles=%u\n" - " mixPeriod=%.2f ms\n", + dprintf(fd, " FastMixer command=%s writeSequence=%u framesWritten=%u\n" + " numTracks=%u writeErrors=%u underruns=%u overruns=%u\n" + " sampleRate=%u frameCount=%zu measuredWarmup=%.3g ms, warmupCycles=%u\n" + " mixPeriod=%.2f ms\n", string, mWriteSequence, mFramesWritten, mNumTracks, mWriteErrors, mUnderruns, mOverruns, mSampleRate, mFrameCount, measuredWarmupMs, mWarmupCycles, @@ -581,26 +580,26 @@ void FastMixerDumpState::dump(int fd) const #endif } if (n) { - fdprintf(fd, " Simple moving statistics over last %.1f seconds:\n", - wall.n() * mixPeriodSec); - fdprintf(fd, " wall clock time in ms per mix cycle:\n" - " mean=%.2f min=%.2f max=%.2f stddev=%.2f\n", - wall.mean()*1e-6, wall.minimum()*1e-6, wall.maximum()*1e-6, - wall.stddev()*1e-6); - fdprintf(fd, " raw CPU load in us per mix cycle:\n" - " mean=%.0f min=%.0f max=%.0f stddev=%.0f\n", - loadNs.mean()*1e-3, loadNs.minimum()*1e-3, loadNs.maximum()*1e-3, - loadNs.stddev()*1e-3); + dprintf(fd, " Simple moving statistics over last %.1f seconds:\n", + wall.n() * mixPeriodSec); + dprintf(fd, " wall clock time in ms per mix cycle:\n" + " mean=%.2f min=%.2f max=%.2f stddev=%.2f\n", + wall.mean()*1e-6, wall.minimum()*1e-6, wall.maximum()*1e-6, + wall.stddev()*1e-6); + dprintf(fd, " raw CPU load in us per mix cycle:\n" + " mean=%.0f min=%.0f max=%.0f stddev=%.0f\n", + loadNs.mean()*1e-3, loadNs.minimum()*1e-3, loadNs.maximum()*1e-3, + loadNs.stddev()*1e-3); } else { - fdprintf(fd, " No FastMixer statistics available currently\n"); + dprintf(fd, " No FastMixer statistics available currently\n"); } #ifdef CPU_FREQUENCY_STATISTICS - fdprintf(fd, " CPU clock frequency in MHz:\n" - " mean=%.0f min=%.0f max=%.0f stddev=%.0f\n", - kHz.mean()*1e-3, kHz.minimum()*1e-3, kHz.maximum()*1e-3, kHz.stddev()*1e-3); - fdprintf(fd, " adjusted CPU load in MHz (i.e. normalized for CPU clock frequency):\n" - " mean=%.1f min=%.1f max=%.1f stddev=%.1f\n", - loadMHz.mean(), loadMHz.minimum(), loadMHz.maximum(), loadMHz.stddev()); + dprintf(fd, " CPU clock frequency in MHz:\n" + " mean=%.0f min=%.0f max=%.0f stddev=%.0f\n", + kHz.mean()*1e-3, kHz.minimum()*1e-3, kHz.maximum()*1e-3, kHz.stddev()*1e-3); + dprintf(fd, " adjusted CPU load in MHz (i.e. normalized for CPU clock frequency):\n" + " mean=%.1f min=%.1f max=%.1f stddev=%.1f\n", + loadMHz.mean(), loadMHz.minimum(), loadMHz.maximum(), loadMHz.stddev()); #endif if (tail != NULL) { qsort(tail, n, sizeof(uint32_t), compare_uint32_t); @@ -611,12 +610,12 @@ void FastMixerDumpState::dump(int fd) const left.sample(tail[i]); right.sample(tail[n - (i + 1)]); } - fdprintf(fd, " Distribution of mix cycle times in ms for the tails (> ~3 stddev outliers):\n" - " left tail: mean=%.2f min=%.2f max=%.2f stddev=%.2f\n" - " right tail: mean=%.2f min=%.2f max=%.2f stddev=%.2f\n", - left.mean()*1e-6, left.minimum()*1e-6, left.maximum()*1e-6, left.stddev()*1e-6, - right.mean()*1e-6, right.minimum()*1e-6, right.maximum()*1e-6, - right.stddev()*1e-6); + dprintf(fd, " Distribution of mix cycle times in ms for the tails (> ~3 stddev outliers):\n" + " left tail: mean=%.2f min=%.2f max=%.2f stddev=%.2f\n" + " right tail: mean=%.2f min=%.2f max=%.2f stddev=%.2f\n", + left.mean()*1e-6, left.minimum()*1e-6, left.maximum()*1e-6, left.stddev()*1e-6, + right.mean()*1e-6, right.minimum()*1e-6, right.maximum()*1e-6, + right.stddev()*1e-6); delete[] tail; } #endif @@ -626,9 +625,9 @@ void FastMixerDumpState::dump(int fd) const // Instead we always display all tracks, with an indication // of whether we think the track is active. uint32_t trackMask = mTrackMask; - fdprintf(fd, " Fast tracks: kMaxFastTracks=%u activeMask=%#x\n", + dprintf(fd, " Fast tracks: kMaxFastTracks=%u activeMask=%#x\n", FastMixerState::kMaxFastTracks, trackMask); - fdprintf(fd, " Index Active Full Partial Empty Recent Ready\n"); + dprintf(fd, " Index Active Full Partial Empty Recent Ready\n"); for (uint32_t i = 0; i < FastMixerState::kMaxFastTracks; ++i, trackMask >>= 1) { bool isActive = trackMask & 1; const FastTrackDump *ftDump = &mTracks[i]; @@ -648,7 +647,7 @@ void FastMixerDumpState::dump(int fd) const mostRecent = "?"; break; } - fdprintf(fd, " %5u %6s %4u %7u %5u %7s %5zu\n", i, isActive ? "yes" : "no", + dprintf(fd, " %5u %6s %4u %7u %5u %7s %5zu\n", i, isActive ? "yes" : "no", (underruns.mBitFields.mFull) & UNDERRUN_MASK, (underruns.mBitFields.mPartial) & UNDERRUN_MASK, (underruns.mBitFields.mEmpty) & UNDERRUN_MASK, diff --git a/services/audioflinger/StateQueue.cpp b/services/audioflinger/StateQueue.cpp index 48399c0..7e01c9f 100644 --- a/services/audioflinger/StateQueue.cpp +++ b/services/audioflinger/StateQueue.cpp @@ -28,12 +28,12 @@ namespace android { #ifdef STATE_QUEUE_DUMP void StateQueueObserverDump::dump(int fd) { - fdprintf(fd, "State queue observer: stateChanges=%u\n", mStateChanges); + dprintf(fd, "State queue observer: stateChanges=%u\n", mStateChanges); } void StateQueueMutatorDump::dump(int fd) { - fdprintf(fd, "State queue mutator: pushDirty=%u pushAck=%u blockedSequence=%u\n", + dprintf(fd, "State queue mutator: pushDirty=%u pushAck=%u blockedSequence=%u\n", mPushDirty, mPushAck, mBlockedSequence); } #endif diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp index d6333be..d08c966 100644 --- a/services/audioflinger/Threads.cpp +++ b/services/audioflinger/Threads.cpp @@ -574,30 +574,30 @@ void AudioFlinger::ThreadBase::dumpBase(int fd, const Vector<String16>& args __u bool locked = AudioFlinger::dumpTryLock(mLock); if (!locked) { - fdprintf(fd, "thread %p maybe dead locked\n", this); + dprintf(fd, "thread %p maybe dead locked\n", this); } - fdprintf(fd, " I/O handle: %d\n", mId); - fdprintf(fd, " TID: %d\n", getTid()); - fdprintf(fd, " Standby: %s\n", mStandby ? "yes" : "no"); - fdprintf(fd, " Sample rate: %u\n", mSampleRate); - fdprintf(fd, " HAL frame count: %zu\n", mFrameCount); - fdprintf(fd, " HAL buffer size: %u bytes\n", mBufferSize); - fdprintf(fd, " Channel Count: %u\n", mChannelCount); - fdprintf(fd, " Channel Mask: 0x%08x (%s)\n", mChannelMask, + dprintf(fd, " I/O handle: %d\n", mId); + dprintf(fd, " TID: %d\n", getTid()); + dprintf(fd, " Standby: %s\n", mStandby ? "yes" : "no"); + dprintf(fd, " Sample rate: %u\n", mSampleRate); + dprintf(fd, " HAL frame count: %zu\n", mFrameCount); + dprintf(fd, " HAL buffer size: %u bytes\n", mBufferSize); + dprintf(fd, " Channel Count: %u\n", mChannelCount); + dprintf(fd, " Channel Mask: 0x%08x (%s)\n", mChannelMask, channelMaskToString(mChannelMask, mType != RECORD).string()); - fdprintf(fd, " Format: 0x%x (%s)\n", mFormat, formatToString(mFormat)); - fdprintf(fd, " Frame size: %zu\n", mFrameSize); - fdprintf(fd, " Pending config events:"); + dprintf(fd, " Format: 0x%x (%s)\n", mFormat, formatToString(mFormat)); + dprintf(fd, " Frame size: %zu\n", mFrameSize); + dprintf(fd, " Pending config events:"); size_t numConfig = mConfigEvents.size(); if (numConfig) { for (size_t i = 0; i < numConfig; i++) { mConfigEvents[i]->dump(buffer, SIZE); - fdprintf(fd, "\n %s", buffer); + dprintf(fd, "\n %s", buffer); } - fdprintf(fd, "\n"); + dprintf(fd, "\n"); } else { - fdprintf(fd, " none\n"); + dprintf(fd, " none\n"); } if (locked) { @@ -1260,15 +1260,15 @@ void AudioFlinger::PlaybackThread::dumpTracks(int fd, const Vector<String16>& ar // These values are "raw"; they will wrap around. See prepareTracks_l() for a better way. FastTrackUnderruns underruns = getFastTrackUnderruns(0); - fdprintf(fd, " Normal mixer raw underrun counters: partial=%u empty=%u\n", + dprintf(fd, " Normal mixer raw underrun counters: partial=%u empty=%u\n", underruns.mBitFields.mPartial, underruns.mBitFields.mEmpty); size_t numtracks = mTracks.size(); size_t numactive = mActiveTracks.size(); - fdprintf(fd, " %d Tracks", numtracks); + dprintf(fd, " %d Tracks", numtracks); size_t numactiveseen = 0; if (numtracks) { - fdprintf(fd, " of which %d are active\n", numactive); + dprintf(fd, " of which %d are active\n", numactive); Track::appendDumpHeader(result); for (size_t i = 0; i < numtracks; ++i) { sp<Track> track = mTracks[i]; @@ -1300,22 +1300,21 @@ void AudioFlinger::PlaybackThread::dumpTracks(int fd, const Vector<String16>& ar } write(fd, result.string(), result.size()); - } void AudioFlinger::PlaybackThread::dumpInternals(int fd, const Vector<String16>& args) { - fdprintf(fd, "\nOutput thread %p:\n", this); - fdprintf(fd, " Normal frame count: %zu\n", mNormalFrameCount); - fdprintf(fd, " Last write occurred (msecs): %llu\n", ns2ms(systemTime() - mLastWriteTime)); - fdprintf(fd, " Total writes: %d\n", mNumWrites); - fdprintf(fd, " Delayed writes: %d\n", mNumDelayedWrites); - fdprintf(fd, " Blocked in write: %s\n", mInWrite ? "yes" : "no"); - fdprintf(fd, " Suspend count: %d\n", mSuspended); - fdprintf(fd, " Sink buffer : %p\n", mSinkBuffer); - fdprintf(fd, " Mixer buffer: %p\n", mMixerBuffer); - fdprintf(fd, " Effect buffer: %p\n", mEffectBuffer); - fdprintf(fd, " Fast track availMask=%#x\n", mFastTrackAvailMask); + dprintf(fd, "\nOutput thread %p:\n", this); + dprintf(fd, " Normal frame count: %zu\n", mNormalFrameCount); + dprintf(fd, " Last write occurred (msecs): %llu\n", ns2ms(systemTime() - mLastWriteTime)); + dprintf(fd, " Total writes: %d\n", mNumWrites); + dprintf(fd, " Delayed writes: %d\n", mNumDelayedWrites); + dprintf(fd, " Blocked in write: %s\n", mInWrite ? "yes" : "no"); + dprintf(fd, " Suspend count: %d\n", mSuspended); + dprintf(fd, " Sink buffer : %p\n", mSinkBuffer); + dprintf(fd, " Mixer buffer: %p\n", mMixerBuffer); + dprintf(fd, " Effect buffer: %p\n", mEffectBuffer); + dprintf(fd, " Fast track availMask=%#x\n", mFastTrackAvailMask); dumpBase(fd, args); } @@ -3799,7 +3798,7 @@ void AudioFlinger::MixerThread::dumpInternals(int fd, const Vector<String16>& ar PlaybackThread::dumpInternals(fd, args); - fdprintf(fd, " AudioMixer tracks: 0x%08x\n", mAudioMixer->trackNames()); + dprintf(fd, " AudioMixer tracks: 0x%08x\n", mAudioMixer->trackNames()); // Make a non-atomic copy of fast mixer dump state so it won't change underneath us const FastMixerDumpState copy(mFastMixerDumpState); @@ -5717,12 +5716,12 @@ void AudioFlinger::RecordThread::dump(int fd, const Vector<String16>& args) void AudioFlinger::RecordThread::dumpInternals(int fd, const Vector<String16>& args) { - fdprintf(fd, "\nInput thread %p:\n", this); + dprintf(fd, "\nInput thread %p:\n", this); if (mActiveTracks.size() > 0) { - fdprintf(fd, " Buffer size: %zu bytes\n", mBufferSize); + dprintf(fd, " Buffer size: %zu bytes\n", mBufferSize); } else { - fdprintf(fd, " No active record clients\n"); + dprintf(fd, " No active record clients\n"); } dprintf(fd, " Fast track available: %s\n", mFastTrackAvail ? "yes" : "no"); @@ -5738,9 +5737,9 @@ void AudioFlinger::RecordThread::dumpTracks(int fd, const Vector<String16>& args size_t numtracks = mTracks.size(); size_t numactive = mActiveTracks.size(); size_t numactiveseen = 0; - fdprintf(fd, " %d Tracks", numtracks); + dprintf(fd, " %d Tracks", numtracks); if (numtracks) { - fdprintf(fd, " of which %d are active\n", numactive); + dprintf(fd, " of which %d are active\n", numactive); RecordTrack::appendDumpHeader(result); for (size_t i = 0; i < numtracks ; ++i) { sp<RecordTrack> track = mTracks[i]; @@ -5754,7 +5753,7 @@ void AudioFlinger::RecordThread::dumpTracks(int fd, const Vector<String16>& args } } } else { - fdprintf(fd, "\n"); + dprintf(fd, "\n"); } if (numactiveseen != numactive) { diff --git a/services/audioflinger/tests/Android.mk b/services/audioflinger/tests/Android.mk new file mode 100644 index 0000000..874f18f --- /dev/null +++ b/services/audioflinger/tests/Android.mk @@ -0,0 +1,31 @@ +# Build the unit tests for audioflinger + +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_SHARED_LIBRARIES := \ + liblog \ + libutils \ + libcutils \ + libstlport \ + libaudioutils \ + libaudioresampler + +LOCAL_STATIC_LIBRARIES := \ + libgtest \ + libgtest_main + +LOCAL_C_INCLUDES := \ + bionic \ + bionic/libstdc++/include \ + external/gtest/include \ + external/stlport/stlport \ + frameworks/av/services/audioflinger + +LOCAL_SRC_FILES := \ + resampler_tests.cpp + +LOCAL_MODULE := resampler_tests +LOCAL_MODULE_TAGS := tests + +include $(BUILD_EXECUTABLE) diff --git a/services/audioflinger/tests/build_and_run_all_unit_tests.sh b/services/audioflinger/tests/build_and_run_all_unit_tests.sh new file mode 100755 index 0000000..2c453b0 --- /dev/null +++ b/services/audioflinger/tests/build_and_run_all_unit_tests.sh @@ -0,0 +1,22 @@ +#!/bin/bash + +if [ -z "$ANDROID_BUILD_TOP" ]; then + echo "Android build environment not set" + exit -1 +fi + +# ensure we have mm +. $ANDROID_BUILD_TOP/build/envsetup.sh + +pushd $ANDROID_BUILD_TOP/frameworks/av/services/audioflinger/ +pwd +mm + +echo "waiting for device" +adb root && adb wait-for-device remount +adb push $OUT/system/lib/libaudioresampler.so /system/lib +adb push $OUT/system/bin/resampler_tests /system/bin + +sh $ANDROID_BUILD_TOP/frameworks/av/services/audioflinger/tests/run_all_unit_tests.sh + +popd diff --git a/services/audioflinger/tests/resampler_tests.cpp b/services/audioflinger/tests/resampler_tests.cpp new file mode 100644 index 0000000..8f9c270 --- /dev/null +++ b/services/audioflinger/tests/resampler_tests.cpp @@ -0,0 +1,471 @@ +/* + * Copyright (C) 2014 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 "audioflinger_resampler_tests" + +#include <unistd.h> +#include <stdio.h> +#include <stdlib.h> +#include <fcntl.h> +#include <string.h> +#include <sys/mman.h> +#include <sys/stat.h> +#include <errno.h> +#include <time.h> +#include <math.h> +#include <vector> +#include <utility> +#include <cutils/log.h> +#include <gtest/gtest.h> +#include <media/AudioBufferProvider.h> +#include "AudioResampler.h" + +#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0])) + +template<typename T, typename U> +struct is_same +{ + static const bool value = false; +}; + +template<typename T> +struct is_same<T, T> // partial specialization +{ + static const bool value = true; +}; + +template<typename T> +static inline T convertValue(double val) +{ + if (is_same<T, int16_t>::value) { + return floor(val * 32767.0 + 0.5); + } else if (is_same<T, int32_t>::value) { + return floor(val * (1UL<<31) + 0.5); + } + return val; // assume float or double +} + +/* Creates a type-independent audio buffer provider from + * a buffer base address, size, framesize, and input increment array. + * + * No allocation or deallocation of the provided buffer is done. + */ +class TestProvider : public android::AudioBufferProvider { +public: + TestProvider(const void* addr, size_t frames, size_t frameSize, + const std::vector<size_t>& inputIncr) + : mAddr(addr), + mNumFrames(frames), + mFrameSize(frameSize), + mNextFrame(0), mUnrel(0), mInputIncr(inputIncr), mNextIdx(0) + { + } + + virtual android::status_t getNextBuffer(Buffer* buffer, int64_t pts __unused = kInvalidPTS ) + { + size_t requestedFrames = buffer->frameCount; + if (requestedFrames > mNumFrames - mNextFrame) { + buffer->frameCount = mNumFrames - mNextFrame; + } + if (!mInputIncr.empty()) { + size_t provided = mInputIncr[mNextIdx++]; + ALOGV("getNextBuffer() mValue[%d]=%u not %u", + mNextIdx-1, provided, buffer->frameCount); + if (provided < buffer->frameCount) { + buffer->frameCount = provided; + } + if (mNextIdx >= mInputIncr.size()) { + mNextIdx = 0; + } + } + ALOGV("getNextBuffer() requested %u frames out of %u frames available" + " and returned %u frames\n", + requestedFrames, mNumFrames - mNextFrame, buffer->frameCount); + mUnrel = buffer->frameCount; + if (buffer->frameCount > 0) { + buffer->raw = (char *)mAddr + mFrameSize * mNextFrame; + return android::NO_ERROR; + } else { + buffer->raw = NULL; + return android::NOT_ENOUGH_DATA; + } + } + + virtual void releaseBuffer(Buffer* buffer) + { + if (buffer->frameCount > mUnrel) { + ALOGE("releaseBuffer() released %u frames but only %u available " + "to release\n", buffer->frameCount, mUnrel); + mNextFrame += mUnrel; + mUnrel = 0; + } else { + + ALOGV("releaseBuffer() released %u frames out of %u frames available " + "to release\n", buffer->frameCount, mUnrel); + mNextFrame += buffer->frameCount; + mUnrel -= buffer->frameCount; + } + buffer->frameCount = 0; + buffer->raw = NULL; + } + + void reset() + { + mNextFrame = 0; + } + + size_t getNumFrames() + { + return mNumFrames; + } + + void setIncr(const std::vector<size_t> inputIncr) + { + mNextIdx = 0; + mInputIncr = inputIncr; + } + +protected: + const void* mAddr; // base address + size_t mNumFrames; // total frames + int mFrameSize; // frame size (# channels * bytes per sample) + size_t mNextFrame; // index of next frame to provide + size_t mUnrel; // number of frames not yet released + std::vector<size_t> mInputIncr; // number of frames provided per call + size_t mNextIdx; // index of next entry in mInputIncr to use +}; + +/* Creates a buffer filled with a sine wave. + * + * Returns a pair consisting of the sine signal buffer and the number of frames. + * The caller must delete[] the buffer when no longer needed (no shared_ptr<>). + */ +template<typename T> +static std::pair<T*, size_t> createSine(size_t channels, + double freq, double samplingRate, double time) +{ + double tscale = 1. / samplingRate; + size_t frames = static_cast<size_t>(samplingRate * time); + T* buffer = new T[frames * channels]; + for (size_t i = 0; i < frames; ++i) { + double t = i * tscale; + double y = sin(2. * M_PI * freq * t); + T yt = convertValue<T>(y); + + for (size_t j = 0; j < channels; ++j) { + buffer[i*channels + j] = yt / (j + 1); + } + } + return std::make_pair(buffer, frames); +} + +/* Creates a buffer filled with a chirp signal (a sine wave sweep). + * + * Returns a pair consisting of the chirp signal buffer and the number of frames. + * The caller must delete[] the buffer when no longer needed (no shared_ptr<>). + * + * When creating the Chirp, note that the frequency is the true sinusoidal + * frequency not the sampling rate. + * + * http://en.wikipedia.org/wiki/Chirp + */ +template<typename T> +static std::pair<T*, size_t> createChirp(size_t channels, + double minfreq, double maxfreq, double samplingRate, double time) +{ + double tscale = 1. / samplingRate; + size_t frames = static_cast<size_t>(samplingRate * time); + T *buffer = new T[frames * channels]; + // note the chirp constant k has a divide-by-two. + double k = (maxfreq - minfreq) / (2. * time); + for (size_t i = 0; i < frames; ++i) { + double t = i * tscale; + double y = sin(2. * M_PI * (k * t + minfreq) * t); + T yt = convertValue<T>(y); + + for (size_t j = 0; j < channels; ++j) { + buffer[i*channels + j] = yt / (j + 1); + } + } + return std::make_pair(buffer, frames); +} + +/* This derived class creates a buffer provider of datatype T, + * consisting of an input signal, e.g. from createChirp(). + * The number of frames can be obtained from the base class + * TestProvider::getNumFrames(). + */ +template <typename T> +class SignalProvider : public TestProvider { +public: + SignalProvider(const std::pair<T*, size_t>& bufferInfo, size_t channels, + const std::vector<size_t>& values) + : TestProvider(bufferInfo.first, bufferInfo.second, channels * sizeof(T), values), + mManagedPtr(bufferInfo.first) + { + } + + virtual ~SignalProvider() + { + delete[] mManagedPtr; + } + +protected: + T* mManagedPtr; +}; + +void resample(void *output, size_t outputFrames, const std::vector<size_t> &outputIncr, + android::AudioBufferProvider *provider, android::AudioResampler *resampler) +{ + for (size_t i = 0, j = 0; i < outputFrames; ) { + size_t thisFrames = outputIncr[j++]; + if (j >= outputIncr.size()) { + j = 0; + } + if (thisFrames == 0 || thisFrames > outputFrames - i) { + thisFrames = outputFrames - i; + } + resampler->resample((int32_t*) output + 2*i, thisFrames, provider); + i += thisFrames; + } +} + +void buffercmp(const void *reference, const void *test, + size_t outputFrameSize, size_t outputFrames) +{ + for (size_t i = 0; i < outputFrames; ++i) { + int check = memcmp((const char*)reference + i * outputFrameSize, + (const char*)test + i * outputFrameSize, outputFrameSize); + if (check) { + ALOGE("Failure at frame %d", i); + ASSERT_EQ(check, 0); /* fails */ + } + } +} + +void testBufferIncrement(size_t channels, unsigned inputFreq, unsigned outputFreq, + enum android::AudioResampler::src_quality quality) +{ + // create the provider + std::vector<size_t> inputIncr; + SignalProvider<int16_t> provider(createChirp<int16_t>(channels, + 0., outputFreq/2., outputFreq, outputFreq/2000.), + channels, inputIncr); + + // calculate the output size + size_t outputFrames = ((int64_t) provider.getNumFrames() * outputFreq) / inputFreq; + size_t outputFrameSize = 2 * sizeof(int32_t); + size_t outputSize = outputFrameSize * outputFrames; + outputSize &= ~7; + + // create the resampler + const int volumePrecision = 12; /* typical unity gain */ + android::AudioResampler* resampler; + + resampler = android::AudioResampler::create(16, channels, outputFreq, quality); + resampler->setSampleRate(inputFreq); + resampler->setVolume(1 << volumePrecision, 1 << volumePrecision); + + // set up the reference run + std::vector<size_t> refIncr; + refIncr.push_back(outputFrames); + void* reference = malloc(outputSize); + resample(reference, outputFrames, refIncr, &provider, resampler); + + provider.reset(); + +#if 0 + /* this test will fail - API interface issue: reset() does not clear internal buffers */ + resampler->reset(); +#else + delete resampler; + resampler = android::AudioResampler::create(16, channels, outputFreq, quality); + resampler->setSampleRate(inputFreq); + resampler->setVolume(1 << volumePrecision, 1 << volumePrecision); +#endif + + // set up the test run + std::vector<size_t> outIncr; + outIncr.push_back(1); + outIncr.push_back(2); + outIncr.push_back(3); + void* test = malloc(outputSize); + resample(test, outputFrames, outIncr, &provider, resampler); + + // check + buffercmp(reference, test, outputFrameSize, outputFrames); + + free(reference); + free(test); + delete resampler; +} + +template <typename T> +inline double sqr(T v) +{ + double dv = static_cast<double>(v); + return dv * dv; +} + +template <typename T> +double signalEnergy(T *start, T *end, unsigned stride) +{ + double accum = 0; + + for (T *p = start; p < end; p += stride) { + accum += sqr(*p); + } + unsigned count = (end - start + stride - 1) / stride; + return accum / count; +} + +void testStopbandDownconversion(size_t channels, + unsigned inputFreq, unsigned outputFreq, + unsigned passband, unsigned stopband, + enum android::AudioResampler::src_quality quality) +{ + // create the provider + std::vector<size_t> inputIncr; + SignalProvider<int16_t> provider(createChirp<int16_t>(channels, + 0., inputFreq/2., inputFreq, inputFreq/2000.), + channels, inputIncr); + + // calculate the output size + size_t outputFrames = ((int64_t) provider.getNumFrames() * outputFreq) / inputFreq; + size_t outputFrameSize = 2 * sizeof(int32_t); + size_t outputSize = outputFrameSize * outputFrames; + outputSize &= ~7; + + // create the resampler + const int volumePrecision = 12; /* typical unity gain */ + android::AudioResampler* resampler; + + resampler = android::AudioResampler::create(16, channels, outputFreq, quality); + resampler->setSampleRate(inputFreq); + resampler->setVolume(1 << volumePrecision, 1 << volumePrecision); + + // set up the reference run + std::vector<size_t> refIncr; + refIncr.push_back(outputFrames); + void* reference = malloc(outputSize); + resample(reference, outputFrames, refIncr, &provider, resampler); + + int32_t *out = reinterpret_cast<int32_t *>(reference); + + // check signal energy in passband + const unsigned passbandFrame = passband * outputFreq / 1000.; + const unsigned stopbandFrame = stopband * outputFreq / 1000.; + + // check each channel separately + for (size_t i = 0; i < channels; ++i) { + double passbandEnergy = signalEnergy(out, out + passbandFrame * channels, channels); + double stopbandEnergy = signalEnergy(out + stopbandFrame * channels, + out + outputFrames * channels, channels); + double dbAtten = -10. * log10(stopbandEnergy / passbandEnergy); + ASSERT_GT(dbAtten, 60.); + +#if 0 + // internal verification + printf("if:%d of:%d pbf:%d sbf:%d sbe: %f pbe: %f db: %.2f\n", + provider.getNumFrames(), outputFrames, + passbandFrame, stopbandFrame, stopbandEnergy, passbandEnergy, dbAtten); + for (size_t i = 0; i < 10; ++i) { + printf("%d\n", out[i+passbandFrame*channels]); + } + for (size_t i = 0; i < 10; ++i) { + printf("%d\n", out[i+stopbandFrame*channels]); + } +#endif + } + + free(reference); + delete resampler; +} + +/* Buffer increment test + * + * We compare a reference output, where we consume and process the entire + * buffer at a time, and a test output, where we provide small chunks of input + * data and process small chunks of output (which may not be equivalent in size). + * + * Two subtests - fixed phase (3:2 down) and interpolated phase (147:320 up) + */ +TEST(audioflinger_resampler, bufferincrement_fixedphase) { + // all of these work + static const enum android::AudioResampler::src_quality kQualityArray[] = { + android::AudioResampler::LOW_QUALITY, + android::AudioResampler::MED_QUALITY, + android::AudioResampler::HIGH_QUALITY, + android::AudioResampler::VERY_HIGH_QUALITY, + android::AudioResampler::DYN_LOW_QUALITY, + android::AudioResampler::DYN_MED_QUALITY, + android::AudioResampler::DYN_HIGH_QUALITY, + }; + + for (size_t i = 0; i < ARRAY_SIZE(kQualityArray); ++i) { + testBufferIncrement(2, 48000, 32000, kQualityArray[i]); + } +} + +TEST(audioflinger_resampler, bufferincrement_interpolatedphase) { + // all of these work except low quality + static const enum android::AudioResampler::src_quality kQualityArray[] = { +// android::AudioResampler::LOW_QUALITY, + android::AudioResampler::MED_QUALITY, + android::AudioResampler::HIGH_QUALITY, + android::AudioResampler::VERY_HIGH_QUALITY, + android::AudioResampler::DYN_LOW_QUALITY, + android::AudioResampler::DYN_MED_QUALITY, + android::AudioResampler::DYN_HIGH_QUALITY, + }; + + for (size_t i = 0; i < ARRAY_SIZE(kQualityArray); ++i) { + testBufferIncrement(2, 22050, 48000, kQualityArray[i]); + } +} + +/* Simple aliasing test + * + * This checks stopband response of the chirp signal to make sure frequencies + * are properly suppressed. It uses downsampling because the stopband can be + * clearly isolated by input frequencies exceeding the output sample rate (nyquist). + */ +TEST(audioflinger_resampler, stopbandresponse) { + // not all of these may work (old resamplers fail on downsampling) + static const enum android::AudioResampler::src_quality kQualityArray[] = { + //android::AudioResampler::LOW_QUALITY, + //android::AudioResampler::MED_QUALITY, + //android::AudioResampler::HIGH_QUALITY, + //android::AudioResampler::VERY_HIGH_QUALITY, + android::AudioResampler::DYN_LOW_QUALITY, + android::AudioResampler::DYN_MED_QUALITY, + android::AudioResampler::DYN_HIGH_QUALITY, + }; + + // in this test we assume a maximum transition band between 12kHz and 20kHz. + // there must be at least 60dB relative attenuation between stopband and passband. + for (size_t i = 0; i < ARRAY_SIZE(kQualityArray); ++i) { + testStopbandDownconversion(2, 48000, 32000, 12000, 20000, kQualityArray[i]); + } + + // in this test we assume a maximum transition band between 7kHz and 15kHz. + // there must be at least 60dB relative attenuation between stopband and passband. + // (the weird ratio triggers interpolative resampling) + for (size_t i = 0; i < ARRAY_SIZE(kQualityArray); ++i) { + testStopbandDownconversion(2, 48000, 22101, 7000, 15000, kQualityArray[i]); + } +} diff --git a/services/audioflinger/tests/run_all_unit_tests.sh b/services/audioflinger/tests/run_all_unit_tests.sh new file mode 100755 index 0000000..ffae6ae --- /dev/null +++ b/services/audioflinger/tests/run_all_unit_tests.sh @@ -0,0 +1,11 @@ +#!/bin/bash + +if [ -z "$ANDROID_BUILD_TOP" ]; then + echo "Android build environment not set" + exit -1 +fi + +echo "waiting for device" +adb root && adb wait-for-device remount + +adb shell /system/bin/resampler_tests |