/*------------------------------------------------------------------------
 *
 * Copyright (c) 1997-1998 by Cornell University.
 * 
 * See the file "license.txt" for information on usage and redistribution
 * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
 *
 * mp123dec.c
 *
 * Ooi Wei Tsang  weitsang@cs.cornell.edu
 *
 * Usage : mp123dec inputMPG outputPCM
 *
 * Decodes an input MPEG-1 Audio (layer 1, 2 or 3) into a PCM file.
 * It assumes that all frames in the audio are encoded using the same
 * layer encoding method.  
 *
 * NOTE : Currently we output each frame to file after decoding, this 
 * is not the most efficient way of doing things.
 *
 *------------------------------------------------------------------------
 */
#include "dvmbasic.h"
#include "dvmmpeg.h"

/*
 * Ensures that there are at least size bytes of unprocessed data in 
 * the BitStream bs.  If there is not enough data, processed data in 
 * the BitStream are thrown away, unprocessed data are shifted to the 
 * front, new data are read from file and appended to the current
 * unprocessed data.
 */
void CheckBitStreamUnderflow(bs, bp, file, size) 
    BitStream *bs;
    BitParser *bp;
    FILE *file;
    int size;
{
    int off = BitParserTell(bp);
    int left = BitStreamBytesLeft(bs, off);
    if (left < size) {
	BitStreamShift(bs, off);
	BitStreamFileRead(bs, file, left);
	BitParserSeek(bp, 0);
    }
}


/*
 * This is the main routine that decodes a layer 1 audio file.
 */
void DecodeLayer1 (inbs, inbp, inf, outbs, outbp, outf, hdr)
    BitStream *inbs;
    BitParser *inbp;
    FILE *inf;
    BitStream *outbs;
    BitParser *outbp;
    FILE *outf;
    MpegAudioHdr *hdr;
{
    MpegAudioSynData *lv, *rv;
    Audio *left, *right, *merge;
    MpegAudioL1 *laudio, *raudio;
    BitStream *tout;
    int len;

    /*
     * Initialize various data structure.
     * Each layer 1 frames contains 384 samples from each channel.
     */
    lv = MpegAudioSynDataNew();
    rv = MpegAudioSynDataNew();
    left = Audio16New(384);
    right = Audio16New(384);
    merge = Audio16New(768);
    laudio = MpegAudioL1New();
    raudio = MpegAudioL1New();

    /*
     * Decode the first frame and output it to file.
     */
    if (MpegAudioHdrGetMode(hdr) == MPEG_AUDIO_SINGLE_CHANNEL) {
	MpegAudioL1MonoParse(inbp, hdr, laudio);
	MpegAudioL1ToAudio(hdr, laudio, lv, left);
	tout = Audio16CastToBitStream(left);
	BitStreamFileWriteSegment(tout, outf, 0, 768);
    } else {
	MpegAudioL1StereoParse(inbp, hdr, laudio, raudio);
	MpegAudioL1ToAudio(hdr, laudio, lv, left);
	MpegAudioL1ToAudio(hdr, raudio, rv, right);
	Audio16Merge(left, right, merge);
	tout = Audio16CastToBitStream(merge);
	BitStreamFileWriteSegment(tout, outf, 0, 1536);
    }
    BitStreamFree(tout);
    len = MpegAudioHdrFind(inbp);

    /*
     * Repeatedly parse a frame header, parse a frame, decode the frame
     * and output the samples to file, until we are done with all data.
     */
    while (!feof(inf) || len != -1) {
	CheckBitStreamUnderflow(inbs, inbp, inf, 2048);
	MpegAudioHdrParse(inbp, hdr);

	if (MpegAudioHdrGetMode(hdr) == MPEG_AUDIO_SINGLE_CHANNEL) {
	    MpegAudioL1MonoParse(inbp, hdr, laudio);
	    MpegAudioL1ToAudio(hdr, laudio, lv, left);
	    tout = Audio16CastToBitStream(left);
	    BitStreamFileWriteSegment(tout, outf, 0, 768);
	} else {
	    MpegAudioL1StereoParse(inbp, hdr, laudio, raudio);
	    MpegAudioL1ToAudio(hdr, laudio, lv, left);
	    MpegAudioL1ToAudio(hdr, raudio, rv, right);
	    Audio16Merge(left, right, merge);
	    tout = Audio16CastToBitStream(merge);
	    BitStreamFileWriteSegment(tout, outf, 0, 1536);
	}
	BitStreamFree(tout);
	len = MpegAudioHdrFind(inbp);
    }

    MpegAudioL1Free(laudio);
    MpegAudioL1Free(raudio);
    AudioFree(left);
    AudioFree(right);
    AudioFree(merge);
    MpegAudioSynDataFree(lv);
    MpegAudioSynDataFree(rv);
}

