#!/usr/bin/env perl
#
# (c) 2002-2012 by Dirk Jagdmann <doj@cubic.org>
# This software is provided 'as-is', without any express or implied
# warranty. In no event will the authors be held liable for any damages
# arising from the use of this software.
#
# Permission is granted to anyone to use this software for any purpose,
# including commercial applications, and to alter it and redistribute it
# freely, subject to the following restrictions:
#
#     1. The origin of this software must not be misrepresented; you
#        must not claim that you wrote the original software. If you use
#        this software in a product, an acknowledgment in the product
#        documentation would be appreciated but is not required.
#
#     2. Altered source versions must be plainly marked as such, and
#        must not be misrepresented as being the original software.
#
#     3. This notice may not be removed or altered from any source
#        distribution.

use strict;
use Audio::Wav;
use MP3::Info;
use_winamp_genres();
use Ogg::Vorbis::Header;

my $recursion=0;
my $mode="j";			# joint stereo
my $bitrate=128;
my $encode=0;
my $totaltime=0;
my $mplayerExt = 'wma|aac|m4a';

help() if $#ARGV < 0;

parseoptions(\@ARGV);
process(@ARGV);

exit 0;

sub parseoptions
{
    my ($a) = @_;
    while ($_=shift @$a)
    {
	if ($_ eq "-h" || $_ eq "--help")
	{
	    help();
	}
	if ($_ eq "-m")
	{
	    $mode="m";
	    next;
	}
	elsif ($_ eq "-s")
	{
	    $mode="j";
	    next;
	}
	elsif ($_ eq "-r")
	{
	    $recursion=1;
	    next;
	}
	elsif ($_ eq "--encode")
	{
	    $encode=1;
	    next;
	}
	elsif (/^\d+$/)
	{
	    $bitrate=$_;
	    next;
	}
	elsif (/^-(\d+)$/)
	{
	    $bitrate=$1;
	    next;
	}
	# no parameters anymore
	else
	{
	    unshift @$a, $_;
	    last;
	}
    }
}

sub process
{
    my @arg=@_;

    print "bitrate\tchannel\tfreq\ttime\tfilename\n" unless $encode;

    foreach my $old (@arg)
    {

	if (-d $old && $recursion)
	{
	    chdir $old or next;
	    print "---------> entering: $old\n";
	    process(glob("*"));
	    chdir ".." or die "could not enter ..";
	    print "---------> leaving: $old\n";
	    next;
	}

	next unless (-f $old);

	if (!$encode)
	{
	    printinfo($old);
	}
	elsif (checkencode($old))
	{
	    encode($old) or die "could not encode $old";
	}
    }

    if ($totaltime)
    {
	my $sec=$totaltime%60; $totaltime=int($totaltime/60);
	my $min=$totaltime%60; $totaltime=int($totaltime/60);
	print sprintf("totaltime %i:%02i:%02i\n", $totaltime, $min, $sec);
    }
}

#use Data::Dumper;
sub printinfo
{
    my ($fn) = @_;

    if($fn =~ /\.mp3$/i)
    {
	my $info=get_mp3info($fn);
	print $info->{BITRATE}*1000; print "\t";
	print "stereo" if $info->{STEREO};
	print "mono" unless $info->{STEREO};
	print "\t$info->{FREQUENCY}\t$info->{TIME}\t$fn\n";

	$info->{TIME} =~ /(\d+):(\d+)/;
	$totaltime+=$1*60+$2;
    }
    elsif($fn =~ /\.wav$/i)
    {
	eval {
	    my $wav=new Audio::Wav;
	    my $read=$wav->read($fn);
	    my $details=$read->details();
	    #	print Data::Dumper->Dump( [$details] );
	    print $details->{bytes_sec}*8 . "\t"; # bitrate
	    if($details->{channels} == 1) { print "mono\t"; }
	    elsif($details->{channels} == 2) { print "stereo\t"; }
	    else { print $details->{channels} . "\t"; }
	    print $details->{sample_rate} . "\t";
	    my $sec=int($details->{length});
	    $totaltime+=$sec;
	    my $min=int($sec/60); $sec%=60;
	    printf '%02i:%02i', $min, $sec;
	    print "\t$fn\n";
	};
	if($@)
	{
	    print "error\t\t\t\t$@\n";
	}
    }
    elsif($fn =~ /\.ogg$/i)
    {
	my $ogg = Ogg::Vorbis::Header->new($fn);
	my $info=$ogg->info();
	print $info->{bitrate_nominal} . "\t";

	my $c=$info->{channels};
	if($c == 1) { print "mono" }
	elsif($c == 2) { print "stereo" }
	else { print $c };
	print "\t";

	print $info->{rate} . "\t";
	my $sec=int($info->{length});
	$totaltime+=$sec;
	my $min=int($sec/60);
	$sec=$sec%60;
	print  "$min:$sec\t";
	print "$fn\n";
    }
}

