#------------------------------------------------------------------------
#
# 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.
#
#------------------------------------------------------------------------
package require DvmBasic
package require DvmMpeg

if {$argc != 2} {
    puts "enter input MPEG audio filename : "
    set infilename [gets stdin]
    puts "enter PCM output filename : "
    set outfilename [gets stdin]
} else {
    set infilename [lindex $argv 0]
    set outfilename [lindex $argv 1]
}

proc check_bitstream_underflow {bs bp chan size} {
    set off  [bitparser_tell $bp]
    set left [bitstream_bytes_left $bs $off]
    if {$left < $size} {
	bitstream_shift $bs $off
	bitstream_channel_read $bs $chan $left
	bitparser_seek $bp 0
    }
}

proc decode_mpeg_audio_l1 {inbs inbp inchan outbs outbp outchan hdr} {
    set lv [mpeg_audio_syn_data_new]
    set rv [mpeg_audio_syn_data_new]
    set left  [audio_16_new 384]
    set right [audio_16_new 384]
    set merge [audio_16_new 768]
    set laudio [mpeg_audio_l1_new]
    set raudio [mpeg_audio_l1_new]
    set i 0

    if {[mpeg_audio_hdr_get_mode $hdr] == "single channel"} {
	mpeg_audio_l1_mono_parse $inbp $hdr $laudio
	mpeg_audio_l1_to_audio $hdr $laudio $lv $left
	set tout [audio_16_cast_to_bitstream $left]
	bitstream_channel_write_segment $tout $outchan 0 768
    } else {
	mpeg_audio_l1_stereo_parse $inbp $hdr $laudio $raudio
	mpeg_audio_l1_to_audio $hdr $laudio $lv $left
	mpeg_audio_l1_to_audio $hdr $raudio $rv $right
	audio_16_merge $left $right $merge
	set tout [audio_16_cast_to_bitstream $merge]
	bitstream_channel_write_segment $tout $outchan 0 1536
    }
    bitstream_free $tout
    set len [mpeg_audio_hdr_find $inbp]
    while  {![eof $inchan] || $len != -1} {
	check_bitstream_underflow $inbs $inbp $inchan 2048
	mpeg_audio_hdr_parse $inbp $hdr
	if {[mpeg_audio_hdr_get_mode $hdr] == "single channel"} {
	    mpeg_audio_l1_mono_parse $inbp $hdr $laudio
	    mpeg_audio_l1_to_audio $hdr $laudio $lv $left
	    set tout [audio_16_cast_to_bitstream $left]
	    bitstream_channel_write_segment $tout $outchan 0 768
	} else {
	    mpeg_audio_l1_stereo_parse $inbp $hdr $laudio $raudio
	    mpeg_audio_l1_to_audio $hdr $laudio $lv $left 
	    mpeg_audio_l1_to_audio $hdr $raudio $rv $right
	    audio_16_merge $left $right $merge
	    set tout [audio_16_cast_to_bitstream $merge]
	    bitstream_channel_write_segment $tout $outchan 0 1536
	}
	bitstream_free $tout
	set len [mpeg_audio_hdr_find $inbp]
    }
    mpeg_audio_l1_free $laudio
    mpeg_audio_l1_free $raudio
    audio_free $left
    audio_free $right
    audio_free $merge
    mpeg_audio_hdr_free $hdr
    mpeg_audio_syn_data_free $lv
    mpeg_audio_syn_data_free $rv
}

proc decode_mpeg_audio_l2 {inbs inbp inchan outbs outbp outchan hdr} {
    set lv [mpeg_audio_syn_data_new]
    set rv [mpeg_audio_syn_data_new]
    set left  [audio_16_new 1152]
    set right [audio_16_new 1152]
    set merge [audio_16_new 2304]
    set laudio [mpeg_audio_l2_new]
    set raudio [mpeg_audio_l2_new]
    set i 0

    if {[mpeg_audio_hdr_get_mode $hdr] == "single channel"} {
	mpeg_audio_l2_mono_parse $inbp $hdr $laudio
	mpeg_audio_l2_to_audio $hdr $laudio $lv $left 
	set tout [audio_16_cast_to_bitstream $left]
	bitstream_channel_write_segment $tout $outchan 0 2304
    } else {
	mpeg_audio_l2_stereo_parse $inbp $hdr $laudio $raudio
	mpeg_audio_l2_to_audio $hdr $laudio $lv $left
	mpeg_audio_l2_to_audio $hdr $raudio $rv $right
	audio_16_merge $left $right $merge
	set tout [audio_16_cast_to_bitstream $merge]
	bitstream_channel_write_segment $tout $outchan 0 4608
    }
    bitstream_free $tout

    set len [mpeg_audio_hdr_find $inbp]

    while {![eof $inchan] || $len != -1} {
	check_bitstream_underflow $inbs $inbp $inchan 4500
	mpeg_audio_hdr_parse $inbp $hdr
	if {[mpeg_audio_hdr_get_mode $hdr] == "single channel"} {
	    mpeg_audio_l2_mono_parse $inbp $hdr $laudio
	    mpeg_audio_l2_to_audio $hdr $laudio $lv $left 
	    set tout [audio_16_cast_to_bitstream $left]
	    bitstream_channel_write_segment $tout $outchan 0 2304
	} else {
	    mpeg_audio_l2_stereo_parse $inbp $hdr $laudio $raudio
	    mpeg_audio_l2_to_audio $hdr $laudio $lv $left
	    mpeg_audio_l2_to_audio $hdr $raudio $rv $right
	    audio_16_merge $left $right $merge
	    set tout [audio_16_cast_to_bitstream $merge]
	    bitstream_channel_write_segment $tout $outchan 0 4608
	}
	bitstream_free $tout
	set len [mpeg_audio_hdr_find $inbp]
    }
    mpeg_audio_l2_free $laudio
    mpeg_audio_l2_free $raudio
    audio_free $merge
    audio_free $left
    audio_free $right
    mpeg_audio_hdr_free $hdr
    mpeg_audio_syn_data_free $lv
    mpeg_audio_syn_data_free $rv
}

