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