#!/usr/athena/bin/perl
# Play the data on STDIN as an MP3
#
# TODO
# ----
# Acl the queue
# Accept and handle playlists, ogg files, etc.
# Deal with newer ID3v2 tags properly.
# Make this structured code. It's a mess.
#
# Jered Floyd <jered@mit.edu> takes very little credit for this code

use Getopt::Std;

# Get the MP3Info module from this directory, because I suck.
unshift(@INC, "/usr/local/bin");
require MP3::Info;
import MP3::Info;
&use_winamp_genres();

# Attach infoagents outland
system("/bin/athena/attach -n infoagents outland 2>&1 > /dev/null");

# Select the correct output device and set the volume
system("/mit/outland/bin/audio_setdevice -out headphones -volume 100 2>&1 </dev/null > /dev/null");

# The command line we get from lpd is (no spaces between options and args):
#  -C lpr -C class
#  -A LPRng internal identifier
#  -H originating host
#  -J lpr -J jobname (default: list of files)
#  -L lpr -U username
#  -P logname 
#  -Q queuename (lpr -Q)
#  -a printcap af (accounting file name)
#  -d printcap sd entry (spool dir)
#  -e print job data file name (currently being processed)
#  -h print job originiating host (same as -H)
#  -j job number in spool queue
#  -k print job control file name
#  -l printcap pl (page length)
#  -n user name (same as -L)
#  -s printcap sf (status file)
#  -w printcap pw (page width)
#  -x printcap px (page x dimension)
#  -y printcap py (page y dimension)
# accounting file name

# All the filter_options from lpd
getopt('ACFHJLPQRZacdefhijklnprswxy', \%opts);

# Status messages at start of playback
open(ZEPHYR, '|/usr/athena/bin/zwrite -d -n -c sipb-auto -i sipbmp3 -s "SIPB LPR MP3 spooler"');
print(ZEPHYR "User $opts{'n'} on host $opts{'H'} is playing:\n");

# So, the file we're currently processing is "-d/-e". 
# Do some magic to make sure it's an MP3, and get the important bits
# to zephyr out.
$magic0='';
open(DATAFILE, "$opts{'d'}/$opts{'e'}");
sysread(DATAFILE, $magic, 2);
close(DATAFILE);

# MPEG header is beshort &0xffe0
($magic0, $magic1) = unpack("C2", $magic);
if ((($magic0 & 0xff) == 0xff) &&
    (($magic1 & 0xe0) == 0xe0)) {
    # MPEG audio file
    &play_mpeg_audio(\%opts);
} else {
    printf(ZEPHYR "I don't think this is an MPEG audio file... %02x %02x\n", 
	   $magic0, $magic1);
    print ZEPHYR "I'm going to try playing it anyway, because I'm not smart enough to do\nanything else yet.\n";
    &play_mpeg_audio(%opts);
}


# Play an MPEG audio file
sub play_mpeg_audio {
    # Retrieve those command line opts.
    my %opts = %{shift(@_)};

    my %MPEGModes = ( 0 => "stereo",
		      1 => "joint-stereo",
		      2 => "dual-channel",
		      3 => "single-channel");

    # If it's an MP3 file, try to extract useful data
    my $tag = get_mp3tag("$opts{'d'}/$opts{'e'}");
    if (!$tag) {
	print ZEPHYR "No ID3 tag found\n";
	print ZEPHYR "Filename: $opts{'J'}\n\n";
    } else {
	printf(ZEPHYR "Title  : %-30.30s  Artist: %-30.30s\n", 
	       $tag->{TITLE}, $tag->{ARTIST});
	printf(ZEPHYR "Album  : %-30.30s  Year  : %-4.4s\n", 
	       $tag->{ALBUM}, $tag->{YEAR});
	printf(ZEPHYR "Comment: %-30.30s  Genre : %-30.30s\n\n",
	       $tag->{COMMENT}, $tag->{GENRE});
    }
    
    my $info = get_mp3info("$opts{'d'}/$opts{'e'}");
    if (!$info) {
	print ZEPHYR "No MPEG header found\n";
    } else {
	print ZEPHYR "MPEG $info->{VERSION} layer $info->{LAYER}, ";
	if ($info->{VBR}) {
	    print ZEPHYR "VBR ";
	}
	print ZEPHYR "$info->{BITRATE} kbit/s, $info->{FREQUENCY} kHz ";
	print ZEPHYR $MPEGModes{$info->{STEREO}};
	print ZEPHYR "\n\n";
	printf ZEPHYR "Track length: %02d:%02ds\n", $info->{MM}, $info->{SS};
    }
    close(ZEPHYR);
    
    # Play the file
    # mpg123 is a crock.  If you don't give it -q, it needs to be on a pty 
    # or it SEGVs. Really.
    system("/mit/infoagents/bin/mpg123 -q - 2>&1 /tmp/mpg123.out");

    # Done. Status:
    open(ZEPHYR, '|/usr/athena/bin/zwrite -d -n -c sipb-auto -i sipbmp3 -s "SIPB LPR MP3 spooler"');

    # Check if there were any errors
    open(MP3STATUS, "/tmp/mpg123.out");
    if (<MP3STATUS>) {
	print ZEPHYR "Playback completed with the following errors:\n";
	print ZEPHYR $_;
	while (<MP3STATUS>) {
	    print ZEPHYR $_;
	}
    } else {
	print ZEPHYR "Playback completed successfully.\n";
    }
    close(MP3STATUS);
    unlink(MP3STATUS);

    close(ZEPHYR);
}
