/*
** Copyright 2011, The Android Open-Source Project
**
** Licensed under the Apache License, Version 2.0 (the "License");
** you may not use this file except in compliance with the License.
** You may obtain a copy of the License at
**
**     http://www.apache.org/licenses/LICENSE-2.0
**
** Unless required by applicable law or agreed to in writing, software
** distributed under the License is distributed on an "AS IS" BASIS,
** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
** See the License for the specific language governing permissions and
** limitations under the License.
*/

//#define LOG_NDEBUG 0
#define LOG_TAG "ReSampler"

#include <utils/Log.h>
#include <audio_utils/ReSampler.h>

namespace android_audio_legacy {


//------------------------------------------------------------------------------
// speex based resampler
//------------------------------------------------------------------------------

#define RESAMPLER_QUALITY 2

ReSampler::ReSampler(uint32_t inSampleRate,
                                    uint32_t outSampleRate,
                                    uint32_t channelCount,
                                    BufferProvider* provider)
    :  mStatus(NO_INIT), mSpeexResampler(NULL), mProvider(provider),
       mInSampleRate(inSampleRate), mOutSampleRate(outSampleRate), mChannelCount(channelCount),
       mInBuf(NULL), mInBufSize(0)
{
    LOGV("ReSampler() cstor %p In SR %d Out SR %d channels %d",
         this, mInSampleRate, mOutSampleRate, mChannelCount);

    if (mProvider == NULL) {
        return;
    }

    int error;
    mSpeexResampler = speex_resampler_init(channelCount,
                                      inSampleRate,
                                      outSampleRate,
                                      RESAMPLER_QUALITY,
                                      &error);
    if (mSpeexResampler == NULL) {
        LOGW("ReSampler: Cannot create speex resampler: %s", speex_resampler_strerror(error));
        return;
    }

    reset();

    int frames = speex_resampler_get_input_latency(mSpeexResampler);
    mSpeexDelayNs = (int32_t)((1000000000 * (int64_t)frames) / mInSampleRate);
    frames = speex_resampler_get_output_latency(mSpeexResampler);
    mSpeexDelayNs += (int32_t)((1000000000 * (int64_t)frames) / mOutSampleRate);

    mStatus = NO_ERROR;
}

ReSampler::~ReSampler()
{
    free(mInBuf);

    if (mSpeexResampler != NULL) {
        speex_resampler_destroy(mSpeexResampler);
    }
}

void ReSampler::reset()
{
    mFramesIn = 0;
    mFramesRq = 0;

    if (mSpeexResampler != NULL) {
        speex_resampler_reset_mem(mSpeexResampler);
    }
}

int32_t ReSampler::delayNs()
{
    int32_t delay = (int32_t)((1000000000 * (int64_t)mFramesIn) / mInSampleRate);
    delay += mSpeexDelayNs;

    return delay;
}

// outputs a number of frames less or equal to *outFrameCount and updates *outFrameCount
// with the actual number of frames produced.
int ReSampler::resample(int16_t *out, size_t *outFrameCount)
{
    if (mStatus != NO_ERROR) {
        return mStatus;
    }

    if (out == NULL || outFrameCount == NULL) {
        return BAD_VALUE;
    }

    size_t framesRq = *outFrameCount;
    // update and cache the number of frames needed at the input sampling rate to produce
    // the number of frames requested at the output sampling rate
    if (framesRq != mFramesRq) {
        mFramesNeeded = (framesRq * mOutSampleRate) / mInSampleRate + 1;
        mFramesRq = framesRq;
    }

    size_t framesWr = 0;
    size_t inFrames = 0;
    while (framesWr < framesRq) {
        if (mFramesIn < mFramesNeeded) {
            // make sure that the number of frames present in mInBuf (mFramesIn) is at least
            // the number of frames needed to produce the number of frames requested at
            // the output sampling rate
            if (mInBufSize < mFramesNeeded) {
                mInBufSize = mFramesNeeded;
                mInBuf = (int16_t *)realloc(mInBuf, mInBufSize * mChannelCount * sizeof(int16_t));
            }
            BufferProvider::Buffer buf;
            buf.frameCount = mFramesNeeded - mFramesIn;
            mProvider->getNextBuffer(&buf);
            if (buf.raw == NULL) {
                break;
            }
            memcpy(mInBuf + mFramesIn * mChannelCount,
                    buf.raw,
                    buf.frameCount * mChannelCount * sizeof(int16_t));
            mFramesIn += buf.frameCount;
            mProvider->releaseBuffer(&buf);
        }

        size_t outFrames = framesRq - framesWr;
        inFrames = mFramesIn;
        if (mChannelCount == 1) {
            speex_resampler_process_int(mSpeexResampler,
                                        0,
                                        mInBuf,
                                        &inFrames,
                                        out + framesWr * mChannelCount,
                                        &outFrames);
        } else {
            speex_resampler_process_interleaved_int(mSpeexResampler,
                                        mInBuf,
                                        &inFrames,
                                        out + framesWr * mChannelCount,
                                        &outFrames);
        }
        framesWr += outFrames;
        mFramesIn -= inFrames;
        LOGW_IF((framesWr != framesRq) && (mFramesIn != 0),
                "ReSampler::resample() remaining %d frames in and %d frames out",
                mFramesIn, (framesRq - framesWr));
    }
    if (mFramesIn) {
        memmove(mInBuf,
                mInBuf + inFrames * mChannelCount,
                mFramesIn * mChannelCount * sizeof(int16_t));
    }
    *outFrameCount = framesWr;

    return NO_ERROR;
}

}; // namespace android_audio_legacy
