//=========================================================================
// Name:            ResampeStep.cpp
// Purpose:         Describes a resampling step in the audio pipeline.
//
// Authors:         Mooneer Salem
// License:
//
//  All rights reserved.
//
//  This program is free software; you can redistribute it and/or modify
//  it under the terms of the GNU General Public License version 2.1,
//  as published by the Free Software Foundation.  This program is
//  distributed in the hope that it will be useful, but WITHOUT ANY
//  WARRANTY; without even the implied warranty of MERCHANTABILITY or
//  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public
//  License for more details.
//
//  You should have received a copy of the GNU General Public License
//  along with this program; if not, see <http://www.gnu.org/licenses/>.
//
//=========================================================================

#include "ResampleStep.h"

#include <algorithm>
#include <assert.h>
#include <cstdio>

#include "../util/logging/ulog.h"

// returns number of output samples generated by resampling
// TBD -- remove identical version from util.cpp.
static int resample_step(SRC_STATE *src,
            short      output_short[],
            short      input_short[],
            int        output_sample_rate,
            int        input_sample_rate,
            int        length_output_short, // maximum output array length in samples
            int        length_input_short,
            float     *tmpInput,
            float     *tmpOutput
            )
{
    SRC_DATA src_data;
    int      ret;

    assert(src != NULL);

    src_short_to_float_array(input_short, tmpInput, length_input_short);

    src_data.data_in = tmpInput;
    src_data.data_out = tmpOutput;
    src_data.input_frames = length_input_short;
    src_data.output_frames = length_output_short;
    src_data.end_of_input = 0;
    src_data.src_ratio = (float)output_sample_rate/input_sample_rate;

    ret = src_process(src, &src_data);
    if (ret != 0)
    {
        log_warn("Resampling failed: %s", src_strerror(ret));
    }
    assert(ret == 0);

    assert(src_data.output_frames_gen <= length_output_short);
    src_float_to_short_array(tmpOutput, output_short, src_data.output_frames_gen);

    return src_data.output_frames_gen;
}

ResampleStep::ResampleStep(int inputSampleRate, int outputSampleRate)
    : inputSampleRate_(inputSampleRate)
    , outputSampleRate_(outputSampleRate)
{
    int src_error;
    resampleState_ = src_new(SRC_SINC_MEDIUM_QUALITY, 1, &src_error);
    assert(resampleState_ != nullptr);

    // Pre-allocate buffers so we don't have to do so during real-time operation.
    auto maxSamples = std::max(getInputSampleRate(), getOutputSampleRate());
    outputSamples_ = std::shared_ptr<short>(
        new short[maxSamples], 
        std::default_delete<short[]>());
    assert(outputSamples_ != nullptr);
    
    tempInput_ = new float[std::max(inputSampleRate, outputSampleRate)];
    assert(tempInput_ != nullptr);

    tempOutput_ = new float[std::max(inputSampleRate, outputSampleRate)];
    assert(tempOutput_ != nullptr);
}

ResampleStep::~ResampleStep()
{
    src_delete(resampleState_);

    delete[] tempInput_;
    delete[] tempOutput_;
}

int ResampleStep::getInputSampleRate() const
{
    return inputSampleRate_;
}

int ResampleStep::getOutputSampleRate() const
{
    return outputSampleRate_;
}

std::shared_ptr<short> ResampleStep::execute(std::shared_ptr<short> inputSamples, int numInputSamples, int* numOutputSamples)
{
    if (numInputSamples > 0)
    {
        double scaleFactor = ((double)outputSampleRate_)/((double)inputSampleRate_);
        int outputArraySize = std::max(numInputSamples, (int)(scaleFactor*numInputSamples));
        assert(outputArraySize > 0);

        *numOutputSamples = resample_step(
            resampleState_, outputSamples_.get(), inputSamples.get(), outputSampleRate_, 
            inputSampleRate_, outputArraySize, numInputSamples, tempInput_, tempOutput_);
    }
    else
    {
        *numOutputSamples = 0;
    }
 
    return outputSamples_;
}
