http://qs1969.pair.com?node_id=444560

dabreegster has asked for the wisdom of the Perl Monks concerning the following question:

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.