[go: nahoru, domu]

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
35namespace android {
36
37// ----------------------------------------------------------------------------
38
39template <typename T>
40static inline T min(const T& a, const T& b)
41{
42    return a < b ? a : b;
43}
44
45CopyBufferProvider::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
64CopyBufferProvider::~CopyBufferProvider()
65{
66    ALOGV("~CopyBufferProvider(%p)", this);
67    if (mBuffer.frameCount != 0) {
68        mTrackBufferProvider->releaseBuffer(&mBuffer);
69    }
70    free(mLocalBufferData);
71}
72
73status_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
110void 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
128void CopyBufferProvider::reset()
129{
130    if (mBuffer.frameCount != 0) {
131        mTrackBufferProvider->releaseBuffer(&mBuffer);
132    }
133    mConsumed = 0;
134}
135
136DownmixerBufferProvider::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
228DownmixerBufferProvider::~DownmixerBufferProvider()
229{
230    ALOGV("~DownmixerBufferProvider (%p)", this);
231    EffectRelease(mDownmixHandle);
232    mDownmixHandle = NULL;
233}
234
235void 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
277RemixBufferProvider::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
298void 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
304ReformatBufferProvider::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
319void ReformatBufferProvider::copyFrames(void *dst, const void *src, size_t frames)
320{
321    memcpy_by_audio_format(dst, mOutputFormat, src, mInputFormat, frames * mChannelCount);
322}
323
324TimestretchBufferProvider::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
347TimestretchBufferProvider::~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
357status_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
429void 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
454void TimestretchBufferProvider::reset()
455{
456    mRemaining = 0;
457}
458
459status_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
471void 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