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