photo
Jordan Sissel
geek

Mon, 31 May 2004

pimp, lame, ssh, and streaming!

So since pimp has a cheap shoutcast server thing, I decided to use that to play music while I'm at home.

Playing normal mp3s uses too much bandwidth to waste on my DSL connection (~20-30k/s with 128-160kbit mp3s). So, I decided to use lame as a middleman to down-encode the shoutcasted output from pimp. The following is a little script that uses netcat to connect to the shoutcasty thing aswell as act as a server elsewhere for the down-encoded mp3 data to be sent off.

#!/usr/local/bin/ksh

foo() {
	killall nc
	exit 1
}  

trap foo int 
nc -lp 8081 |&

echo "ICY 200 OK" >&p
echo >&p

nc whack 8080 | lame --mp3input -b 64 - - >&p

I probably don't need to use co-processes, but they're fun, so whatever. Run this script then run mplayer against whatever machine it's on. So what I do is, run the script, then mplayer:

whack(~) > ./projects/pimp/pimp &
whack(~) > ./mp3foo
... over on my laptop ...
nightfall(~) > mplayer http://whack:8081


Poof! Instant mp3 shenanigans. Instead of averaging 24K/s I average 7.5K/s while playing streamed mp3s - what's better is I can still control what song is being played throug pimp's telnet interface and I don't have to waste time downloading every individual song I want to listen to.

Furthermore, here's a nice oneliner to do inbound(download) bandwidth monitoring:

netstat -w 1 | perl -e 'while (<>) { next unless m/^\s+\S+\s+\S+\s+(\d+)/; \
	$b++; $a += $1; print "Total/Avg/Cur = $a / " . ($a / $b) . " / $1\n"; }'

Comments: 0 (view comments)
Tags: , , ,
Permalink: /geekery/115
posted at: 03:20

Mon, 24 May 2004

pimp+shoutcast

So regarding a 6 line feature of pimp I *never* thought I'd ever use and wrote in just because I wanted to see how many lines it would take me to do a shoutcast server-ey thing... I'm using it. I don't have a long enough audio cable to reach my receiver from my desktop, but I do have one that reaches from kenya (my server). Since my desktop has pimp and I'm too lazy to set it up on kenya, I run pimp on my desktop andhave an mplayer instance from kenya connect to it. Crazy useful :)

I'm seriously considering leaving this setup this way. <3 music.

Comments: 0 (view comments)
Tags:
Permalink: /geekery/114
posted at: 01:29

Mon, 03 May 2004

Term Abbreviation Handling and Ambiguity Resolution

So I went along looking into implenting good term abbreviation system for pimp's protocol. I've had previous experience doing this, but it was an utter pain to do. I found two pretty decent ways of doing this in my research.

What is term abbreviation? Let's say that for a program you enter commands on a line. You have a lot of commands, and you get tired of typing the full word for every single thing. Term abbreviation allows you to type a partial command and the system will understand what command you mean. This can be seen on VMS systems, some MUD servers, etc.

For example, let's say there's a command called "status". To execute this command you would normally have to type the full word. This becomes tedius after multiple recurrances. With term abbreviation, you can simply type "s" or "st", for instance, and the system will acknowledge that you really mean "status.

My original implementation went something like this (in perl):

# $string is some word that has been set beforehand.
if ($string =~ m/^s(t(a(t(u(s)?)?)?)?)?$/i) {
	# execute code for the status command
	# ...
} elsif ($msg =~ m/^h(e(l(p)?)?)?$/i) {
	# execute code for the help command
	# ...
} else {
	# unknown command, yadda yadda yadda
}
# ...

Obviously typing all that for a regular expression match allowing abbreviation is not only annoying to type but terribly difficult to change and maintain - you have to count parenthesis and question marks, etc. Ugh! Want more pain? Try using backreferences. Yeah, how about this example trapping extra arguments to the command:

my $string = "help fishing";
#...
} elsif ($string =~ m/^h(e(l(p)?)?)? (.*)$/i) {
	print "User requested help for $4\n";
}

If you aren't familiar with backreferences, $4 refers to the fourth group match in that regular expression - count the open parenthesis and you'll see that the (.*) is the fourth group. This is particularly nasty because for every command you want to grok arguments for (atleast in this particular fashion) you have to count how many groups you have.

So, that being a silly idea, there are atleast two other options we can use. One way is to essentially swap the two parameters in our regex comparison. Instead of seeing if $string is a shortened version version of "status", see if $string matches $status.Check this:

my $cmd = "status";
if ("status" =~ m/^$cmd/) {
	# $cmd is "status" or is a shortened version of it.
	print "Status!\n";
} elsif ("help" =~ m/$cmd/) {
	# $cmd is "help" or is a shortened version of it.
	print "Help!\n";
}

Ok, so, we're good now, right? No painful regular expressions, no painful maintanence when we want to change commands later. Neato. There is one small (major?) flaw in this design, however, it can be easily corrected. What if $cmd somehow contains characters special to regular exprsesions? Your match may fail entirely becuase of the way perl handles variables inside regular exprsesion patterns! Not to fear, there is an easy solution - perldoc perlre states that \Q will "quote (disable) pattern metacharacters till \E." So we make one small change to our expressions:

