Search this site


Metadata

Articles

Projects

Presentations

Pulling album covers from Amazon

Amazon provides lots of web services. One of these is it's E-Commerce API which allows you to search it's vast product database (among other things).

In Pimp, the page for any given listening station shows you the current song being played. Along with that, I wanted to provide the album cover for the current track.

You can leverage Amazon's API to search for a given artist and album eventually leading you to the picture of the album cover. To this end, I wrote a little python module that lets you search for an artist and album name combination and will give you a link to the album cover.

So, I wrote albumcover.py as a prototype to turn an artist and album into a url to the album cover image. It works for the 20 or so tests I've put through it.

Streaming data with Pylons

With pimp, I need to serve two kinds of requests. The first, is standard web requests. The second is data streaming requests.

Pylons serves standard requests like a champ. However, I was finding it hard to convince controller methods to send data gradually in a stream. Some brief searching told me that Pylons 0.9.3 supported iterators for data. That is, you could have a controller yield data (using generators) and each yielded piece would be logically sent as it is generated.

However, I was never able to get it to work. I almost gave up when I did yet another search for answers. This search lead me to this thread which explains the solution. In the project config file (development.ini, or whatever), you need to disable debug mode before generators will work properly.

If debug mode is enabled, pylons will wait for data to finish sending (through generators or otherwise) before the data is actually given to the requesting client.

jQuery puffer

The Interface elements plugin for jQuery is super slick. It has a puffer function I want to use. However, the act of 'puffing' makes the element disappear. I want to clone the element and puff the cloned version.
  function magicpuff() {
    $("img").mousedown(function() {
      pos = findPos(this)
      left = pos[0];
      top = pos[1];

      puffer = this.cloneNode(true);
      puffer.style.left = left + "px";
      puffer.style.top = top + "px";
      puffer.style.position = "absolute";
      $(document.body).append(puffer);
      $(puffer).Puff(1000, function() { $(puffer).remove() });

      return false;
    })
  }
  $(document).ready(magicpuff);
This code will duplicate the image clicked placing it directly on top of the old element. It then puffs the new element and removes it when the puff has completed. Simple enough.

What good is code without a fun little demo? View the puffer demo

I should note that it seems that the remove portion doesn't always remove the cloned object. This is especially noticable (though, not visually) when you activate puffing on more than one image at a time. You need somewhat fast hands to do this. Firefox's DOM inspector will show you the additional elements parented by the body tag.

This depends on findPos available from quirksmode, jQuery, and the forementioned Interface plugin.

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"; }'

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.

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.

(?:Perl|Pretty)i?\sMusic\sPlayer and Tic

So I've been busy the past two days working furiously on both tic and pimp. Pimp has been resurrected.

It previously was a silly ncurses frontend to various media players (mplayer, mpg123, xmms, etc). It worked for the most part, but was limited by the dependency on 3rd party programs to run and die on whim. I needed to play mp3s and such from within perl to make this work the way I wanted.

Enter Audio::Ao and Audio::Mad. The former is an interface to libao, a library that talks dirty with your sound devices. The latter is an interface to libmad, a library that decodes mpeg audio decoding.

Right now, I've got a player/server set running. It supports client connections (telnet to port 3242) which supports the following:
   * play/stop/next/pause commands
   * info command, tells you what's playing
   * set command, lets you change the ID3 tag of the currently-playing song.

Everything works so far pretty well. Current problems are:
   * reads the entire mp3 initially, then tosses it to mad. This should be addressed by slowly reading the file (buffered reading) - see below
   * no playlist support! *cry*

So, I decided I had a good idea as to how to read slowly over time, this went along the lines of:
1) Read X bytes, throw it over to Audio::Mad
2) After the stream's empty ($frame->decode($stream) returns -1), repeat step 1

This works, kinda... What happens is you get effective streaming, however there are blips and errors in the sound output. Why, you ask? Because I'm reading a specific number of bytes and completely ignoring the size of each frame in the mp3. What's a frame? Mp3's are split into frames, much like video. If you take half of the data out of a frame, it won't sound right - much as if you were to take half of the data from a video frame, it wouldn't look right.
I have an idea how to read the proper number of bytes so that I always end at the end of a frame, but this will only work for fixed bitrate mp3s; VBR will be another challenge unto itself, but that's a problem to be solved another day.

I don't have streaming incorporated into PiMP yet, but I do have a demo script that does it without concern for the mp3 frames - It's over there on the right.


I also picked up work again on tic, my aim client. It's back up to good 'n working condition. I fixed up a few of the bugs (erroneous output, etc) but found a few more (hurray!). Anyhoo, tab completion now works better.
I'll release tic soon, only a few more features need to be added.