Wrote an mp3 music playing server that can be remotely controlled via TCP
connections using perl and MPEG::MP3Play. Ah how easy it can be to write things in glue languages. git-svn-id: https://samskivert.googlecode.com/svn/trunk@108 6335cc39-0255-0410-8fd6-9bcaacd3b74c
This commit is contained in:
Executable
+26
@@ -0,0 +1,26 @@
|
|||||||
|
#!/usr/bin/perl -w
|
||||||
|
#
|
||||||
|
# $Id: append,v 1.1 2001/03/22 02:41:36 mdb Exp $
|
||||||
|
#
|
||||||
|
# Appends the supplied song or songs to the playlist of a running MP3
|
||||||
|
# daemon.
|
||||||
|
|
||||||
|
use strict;
|
||||||
|
use IO::Socket;
|
||||||
|
|
||||||
|
my $usage = "Usage: $0 mp3_file [mp3_file ...]\n";
|
||||||
|
my $path = shift or die $usage;
|
||||||
|
|
||||||
|
my $port = 2500;
|
||||||
|
my $sock = new IO::Socket::INET(PeerAddr => "localhost",
|
||||||
|
PeerPort => $port,
|
||||||
|
Proto => "tcp");
|
||||||
|
|
||||||
|
do {
|
||||||
|
# send the command
|
||||||
|
print $sock "APPEND $path\n";
|
||||||
|
# print the response
|
||||||
|
print scalar <$sock>;
|
||||||
|
} while ($path = shift);
|
||||||
|
|
||||||
|
close $sock;
|
||||||
Executable
+37
@@ -0,0 +1,37 @@
|
|||||||
|
#!/usr/bin/perl -w
|
||||||
|
#
|
||||||
|
# $Id: mpcmd,v 1.1 2001/03/22 02:41:36 mdb Exp $
|
||||||
|
#
|
||||||
|
# Appends the supplied song or songs to the playlist of a running MP3
|
||||||
|
# daemon.
|
||||||
|
|
||||||
|
use strict;
|
||||||
|
use IO::Socket;
|
||||||
|
|
||||||
|
my $usage = "Usage: $0 COMMAND [ARGUMENT ...]\n";
|
||||||
|
my $command = shift or die $usage;
|
||||||
|
|
||||||
|
my $port = 2500;
|
||||||
|
my $sock = new IO::Socket::INET(PeerAddr => "localhost",
|
||||||
|
PeerPort => $port,
|
||||||
|
Proto => "tcp");
|
||||||
|
|
||||||
|
# send the command
|
||||||
|
print $sock "$command " . join(" ", @ARGV) . "\n";
|
||||||
|
|
||||||
|
# PLAYLIST has a multiline response, others are single line
|
||||||
|
if ($command eq "PLAYLIST") {
|
||||||
|
my $rsp = <$sock>;
|
||||||
|
print $rsp;
|
||||||
|
if ($rsp =~ m/200 Playlist songs: (\d+)/) {
|
||||||
|
my $i;
|
||||||
|
for ($i = 0; $i < $1; $i++) {
|
||||||
|
print scalar <$sock>;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
print scalar <$sock>;
|
||||||
|
}
|
||||||
|
|
||||||
|
close $sock;
|
||||||
Executable
+166
@@ -0,0 +1,166 @@
|
|||||||
|
#!/usr/bin/perl -w
|
||||||
|
#
|
||||||
|
# $Id: musicd,v 1.1 2001/03/22 02:41:36 mdb Exp $
|
||||||
|
#
|
||||||
|
# MP3 audio server. Manages playlists of mp3 files that live on a local
|
||||||
|
# filesystem. The playlsits can be manipulated by connecting to the server
|
||||||
|
# on a port and issuing simple text commands.
|
||||||
|
|
||||||
|
use strict;
|
||||||
|
use IO::Socket;
|
||||||
|
use IO::Select;
|
||||||
|
use MPEG::MP3Play;
|
||||||
|
|
||||||
|
# this is what's currently being played
|
||||||
|
my $playing;
|
||||||
|
# this is our primary playlist
|
||||||
|
my @playlist;
|
||||||
|
# this is our current index into the playlist
|
||||||
|
my $index = -1;
|
||||||
|
# this is our handle on the MP3 player object
|
||||||
|
my $mp3;
|
||||||
|
|
||||||
|
main: {
|
||||||
|
# create the server part of things
|
||||||
|
my $sock = new IO::Socket::INET(LocalHost => "localhost",
|
||||||
|
LocalPort => 2500,
|
||||||
|
Proto => "tcp",
|
||||||
|
Listen => SOMAXCONN,
|
||||||
|
Reuse => 1);
|
||||||
|
$sock or die "Unable to create server socket: $!\n";
|
||||||
|
|
||||||
|
# create our MP3 interface
|
||||||
|
$mp3 = new MPEG::MP3Play;
|
||||||
|
|
||||||
|
# manage our various inputs
|
||||||
|
my $sel = new IO::Select($sock);
|
||||||
|
my $mph = $mp3->get_command_read_pipe();
|
||||||
|
$sel->add($mph);
|
||||||
|
|
||||||
|
# do our main loop thang
|
||||||
|
my (@ready, $fh);
|
||||||
|
while (@ready = $sel->can_read()) {
|
||||||
|
foreach $fh (@ready) {
|
||||||
|
if ($fh == $mph) {
|
||||||
|
$mp3->process_messages_nowait();
|
||||||
|
|
||||||
|
} elsif ($fh == $sock) {
|
||||||
|
$sel->add($sock->accept());
|
||||||
|
|
||||||
|
} else {
|
||||||
|
# if handle_client() returns false, we remove this client from
|
||||||
|
# the select list and close the socket
|
||||||
|
if (!handle_client($fh)) {
|
||||||
|
$sel->remove($fh);
|
||||||
|
$fh->close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# handles input from a client
|
||||||
|
sub handle_client
|
||||||
|
{
|
||||||
|
my ($cfh) = @_;
|
||||||
|
|
||||||
|
# see what the client has to say
|
||||||
|
my $input = <$cfh>;
|
||||||
|
|
||||||
|
if (defined $input) {
|
||||||
|
# remove trailing cr/nl
|
||||||
|
$input =~ s:[\r\n]+$::g;
|
||||||
|
} else {
|
||||||
|
# bail out if the socket is closed
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
# parse and process the command
|
||||||
|
if ($input =~ m/^APPEND (.*)/) {
|
||||||
|
# append this song to the playlist
|
||||||
|
my $path = $1;
|
||||||
|
|
||||||
|
# make sure the file they provided exists and is readable
|
||||||
|
if (-r $path) {
|
||||||
|
push @playlist, $path;
|
||||||
|
print $cfh "200 Appended '$path' to playlist.\n";
|
||||||
|
} else {
|
||||||
|
print $cfh "404 Unable to read playlist candidate: '$path'\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
# if we're not playing a song, start playing now that we have a
|
||||||
|
# playlist with songs in it
|
||||||
|
if (!defined $playing) {
|
||||||
|
play_next_tune();
|
||||||
|
}
|
||||||
|
|
||||||
|
} elsif ($input =~ m/^CLEAR/) {
|
||||||
|
# clear out the current playlist and reset the playing index
|
||||||
|
@playlist = ();
|
||||||
|
$index = -1;
|
||||||
|
print $cfh "200 Playlist cleared.\n";
|
||||||
|
|
||||||
|
} elsif ($input =~ m/^PLAYLIST/) {
|
||||||
|
print $cfh "200 Playlist songs: " . @playlist .
|
||||||
|
" current: $playing\n";
|
||||||
|
my $path;
|
||||||
|
foreach $path (@playlist) {
|
||||||
|
print $cfh "$path\n";
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
} elsif ($input =~ m/^STOP/) {
|
||||||
|
$mp3->stop();
|
||||||
|
print $cfh "200 Music stopped.\n";
|
||||||
|
|
||||||
|
} elsif ($input =~ m/^PLAY/) {
|
||||||
|
$mp3->play();
|
||||||
|
print $cfh "200 Music started.\n";
|
||||||
|
|
||||||
|
} elsif ($input =~ m/^PAUSE/) {
|
||||||
|
$mp3->pause();
|
||||||
|
print $cfh "200 Music paused (use PLAY to unpause).\n";
|
||||||
|
|
||||||
|
} elsif ($input =~ m/^CLOSE/) {
|
||||||
|
print $cfh "200 Bye bye.\n";
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
print $cfh "500 Unknown command: '$input'\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub play_next_tune
|
||||||
|
{
|
||||||
|
# bail if the playlist is empty
|
||||||
|
if (@playlist == 0) {
|
||||||
|
$index = -1;
|
||||||
|
undef $playing;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
# advance the playlist pointer (wrapping around at the end because we
|
||||||
|
# always loop)
|
||||||
|
$index = ($index + 1) % @playlist;
|
||||||
|
$playing = $playlist[$index];
|
||||||
|
|
||||||
|
# open it
|
||||||
|
$mp3->open($playing);
|
||||||
|
|
||||||
|
# and play it
|
||||||
|
$mp3->play();
|
||||||
|
}
|
||||||
|
|
||||||
|
# message handlers
|
||||||
|
package MPEG::MP3Play;
|
||||||
|
|
||||||
|
sub msg_notify_player_state
|
||||||
|
{
|
||||||
|
my ($mp3, $msg) = @_;
|
||||||
|
# play the next tune if we hit the end of this one
|
||||||
|
main::play_next_tune()
|
||||||
|
if ($msg->{state} == &XA_PLAYER_STATE_EOF);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user