I'm trying to write a relatively simple music-playing server with queue features, similar to Moosic or MPD. Jigstar Music Daemon, or JMD, simply listens for an ALRM signal and executes the commands in a file. It's different because it can play a song, pause it, play another, then resume the first when the second is finished. It can do this in a pretty much infinite manner. Operations will only occur to the most recent version of MPG321. (the program I'm using to actually play the songs.)
I know MPG321 has a remote interface and that there's a POE module for controlling it, but it'd take a good deal more work because it can only play one song at once. I'm not experienced enough yet to construct a program that can do a real music queue yet. So I'm sticking with my current design. Here's the code of JMD. Be warned, it's not pretty and it doesn't run under strict.
#!/usr/bin/perl
########################
# Jigstar Music Daemon #
########################
#> #> = Temporary comment.
#> Check for lock file to see if another instance of JMD is running.
die "Error: Lock file exists!" if -e "/var/lock/jmd";
open LOCK, ">/var/lock/jmd";
print LOCK $$;
close LOCK;
#> Check to see if ESD is running. If not, start it.
#> Remember, esdctl returns 1 if ESD isn't running.
system("esdctl serverinfo >> /dev/null") and system("esd -nobeeps &")
+;
#> Exit cleanly when told to do so.
$SIG{INT} = sub {
#> Do I need to force my children to die so they won't become zombie
+s?
unlink "/var/lock/jmd", "/tmp/jmd";
system("killall esd");
exit;
};
$SIG{QUIT} = $SIG{INT};
#> Use CHLD to simulate the 'done' thing.
#> Declare subroutines.
sub Play ($);
sub Next ();
sub Pause ();
sub Resume ();
sub Stop ();
sub Debug ();
#> When woken up by JMC, read the input and execute the commands.
$SIG{ALRM} = sub {
return unless -s "/tmp/jmd";
open INPUT, "/tmp/jmd";
chomp($Cmd = <INPUT>);
close INPUT;
open INPUT, ">/tmp/jmd";
print INPUT undef;
close INPUT;
$Cmd =~ s/^play // and Play $Cmd if $Cmd =~ m/^play /;
Next if $Cmd eq "next";
Status == 1 ? Pause : Resume if $Cmd eq "toggle";
Pause if $Cmd eq "pause";
Resume if $Cmd eq "resume";
Stop if $Cmd eq "stop";
Debug if $Cmd eq "status";
$SIG{QUIT}->() if $Cmd eq "quit";
};
our @PIDs;
sub Play ($) {
my $Args = shift;
Pause;
#> Have MPG321 get all files from /root/mp3. Make it configurable lat
+er.
#> Change STDERR receiver.
if (my $PID = fork()) {
push @PIDs, $PID;
} else {
chdir "/root/mp3/";
exec("mpg321 -v $Args 2> /dev/tty12 &");
}
}
sub Next () {
#> If it's stopped, resume it temporarily to change tracks.
if (Status() == 2) {
Resume;
kill "INT", $PIDs[-1] if @PIDs;
}
kill "INT", $PIDs[-1] if @PIDs;
}
sub Pause () {
kill "STOP", $PIDs[-1] if @PIDs;
}
sub Resume () {
kill "CONT", $PIDs[-1] if @PIDs;
}
sub Stop () {
kill 9, $PIDs[-1] if @PIDs;
pop @PIDs;
}
sub Debug () {
my $Counter = 0;
for my $PID (@PIDs) {
#> Print the ID, the PID, and the status. Don't need to check if not
+hing is
#> there, because there has to be.
print "$Counter: $PID = " . Status($PID) . "\n";
$Counter++;
}
}
#> Return status of a PID. 0 = Isn't running, 1 = Playing, & 2 = Pause
+d.
sub Status ($) {
my $PID = shift || $PIDs[-1];
my $Line = +(split(/\n/, `ps $PID`))[1];
if ($Line !~ m/$PID/) {
return 0;
} elsif ($Line =~ m/T</) {
return 2;
} else {
return 1;
}
}
sleep while 1;
To control it, echo a command to /tmp/jmd and send the ALRM signal to JMD. The commands should do the following:
play ARGS: Pass args to a new MPG321 instance. If the current instance is running, pause it.
Next: Advance the songs.
Pause: Force the current song to pause.
Resume: Force the current song to resume.
Toggle: If paused, resume, and vice versa.
Stop: Stop the current instance.
Debug: Print the list of PIDs and their status.
Usually pausing a song, playing another, then resuming the first won't work under Linux using OSS. The dsp device is locked. So I run esd to mix the silence of the paused songs and the music of the running song.
The problem is simple: Pausing, resuming, stopping, and advancing to the next track won't work. Every function that sends a signal to the current MPG321 instance doesn't do what it should. There are no errors.
I know the methods I am using are messy, but I also now that they work. An older version of JMD that didn't use fork() to start mpg321 did work. I cannot find the difference between the two.
Any help, suggestions, or code is much appreciated. I can also be found on #perl on irc.freenode.net generally, under the alias 'dabreegster'. Thanks.