/*
 * vdr-podcatcher - A plugin for the Linux Video Disk Recorder
 * Copyright (c) 2006 Tobias Grimm <vdr@e-tobi.net>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * 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, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 * $Id: mp3decoder.cc 3219 2006-04-16 15:06:35Z svntobi $
 *
 */

#include "mp3decoder.h"
#include "pesaudiopacket.h"

using namespace std;

Mp3Decoder::Mp3Decoder(ref_ptr<std::istream> mp3Stream)
:mp3Stream(mp3Stream), leftBuffer(0), rightBuffer(0), samplesInBuffer(0)
{
    mad_stream_init(&madStream);
    mad_frame_init(&madFrame);
    mad_synth_init(&madSynth);
    eof = false;
    timeIndex = mad_timer_zero;
    mp3Info.ReadFromStream(*(this->mp3Stream.get()));
}

Mp3Decoder::~Mp3Decoder()
{
    mad_stream_finish(&madStream);
    mad_frame_finish(&madFrame);
    mad_synth_finish(&madSynth);
    delete[] leftBuffer;
    delete[] rightBuffer;
}

int Mp3Decoder::FillReadBuffer()
{
    int unprocessedBytesInBuffer;
    int bytesToRead;
    int bytesRead;

    if (madStream.next_frame != NULL)
    {
        unprocessedBytesInBuffer = madStream.bufend - madStream.next_frame;
        memmove(readBuffer, madStream.next_frame, unprocessedBytesInBuffer);
    }
    else
    {
        unprocessedBytesInBuffer = 0;
    }

    bytesToRead = readBufferSize - unprocessedBytesInBuffer;

    bytesRead = mp3Stream->readsome((char*)(readBuffer +
      unprocessedBytesInBuffer), bytesToRead);

    return bytesRead + unprocessedBytesInBuffer;
}

signed int Mp3Decoder::ScaleTo16Bit(mad_fixed_t sample)
{
    // Taken out of minimad.c example from libmad

    // round
    sample += (1L << (MAD_F_FRACBITS - 16));

    // clip
    if (sample >= MAD_F_ONE)
    {
        sample = MAD_F_ONE - 1;
    }
    else if (sample < -MAD_F_ONE)
    {
        sample = -MAD_F_ONE;
    }

    // quantize
    return sample >> (MAD_F_FRACBITS + 1 - 16);
}

unsigned int Mp3Decoder::GetSampleRate()
{
    return sampleRate;
}

void Mp3Decoder::DecodeNextChunk()
{
    int decodeResult = 1;

    do
    {
        int bytesInBuffer = FillReadBuffer();

        if (bytesInBuffer > 0)
        {
            mad_stream_buffer(&madStream, readBuffer, bytesInBuffer);

            // decodes the next frame's header and subband samples
            decodeResult = mad_frame_decode(&madFrame, &madStream);
        }
        else
        {
            eof = true;
            break;
        }

    }
    while (decodeResult && MAD_RECOVERABLE(madStream.error));

    if (!eof && !decodeResult)
    {
        // synthesizes PCM samples from subband samples
        mad_synth_frame(&madSynth, &madFrame);

        sampleRate = madFrame.header.samplerate;
        mad_timer_add(&timeIndex, madFrame.header.duration);

        Resample(madSynth.pcm);
    }
}

void Mp3Decoder::Resample(mad_pcm& pcm)
{
    mad_fixed_t* left = madSynth.pcm.samples[0];
    mad_fixed_t* right = madSynth.pcm.samples[1];
    unsigned int numberOfChannels = madSynth.pcm.channels;
    unsigned int nsamples  = madSynth.pcm.length;

    int resampleRatio = resampler.Init(GetSampleRate(), 48000);
    int resampledBufferSize = resampleRatio * nsamples;

    delete[] leftBuffer;
    delete[] rightBuffer;

    leftBuffer = new mad_fixed_t[resampledBufferSize];
    leftBufferReadPtr = leftBuffer;

    samplesInBuffer = resampler.Resample(left, nsamples, leftBuffer);

    if (numberOfChannels == 2)
    {
        rightBuffer = new mad_fixed_t[resampledBufferSize];
        rightBufferReadPtr = rightBuffer;
        resampler.Init(GetSampleRate(), 48000);
        resampler.Resample(right, nsamples, rightBuffer);
    }
    else
    {
        rightBuffer = 0;
        rightBufferReadPtr = leftBuffer;
    }
}

void Mp3Decoder::FillPesAudioPacket(PesAudioPacket& packet)
{
    bool packetIsFull = false;

    packet.SetTimeIndex(
      mad_timer_count(timeIndex, MAD_UNITS_SECONDS));
    packet.SetTotalTime(mp3Info.GetTotalTime());

    do
    {
        if (samplesInBuffer == 0)
        {
            DecodeNextChunk();
        }

        if (samplesInBuffer > 0)
        {
            signed int leftScaled = ScaleTo16Bit(*leftBufferReadPtr);
            signed int rightScaled = ScaleTo16Bit(*rightBufferReadPtr);

            if (packet.AddSample(leftScaled, rightScaled))
            {
                leftBufferReadPtr++;
                rightBufferReadPtr++;
                samplesInBuffer--;
            }
            else
            {
                packetIsFull = true;
            }
        }
        else
        {
            eof = true;
        }
    }
    while(!packetIsFull && !Eof());
}

bool Mp3Decoder::Eof()
{
    return eof;
}