proc decode_mpeg_audio_l3 {inbs inbp inchan outbs outbp outchan hdr} {
    set lv [mpeg_audio_syn_data_new]
    set rv [mpeg_audio_syn_data_new]

    set abs   [bitstream_new 4096]
    set abp   [bitparser_new]
    bitparser_wrap $abp $abs
    set left  [audio_16_new 1152]
    set right [audio_16_new 1152]
    set merge [audio_16_new 2304]
    set granule [mpeg_audio_gra_data_new]
    set audio [mpeg_audio_l3_new]
    mpeg_audio_l3_parse $inbp $abp $hdr $audio
    if {[mpeg_audio_hdr_get_mode $hdr] == "single channel"} {
	mpeg_audio_l3_mono_to_audio $hdr $audio $lv $rv $granule $left $right
	set tout [audio_16_cast_to_bitstream $left]
	bitstream_channel_write_segment $tout $outchan 0 2304
    } else {
	mpeg_audio_l3_stereo_to_audio $hdr $audio $lv $rv $granule $left $right
	audio_16_merge $left $right $merge
	set tout [audio_16_cast_to_bitstream $merge]
	bitstream_channel_write_segment $tout $outchan 0 4608
    }
    bitstream_free $tout

    set len [mpeg_audio_hdr_find $inbp]
    set i 0

    while  {![eof $inchan] || $len != -1} {
	check_bitstream_underflow $inbs $inbp $inchan 4500
	mpeg_audio_hdr_parse $inbp $hdr
	mpeg_audio_l3_parse $inbp $abp $hdr $audio
	if {[mpeg_audio_hdr_get_mode $hdr] == "single channel"} {
	    mpeg_audio_l3_mono_to_audio $hdr $audio $lv $rv $granule $left $right
	    set tout [audio_16_cast_to_bitstream $left]
	    bitstream_channel_write_segment $tout $outchan 0 2304
	} else {
	    mpeg_audio_l3_stereo_to_audio $hdr $audio $lv $rv $granule $left $right
	    audio_16_merge $left $right $merge
	    set tout [audio_16_cast_to_bitstream $merge]
	    bitstream_channel_write_segment $tout $outchan 0 4608
	}
	bitstream_free $tout
	set len [mpeg_audio_hdr_find $inbp]

    }
    mpeg_audio_l3_free $audio
    audio_free $left
    audio_free $right
    audio_free $merge
    mpeg_audio_hdr_free $hdr
    mpeg_audio_syn_data_free $lv
    mpeg_audio_syn_data_free $rv
    mpeg_audio_gra_data_free $granule
    bitstream_free $abs
    bitparser_free $abp
}

#-----------------------------------------------------------------
# main program
#-----------------------------------------------------------------

#----------------------------------------------------------------
# open files, create new bitparsers, new bitstreams, read first 
# 65535 bytes from input file into bitstream and attached the 
# bitparsers to the bitstreams
#----------------------------------------------------------------
set inbp   [bitparser_new]
set outbp  [bitparser_new]
set inbs   [bitstream_new 65535]
set outbs  [bitstream_new 65535]
set infile  [open $infilename r]
set outfile [open $outfilename w]
fconfigure $infile  -translation binary -buffersize 65535
fconfigure $outfile -translation binary -buffersize 65535
bitstream_channel_read $inbs $infile 0
bitparser_wrap $inbp  $inbs
bitparser_wrap $outbp $outbs

#----------------------------------------------------------------
# Allocate and parse the mpeg audio header. Check the layer of
# the audio file, and then call the respective routines to decode
# the file.
#----------------------------------------------------------------

set hdr [mpeg_audio_hdr_new]
mpeg_audio_hdr_parse $inbp $hdr
set layer [mpeg_audio_hdr_get_layer $hdr]
if {$layer == 1} {
    decode_mpeg_audio_l1 $inbs $inbp $infile $outbs $outbp $outfile $hdr
} elseif {$layer == 2} {
    decode_mpeg_audio_l2 $inbs $inbp $infile $outbs $outbp $outfile $hdr
} else {
    decode_mpeg_audio_l3 $inbs $inbp $infile $outbs $outbp $outfile $hdr
}
close $infile
close $outfile
bitstream_free $inbs
bitstream_free $outbs
bitparser_free $inbp
bitparser_free $outbp