void DecodeLayer2 (inbs, inbp, inf, outbs, outbp, outf, hdr)
    BitStream *inbs;
    BitParser *inbp;
    FILE *inf;
    BitStream *outbs;
    BitParser *outbp;
    FILE *outf;
    MpegAudioHdr *hdr;
{
    MpegAudioSynData *lv, *rv;
    Audio *left, *right, *merge;
    MpegAudioL2 *laudio, *raudio;
    BitStream *tout;
    int len;

    /*
     * Initialize various data structure.
     * Each layer 1 frames contains 1152 samples from each channel.
     */
    lv = MpegAudioSynDataNew();
    rv = MpegAudioSynDataNew();
    left = Audio16New(1152);
    right = Audio16New(1152);
    merge = Audio16New(2304);
    laudio = MpegAudioL2New();
    raudio = MpegAudioL2New();

    /*
     * Decode the first frame and output it to file.
     */
    if (MpegAudioHdrGetMode(hdr) == MPEG_AUDIO_SINGLE_CHANNEL) {
	MpegAudioL2MonoParse(inbp, hdr, laudio);
	MpegAudioL2ToAudio(hdr, laudio, lv, left);
	tout = Audio16CastToBitStream(left);
	BitStreamFileWriteSegment(tout, outf, 0, 2304);
    } else {
	MpegAudioL2StereoParse(inbp, hdr, laudio, raudio);
	MpegAudioL2ToAudio(hdr, laudio, lv, left);
	MpegAudioL2ToAudio(hdr, raudio, rv, right);
	Audio16Merge(left, right, merge);
	tout = Audio16CastToBitStream(merge);
	BitStreamFileWriteSegment(tout, outf, 0, 4608);
    }
    BitStreamFree(tout);
    len = MpegAudioHdrFind(inbp);

    /*
     * Repeatedly parse a frame header, parse a frame, decode the frame
     * and output the samples to file, until we are done with all data.
     */
    while (!feof(inf) || len != -1) {
	CheckBitStreamUnderflow(inbs, inbp, inf, 4500);
	MpegAudioHdrParse(inbp, hdr);
	if (MpegAudioHdrGetMode(hdr) == MPEG_AUDIO_SINGLE_CHANNEL) {
	    MpegAudioL2MonoParse(inbp, hdr, laudio);
	    MpegAudioL2ToAudio(hdr, laudio, lv, left);
	    tout = Audio16CastToBitStream(left);
	    BitStreamFileWriteSegment(tout, outf, 0, 2304);
	} else {
	    MpegAudioL2StereoParse(inbp, hdr, laudio, raudio);
	    MpegAudioL2ToAudio(hdr, laudio, lv, left);
	    MpegAudioL2ToAudio(hdr, raudio, rv, right);
	    Audio16Merge(left, right, merge);
	    tout = Audio16CastToBitStream(merge);
	    BitStreamFileWriteSegment(tout, outf, 0, 4608);
	}
	BitStreamFree(tout);
	len = MpegAudioHdrFind(inbp);
    }

    MpegAudioL2Free(laudio);
    MpegAudioL2Free(raudio);
    AudioFree(left);
    AudioFree(right);
    AudioFree(merge);
    MpegAudioSynDataFree(lv);
    MpegAudioSynDataFree(rv);
}


