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