1 /* 2 * Copyright (C) 2015 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 #define LOG_TAG "BufferProvider" 18 //#define LOG_NDEBUG 0 19 20 #include <audio_effects/effect_downmix.h> 21 #include <audio_utils/primitives.h> 22 #include <audio_utils/format.h> 23 #include <media/AudioResamplerPublic.h> 24 #include <media/EffectsFactoryApi.h> 25 26 #include <utils/Log.h> 27 28 #include "Configuration.h" 29 #include "BufferProviders.h" 30 31 #ifndef ARRAY_SIZE 32 #define ARRAY_SIZE(x) (sizeof(x)/sizeof((x)[0])) 33 #endif 34 35 namespace android { 36 37 // ---------------------------------------------------------------------------- 38 39 template <typename T> 40 static inline T min(const T& a, const T& b) 41 { 42 return a < b ? a : b; 43 } 44 45 CopyBufferProvider::CopyBufferProvider(size_t inputFrameSize, 46 size_t outputFrameSize, size_t bufferFrameCount) : 47 mInputFrameSize(inputFrameSize), 48 mOutputFrameSize(outputFrameSize), 49 mLocalBufferFrameCount(bufferFrameCount), 50 mLocalBufferData(NULL), 51 mConsumed(0) 52 { 53 ALOGV("CopyBufferProvider(%p)(%zu, %zu, %zu)", this, 54 inputFrameSize, outputFrameSize, bufferFrameCount); 55 LOG_ALWAYS_FATAL_IF(inputFrameSize < outputFrameSize && bufferFrameCount == 0, 56 "Requires local buffer if inputFrameSize(%zu) < outputFrameSize(%zu)", 57 inputFrameSize, outputFrameSize); 58 if (mLocalBufferFrameCount) { 59 (void)posix_memalign(&mLocalBufferData, 32, mLocalBufferFrameCount * mOutputFrameSize); 60 } 61 mBuffer.frameCount = 0; 62 } 63 64 CopyBufferProvider::~CopyBufferProvider() 65 { 66 ALOGV("~CopyBufferProvider(%p)", this); 67 if (mBuffer.frameCount != 0) { 68 mTrackBufferProvider->releaseBuffer(&mBuffer); 69 } 70 free(mLocalBufferData); 71 } 72 73 status_t CopyBufferProvider::getNextBuffer(AudioBufferProvider::Buffer *pBuffer) 74 { 75 //ALOGV("CopyBufferProvider(%p)::getNextBuffer(%p (%zu))", 76 // this, pBuffer, pBuffer->frameCount); 77 if (mLocalBufferFrameCount == 0) { 78 status_t res = mTrackBufferProvider->getNextBuffer(pBuffer); 79 if (res == OK) { 80 copyFrames(pBuffer->raw, pBuffer->raw, pBuffer->frameCount); 81 } 82 return res; 83 } 84 if (mBuffer.frameCount == 0) { 85 mBuffer.frameCount = pBuffer->frameCount; 86 status_t res = mTrackBufferProvider->getNextBuffer(&mBuffer); 87 // At one time an upstream buffer provider had 88 // res == OK and mBuffer.frameCount == 0, doesn't seem to happen now 7/18/2014. 89 // 90 // By API spec, if res != OK, then mBuffer.frameCount == 0. 91 // but there may be improper implementations. 92 ALOG_ASSERT(res == OK || mBuffer.frameCount == 0); 93 if (res != OK || mBuffer.frameCount == 0) { // not needed by API spec, but to be safe. 94 pBuffer->raw = NULL; 95 pBuffer->frameCount = 0; 96 return res; 97 } 98 mConsumed = 0; 99 } 100 ALOG_ASSERT(mConsumed < mBuffer.frameCount); 101 size_t count = min(mLocalBufferFrameCount, mBuffer.frameCount - mConsumed); 102 count = min(count, pBuffer->frameCount); 103 pBuffer->raw = mLocalBufferData; 104 pBuffer->frameCount = count; 105 copyFrames(pBuffer->raw, (uint8_t*)mBuffer.raw + mConsumed * mInputFrameSize, 106 pBuffer->frameCount); 107 return OK; 108 } 109 110 void CopyBufferProvider::releaseBuffer(AudioBufferProvider::Buffer *pBuffer) 111 { 112 //ALOGV("CopyBufferProvider(%p)::releaseBuffer(%p(%zu))", 113 // this, pBuffer, pBuffer->frameCount); 114 if (mLocalBufferFrameCount == 0) { 115 mTrackBufferProvider->releaseBuffer(pBuffer); 116 return; 117 } 118 // LOG_ALWAYS_FATAL_IF(pBuffer->frameCount == 0, "Invalid framecount"); 119 mConsumed += pBuffer->frameCount; // TODO: update for efficiency to reuse existing content 120 if (mConsumed != 0 && mConsumed >= mBuffer.frameCount) { 121 mTrackBufferProvider->releaseBuffer(&mBuffer); 122 ALOG_ASSERT(mBuffer.frameCount == 0); 123 } 124 pBuffer->raw = NULL; 125 pBuffer->frameCount = 0; 126 } 127 128 void CopyBufferProvider::reset() 129 { 130 if (mBuffer.frameCount != 0) { 131 mTrackBufferProvider->releaseBuffer(&mBuffer); 132 } 133 mConsumed = 0; 134 } 135 136 DownmixerBufferProvider::DownmixerBufferProvider( 137 audio_channel_mask_t inputChannelMask, 138 audio_channel_mask_t outputChannelMask, audio_format_t format, 139 uint32_t sampleRate, int32_t sessionId, size_t bufferFrameCount) : 140 CopyBufferProvider( 141 audio_bytes_per_sample(format) * audio_channel_count_from_out_mask(inputChannelMask), 142 audio_bytes_per_sample(format) * audio_channel_count_from_out_mask(outputChannelMask), 143 bufferFrameCount) // set bufferFrameCount to 0 to do in-place 144 { 145 ALOGV("DownmixerBufferProvider(%p)(%#x, %#x, %#x %u %d)", 146 this, inputChannelMask, outputChannelMask, format, 147 sampleRate, sessionId); 148 if (!sIsMultichannelCapable 149 || EffectCreate(&sDwnmFxDesc.uuid, 150 sessionId, 151 SESSION_ID_INVALID_AND_IGNORED, 152 &mDownmixHandle) != 0) { 153 ALOGE("DownmixerBufferProvider() error creating downmixer effect"); 154 mDownmixHandle = NULL; 155 return; 156 } 157 // channel input configuration will be overridden per-track 158 mDownmixConfig.inputCfg.channels = inputChannelMask; // FIXME: Should be bits 159 mDownmixConfig.outputCfg.channels = outputChannelMask; // FIXME: should be bits 160 mDownmixConfig.inputCfg.format = format; 161 mDownmixConfig.outputCfg.format = format; 162 mDownmixConfig.inputCfg.samplingRate = sampleRate; 163 mDownmixConfig.outputCfg.samplingRate = sampleRate; 164 mDownmixConfig.inputCfg.accessMode = EFFECT_BUFFER_ACCESS_READ; 165 mDownmixConfig.outputCfg.accessMode = EFFECT_BUFFER_ACCESS_WRITE; 166 // input and output buffer provider, and frame count will not be used as the downmix effect 167 // process() function is called directly (see DownmixerBufferProvider::getNextBuffer()) 168 mDownmixConfig.inputCfg.mask = EFFECT_CONFIG_SMP_RATE | EFFECT_CONFIG_CHANNELS | 169 EFFECT_CONFIG_FORMAT | EFFECT_CONFIG_ACC_MODE; 170 mDownmixConfig.outputCfg.mask = mDownmixConfig.inputCfg.mask; 171 172 int cmdStatus; 173 uint32_t replySize = sizeof(int); 174 175 // Configure downmixer 176 status_t status = (*mDownmixHandle)->command(mDownmixHandle, 177 EFFECT_CMD_SET_CONFIG /*cmdCode*/, sizeof(effect_config_t) /*cmdSize*/, 178 &mDownmixConfig /*pCmdData*/, 179 &replySize, &cmdStatus /*pReplyData*/); 180 if (status != 0 || cmdStatus != 0) { 181 ALOGE("DownmixerBufferProvider() error %d cmdStatus %d while configuring downmixer", 182 status, cmdStatus); 183 EffectRelease(mDownmixHandle); 184 mDownmixHandle = NULL; 185 return; 186 } 187 188 // Enable downmixer 189 replySize = sizeof(int); 190 status = (*mDownmixHandle)->command(mDownmixHandle, 191 EFFECT_CMD_ENABLE /*cmdCode*/, 0 /*cmdSize*/, NULL /*pCmdData*/, 192 &replySize, &cmdStatus /*pReplyData*/); 193 if (status != 0 || cmdStatus != 0) { 194 ALOGE("DownmixerBufferProvider() error %d cmdStatus %d while enabling downmixer", 195 status, cmdStatus); 196 EffectRelease(mDownmixHandle); 197 mDownmixHandle = NULL; 198 return; 199 } 200 201 // Set downmix type 202 // parameter size rounded for padding on 32bit boundary 203 const int psizePadded = ((sizeof(downmix_params_t) - 1)/sizeof(int) + 1) * sizeof(int); 204 const int downmixParamSize = 205 sizeof(effect_param_t) + psizePadded + sizeof(downmix_type_t); 206 effect_param_t * const param = (effect_param_t *) malloc(downmixParamSize); 207 param->psize = sizeof(downmix_params_t); 208 const downmix_params_t downmixParam = DOWNMIX_PARAM_TYPE; 209 memcpy(param->data, &downmixParam, param->psize); 210 const downmix_type_t downmixType = DOWNMIX_TYPE_FOLD; 211 param->vsize = sizeof(downmix_type_t); 212 memcpy(param->data + psizePadded, &downmixType, param->vsize); 213 replySize = sizeof(int); 214 status = (*mDownmixHandle)->command(mDownmixHandle, 215 EFFECT_CMD_SET_PARAM /* cmdCode */, downmixParamSize /* cmdSize */, 216 param /*pCmdData*/, &replySize, &cmdStatus /*pReplyData*/); 217 free(param); 218 if (status != 0 || cmdStatus != 0) { 219 ALOGE("DownmixerBufferProvider() error %d cmdStatus %d while setting downmix type", 220 status, cmdStatus); 221 EffectRelease(mDownmixHandle); 222 mDownmixHandle = NULL; 223 return; 224 } 225 ALOGV("DownmixerBufferProvider() downmix type set to %d", (int) downmixType); 226 } 227 228 DownmixerBufferProvider::~DownmixerBufferProvider() 229 { 230 ALOGV("~DownmixerBufferProvider (%p)", this); 231 EffectRelease(mDownmixHandle); 232 mDownmixHandle = NULL; 233 } 234 235 void DownmixerBufferProvider::copyFrames(void *dst, const void *src, size_t frames) 236 { 237 mDownmixConfig.inputCfg.buffer.frameCount = frames; 238 mDownmixConfig.inputCfg.buffer.raw = const_cast<void *>(src); 239 mDownmixConfig.outputCfg.buffer.frameCount = frames; 240 mDownmixConfig.outputCfg.buffer.raw = dst; 241 // may be in-place if src == dst. 242 status_t res = (*mDownmixHandle)->process(mDownmixHandle, 243 &mDownmixConfig.inputCfg.buffer, &mDownmixConfig.outputCfg.buffer); 244 ALOGE_IF(res != OK, "DownmixBufferProvider error %d", res); 245 } 246 247 /* call once in a pthread_once handler. */ 248 /*static*/ status_t DownmixerBufferProvider::init() 249 { 250 // find multichannel downmix effect if we have to play multichannel content 251 uint32_t numEffects = 0; 252 int ret = EffectQueryNumberEffects(&numEffects); 253 if (ret != 0) { 254 ALOGE("AudioMixer() error %d querying number of effects", ret); 255 return NO_INIT; 256 } 257 ALOGV("EffectQueryNumberEffects() numEffects=%d", numEffects); 258 259 for (uint32_t i = 0 ; i < numEffects ; i++) { 260 if (EffectQueryEffect(i, &sDwnmFxDesc) == 0) { 261 ALOGV("effect %d is called %s", i, sDwnmFxDesc.name); 262 if (memcmp(&sDwnmFxDesc.type, EFFECT_UIID_DOWNMIX, sizeof(effect_uuid_t)) == 0) { 263 ALOGI("found effect \"%s\" from %s", 264 sDwnmFxDesc.name, sDwnmFxDesc.implementor); 265 sIsMultichannelCapable = true; 266 break; 267 } 268 } 269 } 270 ALOGW_IF(!sIsMultichannelCapable, "unable to find downmix effect"); 271 return NO_INIT; 272 } 273 274 /*static*/ bool DownmixerBufferProvider::sIsMultichannelCapable = false; 275 /*static*/ effect_descriptor_t DownmixerBufferProvider::sDwnmFxDesc; 276 277 RemixBufferProvider::RemixBufferProvider(audio_channel_mask_t inputChannelMask, 278 audio_channel_mask_t outputChannelMask, audio_format_t format, 279 size_t bufferFrameCount) : 280 CopyBufferProvider( 281 audio_bytes_per_sample(format) 282 * audio_channel_count_from_out_mask(inputChannelMask), 283 audio_bytes_per_sample(format) 284 * audio_channel_count_from_out_mask(outputChannelMask), 285 bufferFrameCount), 286 mFormat(format), 287 mSampleSize(audio_bytes_per_sample(format)), 288 mInputChannels(audio_channel_count_from_out_mask(inputChannelMask)), 289 mOutputChannels(audio_channel_count_from_out_mask(outputChannelMask)) 290 { 291 ALOGV("RemixBufferProvider(%p)(%#x, %#x, %#x) %zu %zu", 292 this, format, inputChannelMask, outputChannelMask, 293 mInputChannels, mOutputChannels); 294 (void) memcpy_by_index_array_initialization_from_channel_mask( 295 mIdxAry, ARRAY_SIZE(mIdxAry), outputChannelMask, inputChannelMask); 296 } 297 298 void RemixBufferProvider::copyFrames(void *dst, const void *src, size_t frames) 299 { 300 memcpy_by_index_array(dst, mOutputChannels, 301 src, mInputChannels, mIdxAry, mSampleSize, frames); 302 } 303 304 ReformatBufferProvider::ReformatBufferProvider(int32_t channelCount, 305 audio_format_t inputFormat, audio_format_t outputFormat, 306 size_t bufferFrameCount) : 307 CopyBufferProvider( 308 channelCount * audio_bytes_per_sample(inputFormat), 309 channelCount * audio_bytes_per_sample(outputFormat), 310 bufferFrameCount), 311 mChannelCount(channelCount), 312 mInputFormat(inputFormat), 313 mOutputFormat(outputFormat) 314 { 315 ALOGV("ReformatBufferProvider(%p)(%u, %#x, %#x)", 316 this, channelCount, inputFormat, outputFormat); 317 } 318 319 void ReformatBufferProvider::copyFrames(void *dst, const void *src, size_t frames) 320 { 321 memcpy_by_audio_format(dst, mOutputFormat, src, mInputFormat, frames * mChannelCount); 322 } 323 324 TimestretchBufferProvider::TimestretchBufferProvider(int32_t channelCount, 325 audio_format_t format, uint32_t sampleRate, const AudioPlaybackRate &playbackRate) : 326 mChannelCount(channelCount), 327 mFormat(format), 328 mSampleRate(sampleRate), 329 mFrameSize(channelCount * audio_bytes_per_sample(format)), 330 mLocalBufferFrameCount(0), 331 mLocalBufferData(NULL), 332 mRemaining(0), 333 mSonicStream(sonicCreateStream(sampleRate, mChannelCount)), 334 mFallbackFailErrorShown(false), 335 mAudioPlaybackRateValid(false) 336 { 337 LOG_ALWAYS_FATAL_IF(mSonicStream == NULL, 338 "TimestretchBufferProvider can't allocate Sonic stream"); 339 340 setPlaybackRate(playbackRate); 341 ALOGV("TimestretchBufferProvider(%p)(%u, %#x, %u %f %f %d %d)", 342 this, channelCount, format, sampleRate, playbackRate.mSpeed, 343 playbackRate.mPitch, playbackRate.mStretchMode, playbackRate.mFallbackMode); 344 mBuffer.frameCount = 0; 345 } 346 347 TimestretchBufferProvider::~TimestretchBufferProvider() 348 { 349 ALOGV("~TimestretchBufferProvider(%p)", this); 350 sonicDestroyStream(mSonicStream); 351 if (mBuffer.frameCount != 0) { 352 mTrackBufferProvider->releaseBuffer(&mBuffer); 353 } 354 free(mLocalBufferData); 355 } 356 357 status_t TimestretchBufferProvider::getNextBuffer( 358 AudioBufferProvider::Buffer *pBuffer) 359 { 360 ALOGV("TimestretchBufferProvider(%p)::getNextBuffer(%p (%zu))", 361 this, pBuffer, pBuffer->frameCount); 362 363 // BYPASS 364 //return mTrackBufferProvider->getNextBuffer(pBuffer); 365 366 // check if previously processed data is sufficient. 367 if (pBuffer->frameCount <= mRemaining) { 368 ALOGV("previous sufficient"); 369 pBuffer->raw = mLocalBufferData; 370 return OK; 371 } 372 373 // do we need to resize our buffer? 374 if (pBuffer->frameCount > mLocalBufferFrameCount) { 375 void *newmem; 376 if (posix_memalign(&newmem, 32, pBuffer->frameCount * mFrameSize) == OK) { 377 if (mRemaining != 0) { 378 memcpy(newmem, mLocalBufferData, mRemaining * mFrameSize); 379 } 380 free(mLocalBufferData); 381 mLocalBufferData = newmem; 382 mLocalBufferFrameCount = pBuffer->frameCount; 383 } 384 } 385 386 // need to fetch more data 387 const size_t outputDesired = pBuffer->frameCount - mRemaining; 388 size_t dstAvailable; 389 do { 390 mBuffer.frameCount = mPlaybackRate.mSpeed == AUDIO_TIMESTRETCH_SPEED_NORMAL 391 ? outputDesired : outputDesired * mPlaybackRate.mSpeed + 1; 392 393 status_t res = mTrackBufferProvider->getNextBuffer(&mBuffer); 394 395 ALOG_ASSERT(res == OK || mBuffer.frameCount == 0); 396 if (res != OK || mBuffer.frameCount == 0) { // not needed by API spec, but to be safe. 397 ALOGV("upstream provider cannot provide data"); 398 if (mRemaining == 0) { 399 pBuffer->raw = NULL; 400 pBuffer->frameCount = 0; 401 return res; 402 } else { // return partial count 403 pBuffer->raw = mLocalBufferData; 404 pBuffer->frameCount = mRemaining; 405 return OK; 406 } 407 } 408 409 // time-stretch the data 410 dstAvailable = min(mLocalBufferFrameCount - mRemaining, outputDesired); 411 size_t srcAvailable = mBuffer.frameCount; 412 processFrames((uint8_t*)mLocalBufferData + mRemaining * mFrameSize, &dstAvailable, 413 mBuffer.raw, &srcAvailable); 414 415 // release all data consumed 416 mBuffer.frameCount = srcAvailable; 417 mTrackBufferProvider->releaseBuffer(&mBuffer); 418 } while (dstAvailable == 0); // try until we get output data or upstream provider fails. 419 420 // update buffer vars with the actual data processed and return with buffer 421 mRemaining += dstAvailable; 422 423 pBuffer->raw = mLocalBufferData; 424 pBuffer->frameCount = mRemaining; 425 426 return OK; 427 } 428 429 void TimestretchBufferProvider::releaseBuffer(AudioBufferProvider::Buffer *pBuffer) 430 { 431 ALOGV("TimestretchBufferProvider(%p)::releaseBuffer(%p (%zu))", 432 this, pBuffer, pBuffer->frameCount); 433 434 // BYPASS 435 //return mTrackBufferProvider->releaseBuffer(pBuffer); 436 437 // LOG_ALWAYS_FATAL_IF(pBuffer->frameCount == 0, "Invalid framecount"); 438 if (pBuffer->frameCount < mRemaining) { 439 memcpy(mLocalBufferData, 440 (uint8_t*)mLocalBufferData + pBuffer->frameCount * mFrameSize, 441 (mRemaining - pBuffer->frameCount) * mFrameSize); 442 mRemaining -= pBuffer->frameCount; 443 } else if (pBuffer->frameCount == mRemaining) { 444 mRemaining = 0; 445 } else { 446 LOG_ALWAYS_FATAL("Releasing more frames(%zu) than available(%zu)", 447 pBuffer->frameCount, mRemaining); 448 } 449 450 pBuffer->raw = NULL; 451 pBuffer->frameCount = 0; 452 } 453 454 void TimestretchBufferProvider::reset() 455 { 456 mRemaining = 0; 457 } 458 459 status_t TimestretchBufferProvider::setPlaybackRate(const AudioPlaybackRate &playbackRate) 460 { 461 mPlaybackRate = playbackRate; 462 mFallbackFailErrorShown = false; 463 sonicSetSpeed(mSonicStream, mPlaybackRate.mSpeed); 464 //TODO: pitch is ignored for now 465 //TODO: optimize: if parameters are the same, don't do any extra computation. 466 467 mAudioPlaybackRateValid = isAudioPlaybackRateValid(mPlaybackRate); 468 return OK; 469 } 470 471 void TimestretchBufferProvider::processFrames(void *dstBuffer, size_t *dstFrames, 472 const void *srcBuffer, size_t *srcFrames) 473 { 474 ALOGV("processFrames(%zu %zu) remaining(%zu)", *dstFrames, *srcFrames, mRemaining); 475 // Note dstFrames is the required number of frames. 476 477 // Ensure consumption from src is as expected. 478 //TODO: add logic to track "very accurate" consumption related to speed, original sampling 479 //rate, actual frames processed. 480 const size_t targetSrc = *dstFrames * mPlaybackRate.mSpeed; 481 if (*srcFrames < targetSrc) { // limit dst frames to that possible 482 *dstFrames = *srcFrames / mPlaybackRate.mSpeed; 483 } else if (*srcFrames > targetSrc + 1) { 484 *srcFrames = targetSrc + 1; 485 } 486 487 if (!mAudioPlaybackRateValid) { 488 //fallback mode 489 if (*dstFrames > 0) { 490 switch(mPlaybackRate.mFallbackMode) { 491 case AUDIO_TIMESTRETCH_FALLBACK_CUT_REPEAT: 492 if (*dstFrames <= *srcFrames) { 493 size_t copySize = mFrameSize * *dstFrames; 494 memcpy(dstBuffer, srcBuffer, copySize); 495 } else { 496 // cyclically repeat the source. 497 for (size_t count = 0; count < *dstFrames; count += *srcFrames) { 498 size_t remaining = min(*srcFrames, *dstFrames - count); 499 memcpy((uint8_t*)dstBuffer + mFrameSize * count, 500 srcBuffer, mFrameSize * remaining); 501 } 502 } 503 break; 504 case AUDIO_TIMESTRETCH_FALLBACK_DEFAULT: 505 case AUDIO_TIMESTRETCH_FALLBACK_MUTE: 506 memset(dstBuffer,0, mFrameSize * *dstFrames); 507 break; 508 case AUDIO_TIMESTRETCH_FALLBACK_FAIL: 509 default: 510 if(!mFallbackFailErrorShown) { 511 ALOGE("invalid parameters in TimestretchBufferProvider fallbackMode:%d", 512 mPlaybackRate.mFallbackMode); 513 mFallbackFailErrorShown = true; 514 } 515 break; 516 } 517 } 518 } else { 519 switch (mFormat) { 520 case AUDIO_FORMAT_PCM_FLOAT: 521 if (sonicWriteFloatToStream(mSonicStream, (float*)srcBuffer, *srcFrames) != 1) { 522 ALOGE("sonicWriteFloatToStream cannot realloc"); 523 *srcFrames = 0; // cannot consume all of srcBuffer 524 } 525 *dstFrames = sonicReadFloatFromStream(mSonicStream, (float*)dstBuffer, *dstFrames); 526 break; 527 case AUDIO_FORMAT_PCM_16_BIT: 528 if (sonicWriteShortToStream(mSonicStream, (short*)srcBuffer, *srcFrames) != 1) { 529 ALOGE("sonicWriteShortToStream cannot realloc"); 530 *srcFrames = 0; // cannot consume all of srcBuffer 531 } 532 *dstFrames = sonicReadShortFromStream(mSonicStream, (short*)dstBuffer, *dstFrames); 533 break; 534 default: 535 // could also be caught on construction 536 LOG_ALWAYS_FATAL("invalid format %#x for TimestretchBufferProvider", mFormat); 537 } 538 } 539 } 540 // ---------------------------------------------------------------------------- 541 } // namespace android 542