sub checkencode
{
    return 0 unless $encode;

    my ($old) = @_;
    return 1 if $old =~ /\.wav$/i;
    return 1 if $old =~ /\.mpc$/i;
    return 1 if $old =~ /\.ogg$/i;
    return 1 if $old =~ /\.($mplayerExt)$/io;
    if ($old =~ /\.mp3$/i)
    {
	my $info=get_mp3info($old);
	return ($info->{BITRATE} > $bitrate) || ($info->{FREQUENCY}>44.1);
    }

    return 0;
}

sub encode
{
    my ($old) = @_;
    return wav2mp3($old) if $old =~ /\.wav$/i;
    return mp32mp3($old) if $old =~ /\.mp3$/i;
    return mpc2mp3($old) if $old =~ /\.mpc$/i;
    return ogg2mp3($old) if $old =~ /\.ogg$/i;
    return wma2mp3($old) if $old =~ /\.($mplayerExt)$/io;
    return 0;
}

sub m4a2mp3
{
    my ($filename) = @_;

}

sub mp32mp3
{
    my ($filename) = @_;

    my $info=get_mp3info($filename);

    my $cmd="lame -h -b $bitrate -m $mode ";

    my $tag=get_mp3tag($filename);

    my $title=join(' ', $tag->{TITLE});
    $title =~ s/"//g;	#"
    $cmd.="--tt \"$title\" " if $title;

    my $genre=join(' ', $tag->{GENRE});
    $genre = 'Other' if $genre =~ /Miscellaneous|Podcast|HÃ¶rspiel|Hörspiel/i;
    $cmd.="--tg \"$genre\" " if $genre;

    my $artist=join(' ', $tag->{ARTIST});
    $artist =~ s/"//g;	#"
    $cmd.="--ta \"$artist\" " if $artist;

    my $album=join(' ', $tag->{ALBUM});
    $album =~ s/"//g;	#"
    $cmd.="--tl \"$album\" " if $album;

    $tag->{YEAR}+=0;
    $cmd.="--ty \"$tag->{YEAR}\" " if $tag->{YEAR};

    my $comment=join(' ', $tag->{COMMENT});
    $comment =~ s/"//g;	#"
    $cmd.="--tc \"$comment\" " if $comment;

    $tag->{TRACK}+=0;
    $cmd.="--tn \"$tag->{TRACK}\" " if $tag->{TRACKNUM};

    $cmd.="--id3v1-only ";
    $cmd.="--resample 44.1 " if $info->{FREQUENCY} > 44.1;

    my $c="$cmd \"$filename\" \"$filename.new\"";
    my $r=system($c)/256;
    if ($r>0)
    {
	print "error while encoding $filename\n";
	return 0;
    }
    rename($filename, "$filename.old") or die "could not rename $filename to $filename.old";
    rename("$filename.new", $filename) or die "could not rename $filename.new to $filename";

    return 1;
}