my $cmd = "status";
if ("status" =~ m/^\Q$cmd\E/) {
	# $cmd is "status" or is a shortened version of it.
	print "Status!\n";
} elsif ("help" =~ m/^\Q$cmd\E/) {
	# $cmd is "help" or is a shortened version of it.
	print "Help!\n";
}

All better. Now it doesn't matter what characters are in $cmd our expression won't fail due to improper syntax. It is extremely important to remember that.

What if you don't like lots of if-elsif-else statements? Ok, there's a solution for you. It involves using hot grep and eval action:

my @commands = qw(status help info);

# Let's say for this example the user tped "st" for "status"
my $input_command = "st";

my ($match) = grep { /^\Q$input_command\E/ } @commands;

eval "&{$match}";

sub status { print "status command called\n"; }
sub help { print "help command called\n"; }
sub info { print "info command called\n"; }

Much much shorter, and is good if you prefer it as such. This also helps split your code into simple functions instead of normal top-down code. One final problem exists: What if there is an ambiguity among two similar commands? That is, there might be two commands that, when shortened too much are the same. For example, there might be a "status" command and a "start" command. What now? Easy fix:

my @commands = qw(status help info);

# Let's say for this example the user typed "st" for "status"
my $input_command = "st";

my @matches = grep { /^\Q$input_command\E/ } @commands;


if (scalar(@matches) > 1) {
	print "The command '$input_command' is ambiguous.\n";
} elsif (scalar(@matches) == 0) {
	print "There is no such command or abbreviation '$input_command'\n";
} else {
	eval "&{$matches[0]}";
}


sub status { print "status command called\n"; }
sub help { print "help command calle\n"; }
sub info { print "info command called\n"; }

*UPDATE* A fellow perl monger by the name of John Resig was kind enough to submit some extra code that also implements term abbreviation. It's a slightly different alternative:

my $in = "help";

my $cmds = {
	status => sub { print "Status!\n"; },
	help => sub { print "Help!\n"; },
	helpme => sub { print "Help Me!\n"; }
};

my @m = grep {/^\Q$in\E/i} keys %{$cmds};

if ( @m > 1 && (!exists $cmds->{$in}) ) {
	print "Ambigious!\n";
} elsif ( @m == 0 ) {
	print "No Such!\n";
} else {
	&{$cmds->{$m[0]}};
}

Whew! Now we have a smart solution that's terribly easy to maintain.

Until next time... later :)

Comments: 0 (view comments)
Tags:
Permalink: /geekery/113
posted at: 12:02

Sun, 02 May 2004

Pimp, topkeys, etc...

Did a bit of coding this weekend. PiMP got some pretty major feature upgrades including stream support (think shoutcast). I still need to work on the server<->player protocol aswell as the client<->server protocol. I've got a list of hot features I'm going to add to it as soon as I get more time including iTunes shares support, non-mp3 multimedia support, a graphical frontend, a curses frontend, etc.

I also made some neat changes to topkeys.sh. Topkeys.sh is a handy script I wrote so I could control XMMS with my media/internet buttons on my microsoft keyboard. I no longer use this keyboard and instead use the number pad (which is generally useless to me normally). With this update I added support for multiple interfaces. The two I have written so far are for xmms and bottlerocket. You can toggle between the two (and a dynamic list of others that I may write.
If you want to give it a shot, you'll need atleast topkeys.sh and xmms.cf, over there on the right. xmms.cf and other moduley things should go in ~/.topkeys/
xmms.cf requires xmms, obviously.
x10.cf requires bottlerocket to be installed, get it from freshmeat.

I also wrote a new perl module this weekend called HTTP::Handle. I needed this to make connecting to shoutcast and other mp3 streams less painful, I think it's a good start. I'll update it with some new features sometime soon.

Comments: 0 (view comments)
Tags: ,
Permalink: /geekery/111
posted at: 03:27

Search this site

Navigation

Metadata

Home About Resume My Code (SVN)

Articles

ARP Security Dynamic DNS with DHCP OpenLDAP+Kerberos+SASL PPP over SSH SSH Security: /bin/false Week of Unix Tools Work Efficiency

Projects

fex firefox tabsearch firefox urledit grok keynav liboverride newpsm (FreeBSD) nis2ldap pam_captcha poor man's backup Solaris audio utility xboxproxy xdotool xmlpresenter xpathtool misc scripts

Presentations

Yahoo! Hack Day '08 Yahoo! Hack Day '06 Unix Essentials Vi/Vim Essentials SSH Tunneling (Video)

Tag Cloud

Calendar

< May 2004 >
SuMoTuWeThFrSa
       1
2 3 4 5 6 7 8
9101112131415
16171819202122
23242526272829
3031     

Friends

BarCamp Kent Brewster Tantek Çelik John Resig Wesley Shields Tyler Shields

Technorati