FastMixer.cpp revision b9e722bdf00333557578497c5abfc77c5d779ecb
1/* 2 * Copyright (C) 2012 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17// <IMPORTANT_WARNING> 18// Design rules for threadLoop() are given in the comments at section "Fast mixer thread" of 19// StateQueue.h. In particular, avoid library and system calls except at well-known points. 20// The design rules are only for threadLoop(), and don't apply to FastMixerDumpState methods. 21// </IMPORTANT_WARNING> 22 23#define LOG_TAG "FastMixer" 24//#define LOG_NDEBUG 0 25 26#define ATRACE_TAG ATRACE_TAG_AUDIO 27 28#include "Configuration.h" 29#include <time.h> 30#include <utils/Log.h> 31#include <utils/Trace.h> 32#include <system/audio.h> 33#ifdef FAST_MIXER_STATISTICS 34#include <cpustats/CentralTendencyStatistics.h> 35#ifdef CPU_FREQUENCY_STATISTICS 36#include <cpustats/ThreadCpuUsage.h> 37#endif 38#endif 39#include "AudioMixer.h" 40#include "FastMixer.h" 41 42#define FCC_2 2 // fixed channel count assumption 43 44namespace android { 45 46/*static*/ const FastMixerState FastMixer::initial; 47 48FastMixer::FastMixer() : FastThread(), 49 slopNs(0), 50 // fastTrackNames 51 // generations 52 outputSink(NULL), 53 outputSinkGen(0), 54 mixer(NULL), 55 mMixerBuffer(NULL), 56 mMixerBufferState(UNDEFINED), 57 format(Format_Invalid), 58 sampleRate(0), 59 fastTracksGen(0), 60 totalNativeFramesWritten(0), 61 // timestamp 62 nativeFramesWrittenButNotPresented(0) // the = 0 is to silence the compiler 63{ 64 // FIXME pass initial as parameter to base class constructor, and make it static local 65 previous = &initial; 66 current = &initial; 67 68 mDummyDumpState = &dummyDumpState; 69 70 unsigned i; 71 for (i = 0; i < FastMixerState::kMaxFastTracks; ++i) { 72 fastTrackNames[i] = -1; 73 generations[i] = 0; 74 } 75#ifdef FAST_MIXER_STATISTICS 76 oldLoad.tv_sec = 0; 77 oldLoad.tv_nsec = 0; 78#endif 79} 80 81FastMixer::~FastMixer() 82{ 83} 84 85FastMixerStateQueue* FastMixer::sq() 86{ 87 return &mSQ; 88} 89 90const FastThreadState *FastMixer::poll() 91{ 92 return mSQ.poll(); 93} 94 95void FastMixer::setLog(NBLog::Writer *logWriter) 96{ 97 if (mixer != NULL) { 98 mixer->setLog(logWriter); 99 } 100} 101 102void FastMixer::onIdle() 103{ 104 preIdle = *(const FastMixerState *)current; 105 current = &preIdle; 106} 107 108void FastMixer::onExit() 109{ 110 delete mixer; 111 delete[] mMixerBuffer; 112} 113 114bool FastMixer::isSubClassCommand(FastThreadState::Command command) 115{ 116 switch ((FastMixerState::Command) command) { 117 case FastMixerState::MIX: 118 case FastMixerState::WRITE: 119 case FastMixerState::MIX_WRITE: 120 return true; 121 default: 122 return false; 123 } 124} 125 126void FastMixer::onStateChange() 127{ 128 const FastMixerState * const current = (const FastMixerState *) this->current; 129 const FastMixerState * const previous = (const FastMixerState *) this->previous; 130 FastMixerDumpState * const dumpState = (FastMixerDumpState *) this->dumpState; 131 const size_t frameCount = current->mFrameCount; 132 133 // handle state change here, but since we want to diff the state, 134 // we're prepared for previous == &initial the first time through 135 unsigned previousTrackMask; 136 137 // check for change in output HAL configuration 138 NBAIO_Format previousFormat = format; 139 if (current->mOutputSinkGen != outputSinkGen) { 140 outputSink = current->mOutputSink; 141 outputSinkGen = current->mOutputSinkGen; 142 if (outputSink == NULL) { 143 format = Format_Invalid; 144 sampleRate = 0; 145 } else { 146 format = outputSink->format(); 147 sampleRate = Format_sampleRate(format); 148 ALOG_ASSERT(Format_channelCount(format) == FCC_2); 149 } 150 dumpState->mSampleRate = sampleRate; 151 } 152 153 if ((!Format_isEqual(format, previousFormat)) || (frameCount != previous->mFrameCount)) { 154 // FIXME to avoid priority inversion, don't delete here 155 delete mixer; 156 mixer = NULL; 157 delete[] mMixerBuffer; 158 mMixerBuffer = NULL; 159 if (frameCount > 0 && sampleRate > 0) { 160 // FIXME new may block for unbounded time at internal mutex of the heap 161 // implementation; it would be better to have normal mixer allocate for us 162 // to avoid blocking here and to prevent possible priority inversion 163 mixer = new AudioMixer(frameCount, sampleRate, FastMixerState::kMaxFastTracks); 164 mMixerBuffer = new short[frameCount * FCC_2]; 165 periodNs = (frameCount * 1000000000LL) / sampleRate; // 1.00 166 underrunNs = (frameCount * 1750000000LL) / sampleRate; // 1.75 167 overrunNs = (frameCount * 500000000LL) / sampleRate; // 0.50 168 forceNs = (frameCount * 950000000LL) / sampleRate; // 0.95 169 warmupNs = (frameCount * 500000000LL) / sampleRate; // 0.50 170 } else { 171 periodNs = 0; 172 underrunNs = 0; 173 overrunNs = 0; 174 forceNs = 0; 175 warmupNs = 0; 176 } 177 mMixerBufferState = UNDEFINED; 178#if !LOG_NDEBUG 179 for (unsigned i = 0; i < FastMixerState::kMaxFastTracks; ++i) { 180 fastTrackNames[i] = -1; 181 } 182#endif 183 // we need to reconfigure all active tracks 184 previousTrackMask = 0; 185 fastTracksGen = current->mFastTracksGen - 1; 186 dumpState->mFrameCount = frameCount; 187 } else { 188 previousTrackMask = previous->mTrackMask; 189 } 190 191 // check for change in active track set 192 const unsigned currentTrackMask = current->mTrackMask; 193 dumpState->mTrackMask = currentTrackMask; 194 if (current->mFastTracksGen != fastTracksGen) { 195 ALOG_ASSERT(mMixerBuffer != NULL); 196 int name; 197 198 // process removed tracks first to avoid running out of track names 199 unsigned removedTracks = previousTrackMask & ~currentTrackMask; 200 while (removedTracks != 0) { 201 int i = __builtin_ctz(removedTracks); 202 removedTracks &= ~(1 << i); 203 const FastTrack* fastTrack = ¤t->mFastTracks[i]; 204 ALOG_ASSERT(fastTrack->mBufferProvider == NULL); 205 if (mixer != NULL) { 206 name = fastTrackNames[i]; 207 ALOG_ASSERT(name >= 0); 208 mixer->deleteTrackName(name); 209 } 210#if !LOG_NDEBUG 211 fastTrackNames[i] = -1; 212#endif 213 // don't reset track dump state, since other side is ignoring it 214 generations[i] = fastTrack->mGeneration; 215 } 216 217 // now process added tracks 218 unsigned addedTracks = currentTrackMask & ~previousTrackMask; 219 while (addedTracks != 0) { 220 int i = __builtin_ctz(addedTracks); 221 addedTracks &= ~(1 << i); 222 const FastTrack* fastTrack = ¤t->mFastTracks[i]; 223 AudioBufferProvider *bufferProvider = fastTrack->mBufferProvider; 224 ALOG_ASSERT(bufferProvider != NULL && fastTrackNames[i] == -1); 225 if (mixer != NULL) { 226 name = mixer->getTrackName(fastTrack->mChannelMask, 227 fastTrack->mFormat, AUDIO_SESSION_OUTPUT_MIX); 228 ALOG_ASSERT(name >= 0); 229 fastTrackNames[i] = name; 230 mixer->setBufferProvider(name, bufferProvider); 231 mixer->setParameter(name, AudioMixer::TRACK, AudioMixer::MAIN_BUFFER, 232 (void *) mMixerBuffer); 233 // newly allocated track names default to full scale volume 234 mixer->setParameter(name, AudioMixer::TRACK, AudioMixer::FORMAT, 235 (void *)(uintptr_t)fastTrack->mFormat); 236 mixer->enable(name); 237 } 238 generations[i] = fastTrack->mGeneration; 239 } 240 241 // finally process (potentially) modified tracks; these use the same slot 242 // but may have a different buffer provider or volume provider 243 unsigned modifiedTracks = currentTrackMask & previousTrackMask; 244 while (modifiedTracks != 0) { 245 int i = __builtin_ctz(modifiedTracks); 246 modifiedTracks &= ~(1 << i); 247 const FastTrack* fastTrack = ¤t->mFastTracks[i]; 248 if (fastTrack->mGeneration != generations[i]) { 249 // this track was actually modified 250 AudioBufferProvider *bufferProvider = fastTrack->mBufferProvider; 251 ALOG_ASSERT(bufferProvider != NULL); 252 if (mixer != NULL) { 253 name = fastTrackNames[i]; 254 ALOG_ASSERT(name >= 0); 255 mixer->setBufferProvider(name, bufferProvider); 256 if (fastTrack->mVolumeProvider == NULL) { 257 mixer->setParameter(name, AudioMixer::VOLUME, AudioMixer::VOLUME0, 258 (void *) MAX_GAIN_INT); 259 mixer->setParameter(name, AudioMixer::VOLUME, AudioMixer::VOLUME1, 260 (void *) MAX_GAIN_INT); 261 } 262 mixer->setParameter(name, AudioMixer::RESAMPLE, 263 AudioMixer::REMOVE, NULL); 264 mixer->setParameter(name, AudioMixer::TRACK, AudioMixer::FORMAT, 265 (void *)(uintptr_t)fastTrack->mFormat); 266 mixer->setParameter(name, AudioMixer::TRACK, AudioMixer::CHANNEL_MASK, 267 (void *)(uintptr_t) fastTrack->mChannelMask); 268 // already enabled 269 } 270 generations[i] = fastTrack->mGeneration; 271 } 272 } 273 274 fastTracksGen = current->mFastTracksGen; 275 276 dumpState->mNumTracks = popcount(currentTrackMask); 277 } 278} 279 280void FastMixer::onWork() 281{ 282 const FastMixerState * const current = (const FastMixerState *) this->current; 283 FastMixerDumpState * const dumpState = (FastMixerDumpState *) this->dumpState; 284 const FastMixerState::Command command = this->command; 285 const size_t frameCount = current->mFrameCount; 286 287 if ((command & FastMixerState::MIX) && (mixer != NULL) && isWarm) { 288 ALOG_ASSERT(mMixerBuffer != NULL); 289 // for each track, update volume and check for underrun 290 unsigned currentTrackMask = current->mTrackMask; 291 while (currentTrackMask != 0) { 292 int i = __builtin_ctz(currentTrackMask); 293 currentTrackMask &= ~(1 << i); 294 const FastTrack* fastTrack = ¤t->mFastTracks[i]; 295 296 // Refresh the per-track timestamp 297 if (timestampStatus == NO_ERROR) { 298 uint32_t trackFramesWrittenButNotPresented = 299 nativeFramesWrittenButNotPresented; 300 uint32_t trackFramesWritten = fastTrack->mBufferProvider->framesReleased(); 301 // Can't provide an AudioTimestamp before first frame presented, 302 // or during the brief 32-bit wraparound window 303 if (trackFramesWritten >= trackFramesWrittenButNotPresented) { 304 AudioTimestamp perTrackTimestamp; 305 perTrackTimestamp.mPosition = 306 trackFramesWritten - trackFramesWrittenButNotPresented; 307 perTrackTimestamp.mTime = timestamp.mTime; 308 fastTrack->mBufferProvider->onTimestamp(perTrackTimestamp); 309 } 310 } 311 312 int name = fastTrackNames[i]; 313 ALOG_ASSERT(name >= 0); 314 if (fastTrack->mVolumeProvider != NULL) { 315 gain_minifloat_packed_t vlr = fastTrack->mVolumeProvider->getVolumeLR(); 316 mixer->setParameter(name, AudioMixer::VOLUME, AudioMixer::VOLUME0, 317 (void *) (uintptr_t) 318 (float_from_gain(gain_minifloat_unpack_left(vlr)) * MAX_GAIN_INT)); 319 mixer->setParameter(name, AudioMixer::VOLUME, AudioMixer::VOLUME1, 320 (void *) (uintptr_t) 321 (float_from_gain(gain_minifloat_unpack_right(vlr)) * MAX_GAIN_INT)); 322 } 323 // FIXME The current implementation of framesReady() for fast tracks 324 // takes a tryLock, which can block 325 // up to 1 ms. If enough active tracks all blocked in sequence, this would result 326 // in the overall fast mix cycle being delayed. Should use a non-blocking FIFO. 327 size_t framesReady = fastTrack->mBufferProvider->framesReady(); 328 if (ATRACE_ENABLED()) { 329 // I wish we had formatted trace names 330 char traceName[16]; 331 strcpy(traceName, "fRdy"); 332 traceName[4] = i + (i < 10 ? '0' : 'A' - 10); 333 traceName[5] = '\0'; 334 ATRACE_INT(traceName, framesReady); 335 } 336 FastTrackDump *ftDump = &dumpState->mTracks[i]; 337 FastTrackUnderruns underruns = ftDump->mUnderruns; 338 if (framesReady < frameCount) { 339 if (framesReady == 0) { 340 underruns.mBitFields.mEmpty++; 341 underruns.mBitFields.mMostRecent = UNDERRUN_EMPTY; 342 mixer->disable(name); 343 } else { 344 // allow mixing partial buffer 345 underruns.mBitFields.mPartial++; 346 underruns.mBitFields.mMostRecent = UNDERRUN_PARTIAL; 347 mixer->enable(name); 348 } 349 } else { 350 underruns.mBitFields.mFull++; 351 underruns.mBitFields.mMostRecent = UNDERRUN_FULL; 352 mixer->enable(name); 353 } 354 ftDump->mUnderruns = underruns; 355 ftDump->mFramesReady = framesReady; 356 } 357 358 int64_t pts; 359 if (outputSink == NULL || (OK != outputSink->getNextWriteTimestamp(&pts))) { 360 pts = AudioBufferProvider::kInvalidPTS; 361 } 362 363 // process() is CPU-bound 364 mixer->process(pts); 365 mMixerBufferState = MIXED; 366 } else if (mMixerBufferState == MIXED) { 367 mMixerBufferState = UNDEFINED; 368 } 369 //bool didFullWrite = false; // dumpsys could display a count of partial writes 370 if ((command & FastMixerState::WRITE) && (outputSink != NULL) && (mMixerBuffer != NULL)) { 371 if (mMixerBufferState == UNDEFINED) { 372 memset(mMixerBuffer, 0, frameCount * FCC_2 * sizeof(short)); 373 mMixerBufferState = ZEROED; 374 } 375 // if non-NULL, then duplicate write() to this non-blocking sink 376 NBAIO_Sink* teeSink; 377 if ((teeSink = current->mTeeSink) != NULL) { 378 (void) teeSink->write(mMixerBuffer, frameCount); 379 } 380 // FIXME write() is non-blocking and lock-free for a properly implemented NBAIO sink, 381 // but this code should be modified to handle both non-blocking and blocking sinks 382 dumpState->mWriteSequence++; 383 ATRACE_BEGIN("write"); 384 ssize_t framesWritten = outputSink->write(mMixerBuffer, frameCount); 385 ATRACE_END(); 386 dumpState->mWriteSequence++; 387 if (framesWritten >= 0) { 388 ALOG_ASSERT((size_t) framesWritten <= frameCount); 389 totalNativeFramesWritten += framesWritten; 390 dumpState->mFramesWritten = totalNativeFramesWritten; 391 //if ((size_t) framesWritten == frameCount) { 392 // didFullWrite = true; 393 //} 394 } else { 395 dumpState->mWriteErrors++; 396 } 397 attemptedWrite = true; 398 // FIXME count # of writes blocked excessively, CPU usage, etc. for dump 399 400 timestampStatus = outputSink->getTimestamp(timestamp); 401 if (timestampStatus == NO_ERROR) { 402 uint32_t totalNativeFramesPresented = timestamp.mPosition; 403 if (totalNativeFramesPresented <= totalNativeFramesWritten) { 404 nativeFramesWrittenButNotPresented = 405 totalNativeFramesWritten - totalNativeFramesPresented; 406 } else { 407 // HAL reported that more frames were presented than were written 408 timestampStatus = INVALID_OPERATION; 409 } 410 } 411 } 412} 413 414FastMixerDumpState::FastMixerDumpState( 415#ifdef FAST_MIXER_STATISTICS 416 uint32_t samplingN 417#endif 418 ) : FastThreadDumpState(), 419 mWriteSequence(0), mFramesWritten(0), 420 mNumTracks(0), mWriteErrors(0), 421 mSampleRate(0), mFrameCount(0), 422 mTrackMask(0) 423{ 424#ifdef FAST_MIXER_STATISTICS 425 increaseSamplingN(samplingN); 426#endif 427} 428 429#ifdef FAST_MIXER_STATISTICS 430void FastMixerDumpState::increaseSamplingN(uint32_t samplingN) 431{ 432 if (samplingN <= mSamplingN || samplingN > kSamplingN || roundup(samplingN) != samplingN) { 433 return; 434 } 435 uint32_t additional = samplingN - mSamplingN; 436 // sample arrays aren't accessed atomically with respect to the bounds, 437 // so clearing reduces chance for dumpsys to read random uninitialized samples 438 memset(&mMonotonicNs[mSamplingN], 0, sizeof(mMonotonicNs[0]) * additional); 439 memset(&mLoadNs[mSamplingN], 0, sizeof(mLoadNs[0]) * additional); 440#ifdef CPU_FREQUENCY_STATISTICS 441 memset(&mCpukHz[mSamplingN], 0, sizeof(mCpukHz[0]) * additional); 442#endif 443 mSamplingN = samplingN; 444} 445#endif 446 447FastMixerDumpState::~FastMixerDumpState() 448{ 449} 450 451// helper function called by qsort() 452static int compare_uint32_t(const void *pa, const void *pb) 453{ 454 uint32_t a = *(const uint32_t *)pa; 455 uint32_t b = *(const uint32_t *)pb; 456 if (a < b) { 457 return -1; 458 } else if (a > b) { 459 return 1; 460 } else { 461 return 0; 462 } 463} 464 465void FastMixerDumpState::dump(int fd) const 466{ 467 if (mCommand == FastMixerState::INITIAL) { 468 dprintf(fd, " FastMixer not initialized\n"); 469 return; 470 } 471#define COMMAND_MAX 32 472 char string[COMMAND_MAX]; 473 switch (mCommand) { 474 case FastMixerState::INITIAL: 475 strcpy(string, "INITIAL"); 476 break; 477 case FastMixerState::HOT_IDLE: 478 strcpy(string, "HOT_IDLE"); 479 break; 480 case FastMixerState::COLD_IDLE: 481 strcpy(string, "COLD_IDLE"); 482 break; 483 case FastMixerState::EXIT: 484 strcpy(string, "EXIT"); 485 break; 486 case FastMixerState::MIX: 487 strcpy(string, "MIX"); 488 break; 489 case FastMixerState::WRITE: 490 strcpy(string, "WRITE"); 491 break; 492 case FastMixerState::MIX_WRITE: 493 strcpy(string, "MIX_WRITE"); 494 break; 495 default: 496 snprintf(string, COMMAND_MAX, "%d", mCommand); 497 break; 498 } 499 double measuredWarmupMs = (mMeasuredWarmupTs.tv_sec * 1000.0) + 500 (mMeasuredWarmupTs.tv_nsec / 1000000.0); 501 double mixPeriodSec = (double) mFrameCount / (double) mSampleRate; 502 dprintf(fd, " FastMixer command=%s writeSequence=%u framesWritten=%u\n" 503 " numTracks=%u writeErrors=%u underruns=%u overruns=%u\n" 504 " sampleRate=%u frameCount=%zu measuredWarmup=%.3g ms, warmupCycles=%u\n" 505 " mixPeriod=%.2f ms\n", 506 string, mWriteSequence, mFramesWritten, 507 mNumTracks, mWriteErrors, mUnderruns, mOverruns, 508 mSampleRate, mFrameCount, measuredWarmupMs, mWarmupCycles, 509 mixPeriodSec * 1e3); 510#ifdef FAST_MIXER_STATISTICS 511 // find the interval of valid samples 512 uint32_t bounds = mBounds; 513 uint32_t newestOpen = bounds & 0xFFFF; 514 uint32_t oldestClosed = bounds >> 16; 515 uint32_t n = (newestOpen - oldestClosed) & 0xFFFF; 516 if (n > mSamplingN) { 517 ALOGE("too many samples %u", n); 518 n = mSamplingN; 519 } 520 // statistics for monotonic (wall clock) time, thread raw CPU load in time, CPU clock frequency, 521 // and adjusted CPU load in MHz normalized for CPU clock frequency 522 CentralTendencyStatistics wall, loadNs; 523#ifdef CPU_FREQUENCY_STATISTICS 524 CentralTendencyStatistics kHz, loadMHz; 525 uint32_t previousCpukHz = 0; 526#endif 527 // Assuming a normal distribution for cycle times, three standard deviations on either side of 528 // the mean account for 99.73% of the population. So if we take each tail to be 1/1000 of the 529 // sample set, we get 99.8% combined, or close to three standard deviations. 530 static const uint32_t kTailDenominator = 1000; 531 uint32_t *tail = n >= kTailDenominator ? new uint32_t[n] : NULL; 532 // loop over all the samples 533 for (uint32_t j = 0; j < n; ++j) { 534 size_t i = oldestClosed++ & (mSamplingN - 1); 535 uint32_t wallNs = mMonotonicNs[i]; 536 if (tail != NULL) { 537 tail[j] = wallNs; 538 } 539 wall.sample(wallNs); 540 uint32_t sampleLoadNs = mLoadNs[i]; 541 loadNs.sample(sampleLoadNs); 542#ifdef CPU_FREQUENCY_STATISTICS 543 uint32_t sampleCpukHz = mCpukHz[i]; 544 // skip bad kHz samples 545 if ((sampleCpukHz & ~0xF) != 0) { 546 kHz.sample(sampleCpukHz >> 4); 547 if (sampleCpukHz == previousCpukHz) { 548 double megacycles = (double) sampleLoadNs * (double) (sampleCpukHz >> 4) * 1e-12; 549 double adjMHz = megacycles / mixPeriodSec; // _not_ wallNs * 1e9 550 loadMHz.sample(adjMHz); 551 } 552 } 553 previousCpukHz = sampleCpukHz; 554#endif 555 } 556 if (n) { 557 dprintf(fd, " Simple moving statistics over last %.1f seconds:\n", 558 wall.n() * mixPeriodSec); 559 dprintf(fd, " wall clock time in ms per mix cycle:\n" 560 " mean=%.2f min=%.2f max=%.2f stddev=%.2f\n", 561 wall.mean()*1e-6, wall.minimum()*1e-6, wall.maximum()*1e-6, 562 wall.stddev()*1e-6); 563 dprintf(fd, " raw CPU load in us per mix cycle:\n" 564 " mean=%.0f min=%.0f max=%.0f stddev=%.0f\n", 565 loadNs.mean()*1e-3, loadNs.minimum()*1e-3, loadNs.maximum()*1e-3, 566 loadNs.stddev()*1e-3); 567 } else { 568 dprintf(fd, " No FastMixer statistics available currently\n"); 569 } 570#ifdef CPU_FREQUENCY_STATISTICS 571 dprintf(fd, " CPU clock frequency in MHz:\n" 572 " mean=%.0f min=%.0f max=%.0f stddev=%.0f\n", 573 kHz.mean()*1e-3, kHz.minimum()*1e-3, kHz.maximum()*1e-3, kHz.stddev()*1e-3); 574 dprintf(fd, " adjusted CPU load in MHz (i.e. normalized for CPU clock frequency):\n" 575 " mean=%.1f min=%.1f max=%.1f stddev=%.1f\n", 576 loadMHz.mean(), loadMHz.minimum(), loadMHz.maximum(), loadMHz.stddev()); 577#endif 578 if (tail != NULL) { 579 qsort(tail, n, sizeof(uint32_t), compare_uint32_t); 580 // assume same number of tail samples on each side, left and right 581 uint32_t count = n / kTailDenominator; 582 CentralTendencyStatistics left, right; 583 for (uint32_t i = 0; i < count; ++i) { 584 left.sample(tail[i]); 585 right.sample(tail[n - (i + 1)]); 586 } 587 dprintf(fd, " Distribution of mix cycle times in ms for the tails (> ~3 stddev outliers):\n" 588 " left tail: mean=%.2f min=%.2f max=%.2f stddev=%.2f\n" 589 " right tail: mean=%.2f min=%.2f max=%.2f stddev=%.2f\n", 590 left.mean()*1e-6, left.minimum()*1e-6, left.maximum()*1e-6, left.stddev()*1e-6, 591 right.mean()*1e-6, right.minimum()*1e-6, right.maximum()*1e-6, 592 right.stddev()*1e-6); 593 delete[] tail; 594 } 595#endif 596 // The active track mask and track states are updated non-atomically. 597 // So if we relied on isActive to decide whether to display, 598 // then we might display an obsolete track or omit an active track. 599 // Instead we always display all tracks, with an indication 600 // of whether we think the track is active. 601 uint32_t trackMask = mTrackMask; 602 dprintf(fd, " Fast tracks: kMaxFastTracks=%u activeMask=%#x\n", 603 FastMixerState::kMaxFastTracks, trackMask); 604 dprintf(fd, " Index Active Full Partial Empty Recent Ready\n"); 605 for (uint32_t i = 0; i < FastMixerState::kMaxFastTracks; ++i, trackMask >>= 1) { 606 bool isActive = trackMask & 1; 607 const FastTrackDump *ftDump = &mTracks[i]; 608 const FastTrackUnderruns& underruns = ftDump->mUnderruns; 609 const char *mostRecent; 610 switch (underruns.mBitFields.mMostRecent) { 611 case UNDERRUN_FULL: 612 mostRecent = "full"; 613 break; 614 case UNDERRUN_PARTIAL: 615 mostRecent = "partial"; 616 break; 617 case UNDERRUN_EMPTY: 618 mostRecent = "empty"; 619 break; 620 default: 621 mostRecent = "?"; 622 break; 623 } 624 dprintf(fd, " %5u %6s %4u %7u %5u %7s %5zu\n", i, isActive ? "yes" : "no", 625 (underruns.mBitFields.mFull) & UNDERRUN_MASK, 626 (underruns.mBitFields.mPartial) & UNDERRUN_MASK, 627 (underruns.mBitFields.mEmpty) & UNDERRUN_MASK, 628 mostRecent, ftDump->mFramesReady); 629 } 630} 631 632} // namespace android 633