sub mpc2mp3
{
    my ($old) = @_;

    my $new=$old;
    $new =~ s/\.mpc$/\.mp3/i;

    my $tmp="/tmp/$$.wav";

    print "analyzing $old\n";
    my $cmd="mppdec \"$old\" $tmp 2>&1";
    my $out=`$cmd`;

    # search for scale value
    $out =~ /--scale\s+([\d\.]+)\b/;
    my $scale=$1;
    if ($scale>0.0)
    {
	print "found scaling factor $scale\n";

	# decode again
	$cmd="mppdec --scale $scale \"$old\" $tmp";
	my $r=system($cmd)/256;
	if ($r>0)
	{
	    print "error while decoding $old\n";
	    unlink $tmp;
	    return 0;
	}
    }

    # get tags
    $cmd="apetag \"$old\"";
    my %tag;
    open(A, "$cmd |") or die "could not $cmd";
    while (<A>)
    {
	chomp;
	/(.+?):(.+)/;
	my $key=lc $1;
	$tag{$key}=$2;
    }
    close(A);

    $cmd="lame -h -b $bitrate -m $mode $tmp \"$new\" ";
    $tag{title} =~ s/"//g;	#"
    $cmd.="--tt \"$tag{title}\" " if $tag{title};
    $tag{artist} =~ s/"//g;	#"
    $cmd.="--ta \"$tag{artist}\" " if $tag{artist};
    $tag{album} =~ s/"//g;	#"
    $cmd.="--tl \"$tag{album}\" " if $tag{album};
    $tag{year}+=0;
    $cmd.="--ty \"$tag{year}\" " if $tag{year};
    $tag{comment} =~ s/"//g;	#"
    $cmd.="--tc \"$tag{comment}\" " if $tag{comment};
    $cmd.="--tg \"$tag{genre}\" " if $tag{genre};
    $tag{track}+=0;
    $cmd.="--tn \"$tag{track}\" " if $tag{track};

    $cmd.="--id3v1-only ";

    # encode
    my $r=system($cmd)/256;
    if ($r>0)
    {
	print "error while encoding $new\n";
	unlink $tmp;
	return 0;
    }

    unlink $tmp;

    return 1;
}

sub wav2mp3
{
    my ($old) = @_;

    my $new=$old;
    $new =~ s/\.wav$/\.mp3/i;

    my $cmd="lame -h -b $bitrate -m $mode \"$old\" \"$new\"";
    my $r=system($cmd)/256;
    if ($r>0)
    {
	print "error while encoding $old\n";
	return 0;
    }
    return 1;
}

sub wma2mp3
{
    my ($old) = @_;
    my $wav=$old;
    $wav =~ s/\.($mplayerExt)$/\.wav/io;

    my $cmd="mplayer -vc null -vo null -ao pcm:fast -ao pcm:waveheader '$old'";

    print "$cmd\n";
    my $r=system($cmd)/256;
    if ($r>0)
    {
	print "error while converting $old -> $wav\n";
	return 0;
    }
    rename("audiodump.wav", $wav);

    my $r=wav2mp3($wav);

    unlink $wav;
    return $r;
}

sub ogg2mp3
{
    my ($old) = @_;
    my $new=$old;
    $new =~ s/\.ogg$/\.mp3/i;

    my $ogg = Ogg::Vorbis::Header->new($old);
    my $info=$ogg->info();

    if($info->{channels}==2 && $mode eq 'm') { $mode="s -a" }
    my $cmd="oggdec -Q -o - -b 16 -e 0 -R -s 1 \"$old\" | lame -r -h -x -s $info->{rate} --bitwidth 16 -b $bitrate -m $mode - \"$new\"";

    # TODO: convert ogg tags to id3v1

    print "$cmd\n";
    my $r=system($cmd)/256;
    if ($r>0)
    {
	print "error while converting $old -> $new\n";
	return 0;
    }
    return 1;
}

sub help
{
    print "mp3recode.pl [-m] [-s|-j] [-000] [--encode] <files>\n";
    print "-m        encode to mono\n";
    print "-s        encode to joint-stereo\n";
    print "-000      bitrate to encode\n";
    print "--encode  give this switch to start reencoding\n";
    print "-r        recurse into subdirectories\n";
    exit 1;
}
