#!/usr/bin/perl
use strict;
use warnings;
use threads;
use Thread::Queue;
use Thread::Semaphore;
use YAML::XS qw(LoadFile);

my $d = LoadFile 'data.yml';
my $encoding_threads = 3;
my @drives = qw(/dev/cdrom0 /dev/cdrom1 /dev/cdrom2);

my $semaphore = new Thread::Semaphore;

print "--- Ripping \"$$d{title}\" ---\n";

my $encode_queue = Thread::Queue->new();
my @enc_thr; # Holds speex process threads
for (1..$encoding_threads) {
	my $thr = async {
		while (defined(my $encodejob = $encode_queue->dequeue())) {
			&encode($encodejob);
		}
	};
	push @enc_thr, $thr;
}

my $read_queue = Thread::Queue->new();
$read_queue->enqueue( $$d{starting_disc}..$$d{num_discs} );
$read_queue->enqueue( (undef) x (scalar @drives) );
my @read_thr;
for my $drive (@drives) {
	my $thr = async {
	    while (defined(my $disc = $read_queue->dequeue())) {
			&read($drive, $disc);
			$encode_queue->enqueue($disc);
		}
	};
	push @read_thr, $thr;
	&small_sleep(0.1);
}

$_->join for (@read_thr);
$encode_queue->enqueue( (undef) x $encoding_threads );
print "Waiting for background encoding threads to finish...\n";

$_->join for (@enc_thr);
`eject $_` for (@drives);
print "Done\n";

sub read {
    my ($drive, $disc) = @_;
    my @track_groups = split /\n/, $$d{track_groups};
    $semaphore->down;
	print("Please insert disc $disc\n");
    qx(eject $drive);
	# Wait until disc inserted:
    sleep 1 while (&rip($drive, "-1:[1.1]", "/dev/null"));
    $semaphore->up;
    #sleep 2;
    for my $t (@track_groups) {
		sleep 1 while (&rip($drive, $t, "$disc.$t.wav"));
	}			
}


sub rip {
	my ($drive, $tracks, $filename) = @_;
	open STDERR, ">>/dev/null";
	return system("cdparanoia", "-q", "-Z", "-d", "$drive", "--", 
		   "$tracks", "$filename");
	close STDERR;
}


sub encode {
    my ($disc) = shift(@_);
	for my $aref (&meta($disc)) {
		my ($filename, $spx, @comments) = @$aref;
		&downmix($filename);
		&normalize($filename);
		&resample($filename);
		&speex(@$aref);
	}
}


# Takes $disc as input, returns .wav filename, .spx filename,
# followed by list of comments as an arrayref for each track group.
sub meta {
    my ($disc) = shift(@_);
	my @list = split /\n/, $$d{chapters};
	my @track_groups = split /\n/, $$d{track_groups};
	my $num_lectures = sprintf("%02d", scalar @list);
	my $outdir = "$$d{title} ($$d{year})";
	my @output_array;
    for my $i (0..@track_groups) {
		my $lecture_num = sprintf("%02d", ($disc-1)*@track_groups + $i + 1);
		my $filename = "$disc.$track_groups[$i].wav";
		my @comments = ("Title=$list[$lecture_num -1]", 
						"Artist=$$d{author}", 
						"Album=$$d{title}", 
						"Date=$$d{year}", 
						"Track Number=$lecture_num of $num_lectures",
						"Genre=Speech",
						"Comment=$$d{comment}");
		mkdir("$$d{title} ($$d{year})");
		my $spx = "$$d{title} ($$d{year})/" .
				  "($lecture_num.$num_lectures) $list[$lecture_num - 1].spx";
		push(@output_array, [$filename, $spx, @comments]);	
	}
	return @output_array;
}
		

sub speex {
		my ($filename, $spx, @comments) = @_;
		my @cmd = qw(nice ionice -c3 speexenc --vbr --dtx);
		for my $c (@comments) {
			push(@cmd, ("--comment", "$c"));
		}
		push(@cmd, ("$filename", "$spx"));
		my $e = system(@cmd);
		unlink("$filename") if (! $e);
		return $e;
}


sub downmix {
	my $filename = shift(@_);
	my $e = system("nice", "sox", "$filename", 
				   "mono_$filename", "channels", "1");
	rename("mono_${filename}", "$filename");
	return $e;
}		


sub normalize {
	my ($filename) = shift(@_);
	my $e = system("nice", "normalize-audio", "-q", "$filename");
	return $e;
}


sub resample {
	my $filename = shift(@_);
	my $e = system("nice", "ssrc", "--quiet", "--twopass", "--rate", 
				   "32000", "$filename", "resample_$filename");
	rename("resample_${filename}", "$filename");
	return $e;
}	


sub small_sleep {
	select(undef, undef, undef, shift(@_) );
}