void DecodeLayer3 (inbs, inbp, inf, outbs, outbp, outf, hdr) 
    BitStream *inbs;
    BitParser *inbp;
    FILE *inf;
    BitStream *outbs;
    BitParser *outbp;
    FILE *outf;
    MpegAudioHdr *hdr;
{
    MpegAudioSynData *lv, *rv;
    Audio *left, *right, *merge;
    MpegAudioL3 *audio;
    MpegAudioGraData *gra;
    BitStream *abs;
    BitParser *abp;
    BitStream *tout;
    int len;


    /*
     * Initialize various data structure.
     * Each layer 1 frames contains 1152 samples from each channel.
     */
    lv = MpegAudioSynDataNew();
    rv = MpegAudioSynDataNew();
    left = Audio16New(1152);
    right = Audio16New(1152);
    merge = Audio16New(2304);
    abs = BitStreamNew(4096);
    abp = BitParserNew();
    BitParserWrap(abp, abs);
    gra = MpegAudioGraDataNew();
    audio = MpegAudioL3New();

    /*
     * Decode the first frame and output it to file.
     */
    MpegAudioL3Parse(inbp, abp, hdr, audio);
    if (MpegAudioHdrGetMode(hdr) == MPEG_AUDIO_SINGLE_CHANNEL) {
	MpegAudioL3MonoToAudio(hdr, audio, lv, gra, left);
	tout = Audio16CastToBitStream(left);
	BitStreamFileWriteSegment(tout, outf, 0, 2304);
    } else {
	MpegAudioL3StereoToAudio(hdr, audio, lv, rv, gra, left, right);
	Audio16Merge(merge, left, right);
	tout = Audio16CastToBitStream(merge);
	BitStreamFileWriteSegment(tout, outf, 0, 4608);
    }
    BitStreamFree(tout);
    len = MpegAudioHdrFind(inbp);

    /*
     * Repeatedly parse a frame header, parse a frame, decode the frame
     * and output the samples to file, until we are done with all data.
     */
    while (!feof(inf) || len != -1) {
	CheckBitStreamUnderflow(inbs, inbp, inf, 4500);
	MpegAudioHdrParse(inbp, hdr);
	MpegAudioL3Parse(inbp, abp, hdr, audio);
	if (MpegAudioHdrGetMode(hdr) == MPEG_AUDIO_SINGLE_CHANNEL) {
	    MpegAudioL3MonoToAudio(hdr, audio, lv, gra, left);
	    tout = Audio16CastToBitStream(left);
	    BitStreamFileWriteSegment(tout, outf, 0, 2304);
	} else {
	    MpegAudioL3StereoToAudio(hdr, audio, lv, rv, gra, left, right);
	    Audio16Merge(left, right, merge);
	    tout = Audio16CastToBitStream(merge);
	    BitStreamFileWriteSegment(tout, outf, 0, 4608);
	}
	BitStreamFree(tout);
	len = MpegAudioHdrFind(inbp);
    }

    MpegAudioL3Free(audio);
    AudioFree(left);
    AudioFree(right);
    AudioFree(merge);
    MpegAudioSynDataFree(lv);
    MpegAudioSynDataFree(rv);
    MpegAudioGraDataFree(gra);
    BitStreamFree(abs);
    BitParserFree(abp);
}

int main(int argc, char *argv[])
{
    BitParser *inbp, *outbp;
    BitStream *inbs, *outbs;
    FILE *inf, *outf;
    MpegAudioHdr *hdr;
    int layer;

    /*
     * Parse arguments, initialize headers, and open input/output files.
     */
    if (argc < 3) {
        fprintf(stderr, "usage : %s input output\n", argv[0]);
	exit(1);
    }
    inbp = BitParserNew();
    outbp = BitParserNew();
    inbs = BitStreamNew(65536);
    outbs = BitStreamNew(65536);
    inf = fopen(argv[1], "rb");
    if (inf == NULL) {
	fprintf(stderr, "unable to open file %s for reading", argv[1]);
	exit(1);
    }
    outf = fopen(argv[2], "wb");
    if (outf == NULL) {
	fprintf(stderr, "unable to open file %s for writing", argv[2]);
	exit(1);
    }
    BitStreamFileRead(inbs, inf, 0);
    BitParserWrap(inbp, inbs);
    BitParserWrap(outbp, outbs);
    
    /*
     * Read the first header to determine which layer this file belongs to.
     * Then call the appropiate function the decode the rest of the file.
     */
    hdr = MpegAudioHdrNew();
    MpegAudioHdrParse(inbp, hdr);
    layer = MpegAudioHdrGetLayer(hdr);
    if (layer == 1) {
	DecodeLayer1(inbs, inbp, inf, outbs, outbp, outf, hdr);
    } else if (layer == 2) {
	DecodeLayer2(inbs, inbp, inf, outbs, outbp, outf, hdr);
    } else {
	DecodeLayer3(inbs, inbp, inf, outbs, outbp, outf, hdr);
    }
    
    /* 
     * Clean up here.
     */
    fclose(inf);
    fclose(outf);
    BitStreamFree(inbs);
    BitStreamFree(outbs);
    BitParserFree(inbp);
    BitParserFree(outbp);
    MpegAudioHdrFree(hdr);

    return 0;
}