http://qs321.pair.com?node_id=165048

Problem: I have a 40gb HD on an old P/133 running Debian that serves as my mp3 server. It is plugged into the stereo, so I can listen for days without having to mess with the stereo. This is not the problem.

The problem is that half the time I have no clue what I'm listening to. The server has no monitor, so I operate it via ssh, but I'm not *always* at the controlling terminal either. I download a lot of music from emusic.com and occasionally I will borrow a CD (from a friend or the library) and rip it. So my collection of mp3s is significantly larger than my memory.

Now I could use MP3::Info to list the songs to the terminal as they play, but that still requires me to keep an open terminal, and to run to the computer to see what's playing if I want to know. Better would be a way that would allow me to get song information at the same time I get the songs. You know, like on the radio.

Enter Compu Kasem. Through the magic of speech synthesis and a little Perl glue, the cyber DJ accepts a playlist (optional shuffle), announces the songs before and after they play, as well as the time (again, easier than hunting down a clock). Requires the Festival speech synthesizer and uses the 'aumix' audio utility to set the volume. Also, for now, works only with mp3 files (sorry Ogg-philes) via the mpg123 (or mpg321) application/library.

Also, while a song is playing, you can use the following commands:
. increase volume
, decrease volume
(space) skip song
1234567890 set volume to 10% to 100% (in 10% increments)

To do list includes: build set of routines for speech functions, include variety of song introduction/recap phrases, only announcing time in fifteen minute intervals, perhaps grabbing news headlines and/or weather information and inserting those between songs.

Update: of course there are several modules for dealing with Festival servers (i.e. running festival as a daemon rather than piping to it, as I do here). So this is on the to-do list, because it will probably cause less lag (as starting the festival client after before each song is a bit slow).
#!/usr/bin/perl -w use strict; use Audio::Play::MPG123; use MP3::Info; use Term::ReadKey; use Getopt::Long; my $shuffle; GetOptions( "shuffle" => \$shuffle ); my $DEBUG = 1; my $max_vol = 100; my $VOLUME = 70; my $speech_speed = 3; #a single digit between 0 and 9 my @playlist; if( @ARGV ) { @playlist = @ARGV; } else { say_error( "No playlist given. Please select some songs for me to p +lay!" ); } if( $shuffle ) #Shuffle songs in playlist { #fisher-yates shuffle from Perl Cookbook my $array = \@playlist; my $i; for( $i = @$array; --$i; ) { my $j = int rand ($i+1); next if $i==$j; @$array[$i,$j] = @$array[$j,$i]; } } my $key; my( $this_artist, $last_artist ); my( $this_album, $last_album ); my( $this_song, $last_song ); my( $this_year, $last_year ); ReadMode 3; # Turn off controls keys for( @playlist ) { print "Starting song '$_'\n" if $DEBUG; my $song = MP3::Info->new( $_ ); my $this_artist = $song->artist; my $this_album = $song->album; my $this_song = $song->title; my $this_year = $song->year; my $prev_vol = $VOLUME; set_vol( $max_vol ); print "Doing DJ routine\n" if $DEBUG; open( VOICE, "| festival --pipe" ) or die "$!"; print VOICE "(voice_rab_diphone)"; print VOICE "(Parameter.set 'Duration_Stretch 1.$speech_speed)"; if( $last_song ){ print VOICE "(SayText \"That was $last_artist with the song $last_ +song\")"; my $the_time = get_time(); print VOICE "(SayText \"The time is now $the_time\")"; } print VOICE "(SayText \"Here is $this_song played by $this_artist\ +")"; close VOICE; $last_artist = $this_artist; $last_album = $this_album; $last_song = $this_song; $last_year = $this_year; set_vol( $prev_vol ); print "Running player for this song\n" if $DEBUG; my $player = new Audio::Play::MPG123; $player->load( $_ ); my $stopper = 0; until( $player->state == 0 or $stopper) { unless (not defined ($key = ReadKey(-1))) { if( $key =~ /^\d$/ ) { $key = 10 unless $key; set_vol( $key * 10 ); } elsif( $key eq '.' ) { set_vol( $VOLUME + 1 ); } elsif( $key eq ',' ) { set_vol( $VOLUME - 1 ); } elsif( $key eq ' ' ) { print "Skipping to next song\n" if $DEBUG; $player->stop; $stopper = 1; next; } } $player->poll(1); } } ReadMode 0; # Reset tty mode before exiting sub say_error { my $err_msg = shift or die "No error given: $!"; my $prev_vol = $VOLUME; set_vol( $max_vol ); open( VOICE, "| festival --pipe" ) or die "$!"; print VOICE "(voice_rab_diphone)"; print VOICE "(Parameter.set 'Duration_Stretch 1.$speech_speed)"; print VOICE "(SayText \"$err_msg\")"; close VOICE; set_vol( $prev_vol ); } sub get_time { my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime +(time); my $time; if( $min == 0 && $hour == 0 ) { $time = "midnight"; } elsif( $min == 0 && $hour == 12 ) { $time = "noon"; } elsif( $hour < 12 && $min <= 30) { $time = "$min minutes after $hour in the morning"; } elsif( $hour < 12 && $min > 30) { my $bmin = 60 - $min; $hour++; $time = "$bmin minutes before $hour in the morning"; } elsif( $hour == 12 && $min <= 30) { $time = "$min minutes after $hour in the afternoon"; } elsif( $hour == 12 && $min > 30) { my $bmin = 60 - $min; $hour++; $time = "$bmin minutes before $hour in the afternoon"; } elsif( $hour > 12 && $min <= 30) { $hour -= 12; $time = "$min minutes after $hour in the morning"; } elsif( $hour > 12 && $min > 30) { $hour -= 12; my $bmin = 60 - $min; $hour++; $time = "$bmin minutes before $hour in the morning"; } else { $time = "I have no idea what time it is!"; } return $time } sub set_vol { $VOLUME = shift || return; $VOLUME = 100 if $VOLUME >= 100; $VOLUME = 0 if $VOLUME < 0; system( "aumix -v $VOLUME" ); }