Friday, September 22, 2006

Putting My Money Where My Mouth Is

A couple of months ago I was pontificating about, among other things, how UMD movies were failing in the marketplace, and how I believed it was purely due to the poor price point. Well, I was in Future Shop last weekend, and it turns out they have a bunch of UMDs on sale for $8.99, and not only are some of them not crap, they even ones I'm interested in. I'd call nine bucks cheap enough that I'm willing to bite, so I bought Robots and X-Men. I've only watched a little of Robots so far, but I'm pretty impressed by the disc. It has a couple of previews, extras, subtitles and even the French soundtrack (it's the Canadian release). That's pretty much what I'd expect on a DVD.

As it turns out, they're all Twentieth Century Fox releases. I don't know how they came to be priced so reasonably, but I'm not going to complain. Of course, even at $9 I'm not going to buy just anything - some of the stuff on UMD is truly terrible (well, IMHO anyway), but as long as they're putting out some decent animation or sci-fi at reasonable prices I'll probably continue to buy.

Labels:

Monday, September 11, 2006

TV Gem #1: Ben 10

I grew up watching Saturday morning cartoons. Back in those days there was no cable, at least not where I was. We didn't even have a proper antenna, just "rabbit ears". I remember getting up at the crack of dawn and spending most of the morning fiddling with that antenna trying to get a decent picture from some station just south of the border (I grew up just a couple of miles north of the US border so I actually had a decent chance of getting something). It wasn't that there was nothing good on the Canadian stations, because often there was (little did I know much of it was just older stuff from the US) but being a young boy I was more interested in action-y shows than comedy.

As I grew older, animation (can't call them "cartoons" any more as it has the connotation that they're just for kids) was harder to find. The Simpsons didn't have their own show yet, but I did discover Robotech. This show seemed to be made for me. The animation quality was amazing, it had a real story, and characters actually died (none of this robot-plane or ejecting from helicopters crap). Only years later did I find out the true roots of the show, and that got me hooked on anime.

Ever since, I've kept my eyes open for weird little TV shows and movies. Sometimes these shows are domestic, like Titan A.E., and sometimes not, like Kaena. They don't even have to be animated - The Returner is a load of fun. There is so much out there that's not North-American produced-for-the-masses that I can't hope to keep up.

Luckily, that's where Chuck comes in. Chuck runs On Location, the first video rental place in Winnipeg to rent only DVDs (the store opened when Blockbuster still rented only VHS tapes). Chuck doesn't carry 50 copies of the latest releases - he might have five if he thinks it will really move. What he does have is an awesome collection of older titles, and an even more awesome collection of independent and foreign titles. If I walk in and say "Hey Chuck, I want to watch something weird" he's always got something. Most recently, he put me on to Nightwatch and Daywatch - Russian vampire movies. If you have seen Ultraviolet and liked the bit with motorcycle (you know the bit I mean) you should definitely check out Daywatch - there's a very similar scene with an RX-8. I don't know who got the idea first, but I'm sure that one inspired the other (at least for that scene). The weird thing is, I can't seem to find a mention of this anywhere on the 'net. Can it really be that no one else has made the connection?

This is all a very roundabout way of getting the my latest find: Ben 10. I can't believe how cool this show is. Follow that link and read about it. Watch it on Cartoon Network if you're in the US, or Teletoon here in Canada. Just watch it. When I saw the ads I was intrigued, but I figured it would likely be a "kid gets super powers and has to hide them from everybody". Nope. Ben has an alien device glom onto him and the first thing he does is run screaming for Grandpa. Maybe he suddenly matures to become the responsible steward of his newly-acquired powers? Heck no, he uses them to play pranks on his geeky cousin. Ben's still a 10-year-old kid, and he acts like one more often than not. I love it. It's a great blend of humour and action. I think this is one of the best animated shows I've seen since Cybersix.

Labels:

Thursday, August 24, 2006

How to Cross-Compile FreeType

I've been using BlogPlatrol to monitor the meagre hits to my page, and among other things it tells me how people got here, and it seems that rather a lot of you are typing things like "cross-compile freetype apinames". I did the same search myself and found out that my off-hand mention of troubles cross-compiling FreeType is the very first result Google reports. Go figure.

So I figure this must be a real problem, and that there's no good solution out there right now. In the interests of attracting even more hits, and maybe even helping out a few people, here's a more detailed description of what you need to do to successfully cross-compile FreeType.

Presumably if you're doing a search similar to the one I mentioned you already have the FreeType sources and a cross-compiler installed, and you've probably even run FreeType's 'configure' script, seemingly successfully. Just to give you the warm fuzzies, here's what I did:

[funaho ~/freetype-2.2.1]% ./configure --host=arm-linux

when you run 'make', everything purrs along until it tries to run 'apinames' and fails. What you have to do at this point is:

[funaho ~/freetype-2.2.1]% cd src/tools
[funaho ~/freetype-2.2.1/src/tools]% gcc apinames.c -o apinames
[funaho ~/freetype-2.2.1/src/tools]% cp apinames ../../objs
[funaho ~/freetype-2.2.1/src/tools]% cd ../..
[funaho ~/freetype-2.2.1]% make

make should resume where it left off, trying to run apinames, only this time it will succeed. Hope that helps folks.

Labels:

Wednesday, July 26, 2006

Comments are Working Again

It seems that for some indeterminate amount of time comments have not been working here. I don't know what was going on over at HaloScan but it looks like it is resolved now. If you post a comment it will actually show up, and the COMMENTS link at the bottom of each posting will reflect the number of comments for that post. I know I have had a few visitors, but I have no idea if anyone has tried to post anything. Haloscan also claims I'll get e-mail notifications if someone posts here, and it's worked once, so with any luck it will continue to function and I can find out if I have any actual readers out there or not.

Labels:

Sunday, July 16, 2006

While I'm on the Subject of Tabs...

When Firefox came out with tabs implemented, one thing was immediately obvious: this would save real estate on my screen. Even at work, where I have two monitors, I don't have enough space to have multiple browser instances open. I have a hex editor and a text editor (Crimson Editor) that both use tabs to handle multiple files, and I've never wanted them to work differently.

What I don't understand is why more applications aren't making use of this new mechanism. Actually, just about every modern spreadsheet does, except that the tabs are pages in the same spreadsheet, rather than different documents. Adobe Acrobat almost gets it right - all documents load into the same window, but you have to use the "Window" menu to switch betweeen them. I'm sure there are many other examples of apps that are halfway there, but what I'm really intested in are the apps that are all the way there. GAIM, for example. I would love to see tabs in OpenOffice.org.

Yesterday I had the functionality to support tabs in wmamp working, but the visuals weren't there. Instead only the name of the active tab was shown. I spent some time today implementing some general-purpose tab routines, and I think I spent almost as much time getting them to look right today as I spent making them work yesterday. This is probably why I don't like UI programming very much. What I considered to be the "interesting" code was already done and working, having "pretty tabs" (okay, pretty is relative here) is just so much chrome. But it's done now, and I likely won't look at it again unless someone decides to make me some nice graphical buttons to replace the text. And I would appreciate said graphics. In terms of audio playback functionality I believe that wmamp now at least matches (and possibly exceeds) the functionality of the original Linksys player. It does not, however, look as nice. A fellow by the name of Ben Frost (for whom I have no contact information) did the original wmamp logo, and I quite like it. I'd love to have tab graphics with a similar look, but until someone can hand them to me implementing something like that will be a low priority task.

In the mean time, I'm going to rework the listview code. I've been able to determine that in almost every case the items fed into the listview are already sorted the way they should be. listview jumps through all kinds of hoops to do things like moving to the previous or next element (never mind paging by 10). This code would be so much cleaner (not to mention leaner) if it used arrays, so I figure I'll take a stab at that. The only other outstanding features I can think of then are "play once/loop" and a shuffle feature. Okay, so Linksys' software has those features and we don't, so I guess we really don't have feature parity yet.

Once those are done, I think that iTunes playback is essentially complete. After that is the next project I'm keen to work on: enabling SHOUTcast and icecast playback.

Labels:

Saturday, July 15, 2006

Who is the Muse of Programming?

I believe I was visited by the Muse of Programming today. I've been adding all these cool new functions to wmamp but I couldn't think of a good way to make them available without requiring the user to do even more navigation (at least in some cases). I've been struggling with this for days now because I had this nagging feeling in the back of my head that there was a way to do this and make the "action tree" shallower(?) instead of deeper. I know this feeling - it's very similar to the feeling I get when I am looking for a bug and know I should be able to see it but can't. The thing to do in those situations is walk away and do something else for a while. I have been hoping that eventually the answer would come to me, and it looks like maybe it has.

It's so simple (now that I have thought of it), and I can't believe it didn't occur to me earlier. After selecting a server, the next screen has four "tabs" at the top: Playlists, Artists, Albums, and Genres. The left and right arrows move between these, and the rest of the screen is populated with items based on the active tab. If you have UI experience, you can start laughing now, but this is a real breakthrough for me.

Besides, I was right: this does make the tree shallower. The tabs populate quickly, even though I'm dumping the contents of the old tab and loading the new tab every time left or right is pressed. If this becomes an issue with large song collections, it should be possible to keep the lists in memory to further speed up the process. The typical scenario is now to select a server, select a category, then confirm your songs. That's only three screens, instead of the four it used to be, and a far cry from the five screens I had anticipated might be required in some cases. I figure that the worst case would be if you selected an artist, and then an album by that artist, and then confirmed the songs. This would be four screens, but that selection method isn't available right now (selecting an artist shows all their songs immediately, there's no capability to do album selection by artist), and it's probably not necessary anyway given that album selection can be done directly. I can only envision this being desirable if the user wants a specific album but only knows the artist and not the name of the album they're interested in. Is this a scenario that's likely to occur often (or at all)?

I'm pretty happy with how this turned out. It was remarkably easy to put together and it doesn't break from the old interface too badly. The one thing that has changed is that on the "tab" screen the left button is no longer a synonym for the "previous" button, and right isn't a synonym for "select". I think the added functionality is worth it, but I should make the behaviour consistent across all screens as much as possible.

Labels:

Thursday, July 13, 2006

Browsing is Nifty

Genre browsing works now, well, at least as well as it can work when most files don't contain any genre information, or worse, contain mis-spelled genre information. I did manage to find a rather handy tool: mp3tag, which thankfully can tag more than mp3s (eg. oggs). Check out the website to see all the different formats it can handle.

I discovered that my "new" deadlock that I'd found yesterday isn't actually new - an old deadlock bug just had new symptoms because I'd fixed it incorrectly. I think I finally have it fixed now (at least, until the next time).

I really like the way that the functionality is shaping up, the problem now is that there's no good way to access all these new functions. I could insert a new screen after server selection, but this means that the worst-case scenario involves traversing five screens before you make a music selection. There has to be a way to flatten this out.

Labels:

Wednesday, July 12, 2006

I can Browse Artists. Cool.

Browsing by artist actually works, and appears to work with both mt-daapd and iTunes. It should now be trivial to enable browsing by album and genre. "Browsing" used to be done the BFI way (ie. get a list of all songs and throw out the ones we're not interested in) which is perfectly valid, especially when more sophisticated mechanisms are not well (or at all) documented. For large collections of audio files sending browse requests to the DAAP server pushes all the "heavy lifting" over to the server, which probably runs faster and just generally has more resources than the WMA11b. Between browsing and smart playlists it should be possible to cut down on network traffic in many cases. What I have noticed though, is that iTunes sends a sorted list of, say, artists, which is not great for our binary tree sorting code. Sorting by adding items to a binary tree can be very cheap, but it presumes that the data being added is unsorted. Adding already-sorted items to the list is a worst-case scenario that causes the code to do the maximum amount of work.

It remains to be seen how much of the mt-daapd documentation I found yesterday can be used with the DAAP protocol. Basic browsing appears to be supported, but mt-daapd also allows for browsing with filtering (ie. browse all albums by a specified artist). It would be nice to be able to use these as well, but if they do not work with iTunes or other DAAP servers then there's a diminishing reward for implementing them.

For now I think I will content myself with implementing the browsing that I am confident will work across platforms, and then addressing issues like stability and performance. I saw tonight another deadlock of some sort, where I stopped the song that was playing and then tried to cue up a new set of songs, but nothing happens. Playback appeared to be impossible at this point. I thought I had found all this problems, but apparently not.

Labels:

Tuesday, July 11, 2006

Playlists are Working, Browsing is Next

I've been able to debug and prove out my playlist code by temporarily replacing the artist selection code with playlist selection, so that's pretty cool. The DAAP server I'm running - mt-daapd - has all kinds of support for iTunes-style smartlists, which are composed on-the-fly based on criteria you provide. If your mp3s (or oggs) have appropriate meta-data, like the year the song was released or the genre, you can include songs in smartlists based on these attributes. There also seems to be a top-level playlist created with the server's name that includes all songs, so that makes some of the work I did earlier redundant. Not that I'm going to complain about that, especially if it means that I get to rip out some code.

Also, I've found some kind of documentation of the "browse" capabilities here, although this seems to be documentation on how to get XML responses from mt-daapd, not actual DAAP output. I'm hoping that similar requests can be made over DAAP. I can't believe they only showed up on the second page of my google search. With any luck this will mean that it should be possible to do album and artist browsing (and other kinds too) without having to suck in details about every song that the DAAP server knows about. If it works wmamp is going to become way more usable.

Labels:

Monday, July 10, 2006

Finding Bugs is Hard

Debugging is twice as hard as writing the code in the first place. Therefore, if you write the code as cleverly as possible, you are, by definition, not smart enough to debug it. -- Brian Kernighan

With thorough abuse comes success. After finding and fixing one deadlock condition I was able to find a second: start a song playing, press stop, then pause. After you've done this, you can't play songs any more. Easy enough to fix once it's reproducible.

I've done some more work on libmdnsd also. Initially all I wanted to do was make a shared object (the Unix version of a dll) out of it, but I found that some of its output was annoying me. For some reason the original mdnsd code would return network addresses and ports in the processor's native format (typically little-endian). I'm not sure why this was done as it makes displaying an IP address into a programming exercise (albeit a simple one) rather than a function call (inet_ntoa - found in any decent libc implementation). The other thing is that this makes results returned by the original mdnsd different from most other name resolution services, so I decided to fix it. IP addresses are returned in a proper structure and ports are big-endian. Other data returned should probably also be big-endian, but I'll deal with that when I (or someone) actually tries to use them.

"Fixing" libmdnsd naturally required changes to be made to wmamp, mostly for the better I think. Server information is stored a little more compactly (though I think it could still be improved - it's not just every cycle that counts - every byte does too) and on the whole I think it is a little more readable. So lots of changes under the hood there, but no real change in functionality. I didn't break anything (for long) so I can't complain.

I've been meaning to get playlists working, so I took a stab at it last night and I'm most of the way there. I can query the server for all playlists, and I think I can query the songs in the playlist, but I haven't been able to try it yet because I've run into an interface problem. wmamp's interface was originally strictly hierarchical: select a server, an artist, an album, then a song. I've since changed the interface a little so that the process can be short-circuited: you can select the server in such a way that it immediately shows all songs on the server, or you can select an artist such that it shows all their songs, irrespective of the album the belong to (if any). This was, IMHO, better than the previous mechanism - it let me listen to a variety of songs and also leave the player running for an extended period of time without driving me nuts.

My first thought was to show both artists and playlists after a server was selected, and while this is possible wmamp's penchant for sorting will cause artists and playlists to be intermixed and it's not obvious which is which and I feel that is bad. The other thing I want to keep in mind is that some work has already been done to provide a web interface to wmamp with an eye towards controlling music selection when no television is connected.

Implementing playlist support has been a very valuable experience though. I had suspected that memory was still leaking somewhere, and it was, but not at all where I had expected. Entries are sorted by inserting them into a binary tree. It turns out that the code for sorting made two mistakes - it would not always detect duplicate entries (which should not be added to the tree) and when it did detect a duplicate (rarely) it had no way of communicating to its caller that it had not been added to the tree and needed to be free()'d (as data being added to the tree was generally malloc()'d). Both of these have been corrected, and if there is a memory leak now it is much more subtle than what I was seeing previously. I have another suspicion that I haven't had time to check out yet: when any DAAP request is processed it's added into a tree (as above) which sorts the entries based on some simple key. For display, the contents of this tree are moved into a different tree, also keyed, but the key may be more elaborate. If the keys are identical (as in most cases) then the second tree actually becomes a simple linked-list and and not a tree at all. If the additional sorting is not important then it may be faster and more memory efficient to count the items in the tree and malloc() an array to hold the listview items.

Oh, and a note to other wmamp hackers who want to keep the code small: libhttpd appears to be compiled by default with -g (add debugging information) and -O (some optimisation). Unless you are trying to debug this code, rebuild it without -g and use -Os instead of -O. The resulting code is much smaller, and probably faster too. I think this knocked another 10-12kB off the size of wmamp. As I recall, my current builds of wmamp are now around 64kB.

Labels:

Thursday, July 06, 2006

Closer and Closer to Something I Can Use

Only minor improvements to wmamp in the last couple of days. I tracked down a longstanding deadlock issue: pausing a song then returning to the song list and pressing SELECT on any song would kill playback until wmamp will killed and restarted. It turns out that when paused it was looking only for a return to play, and so wasn't handling the situation when you then tried to kill the current play list and load a new one. It was an interesting bug to track down, and I wasn't even in front of the computer while I did it. I've had this happen to me before, it's like I soak up all the information and the while I'm doing something else (like watching Starship Operators) my subconscious finds the answer for me. When the source of the bug popped into my head I just knew it was right, I knew exactly where to look for it and I knew exactly how to fix it. The first time it ever happened I was driving home from work and I almost turned around to go and prove to myself that I've solved the problem.

I also tracked down a new problem I'd only started seeing once I was able to access all the songs on my server - sometimes I'd select a song from the list and it would play, and sometimes the wrong song (but always the same wrong song) would play. It turns out that wmamp was limited to handling 30 songs in a playlist. That's not unreasonable when they're all coming from the same album (as they would have in the past), but it's no longer true with my changes. For now I've upped the limit to 100, but in the long term I have to figure out how to do "browsing", which I'm hoping means querying the DAAP server for songs based on some provided criteria. I wasn't sure if mt-daapd supported it, but from what I've been reading on the project's page it does. Now I just have to figure out how to browse. Thus far I've not been having a lot of luck getting low-level protocol details except through reading source code. Not that I have problem reading source code - working source ultimately tells you what's really happening, but it's not always the easiest to understand.

Labels:

Tuesday, July 04, 2006

Usability. Meh.

"My Sharona" by The Kinks was just playing, and now it's Echo and the Bunnymen's cover of "People are Strange", and it's wmamp that's playing them. This shouldn't be a big deal, but it wasn't something that could be done before. I can only assume that the original interface for music selection was some sort of proof-of-concept or something, because I can't imagine using it myself on a day-to-day basis. Music selection in wmamp has apparently been very simple right from day one: pick a DAAP server, select an artist, select an album, then select a song to start playing. If the song screen lists more than one song it will play all songs from the one selected to the end.

I have only a small selection of songs on my linux server (remember, only 4GB of space on the hard drives), and so I have at most a handful of songs from any one artist, and those are likely from a couple of different albums. Until now this has meant that any time I want to do some testing I end up listening to a handful of songs over and over. Since I'd finally had some luck tuning uClibc and busybox (more on this later) I decided to keep the hacking light for the evening (ha!).

My idea to improve the interface was to provide two methods of selection at the server and artist level: method 1 selects all songs available at that level and method 2 follows the original scheme. The concept is simple, but the execution not so much. The code for making DAAP requests was tied fairly tightly to the original selection mechanism. I also learned something else: whenever wmamp makes a request for a list of songs it receives the entire song list from the server and extracts the ones it wants. I don't know if this is an artifact of the way that the requests are constructed or a limit of DAAP, but it sure would be nice if a specific query could be sent to the server and it returned only matching results. This may actually be possible - I haven't looked at DAAP that closely yet but it looks like I may have to. Anyway, wmamp has a routine that reads in the song descriptions and keeps only those that match the artist and album requested (remember, this is the original code I'm talking about here). This was where the bulk of my work was done - certain search parameters might not be available (or any, if you played all the songs on the server), and it would discard valid songs as non-matches. That wasn't too hard to fix but it was only half the job. All my ogg files have Song Title, Artist and Album Name information, but a lot of the mp3s contain only the name in the id3tag. I've even seen mp3s with no meta-data - only the filename of the mp3 told you what it was. Some songs were being thrown out because of missing album or artist information, and so I had to code up something to deal with crappy mp3 encoders.

There was other work too, but that was the heavy lifting. No point in boring people with all the minutia.

The other thing I managed to do today was pare down the size of busybox and uClibc. Neither got a lot smaller, especially since I'm statically linking busybox, but even so, the image I'm downloading is around 1.1MB whereas the original 0.6 image weighs in at 1.6MB. If I didn't have to deal with the original uClibc it would be even smaller. I really need to replace the disk image that's flashed into the WMA11b itself. If I could get some reasonably modern code in there I'd have a lot more RAM to work with. I'm going to need it unless I can start making smarter DAAP requests.

Labels:

Sunday, July 02, 2006

It's alive. Alive!

Buildroot failed to compile last night, but at least I know why. Debian testing apparently has three different versions of gcc installed: 3.3, 3.4 and 4.0. 4.0 is the default and apparently trying to build gcc 3.4.2 and its support libraries with a new gcc is not something that works. Once I had forced buildroot to use gcc 3.4 I was able to complete the buildroot process. I have to give the uClibc guys credit - the process works pretty well. Once you're done you have uClibc for your target, a complete suite of cross-compiler tools, a working BusyBox and potentially a lot of other goodies if you've enabled them. I did a quick check and out-of-the-box I get a libc that's about 300kB, which is pretty good compared to the 1.1MB of glibc. It's not as good as the one in flash however, which is a measly 189kB. The difference is likely due in part to unnecessary features in the default build and in part to the fact that I built uClibc 0.9.27 while the one in flash is 0.9.12. The same is true for BusyBox - they have 0.60.2 and I've got 1.1.3 (and all kinds of things enabled).

At least I am making progress. All the libraries that wmamp needs recompiled without any problems, and there is only one issue with wmamp itself: it uses strverscmp(), which does some very cool things, but is a GNU extension to glibc. It looks like uClibc should/can support it but so far I haven't been able to link wmamp unless I substitute strcmp() , and for now that is okay.

I've been able to create an image with all the uClibc stuff, but getting it all to work has been tricky. The two versions of uClibc are in no way compatible, and so once I start trying to symlink the new libc in place programs (like busybox) start failing. Since I can't replace the files in flash (yet), I need a way to be able to create all the symlinks to the new libc files without the application doing the symlinking failing. The solution for now is a statically-linked BusyBox. When the image loads the new BusyBox replaces the old one and creates all the necessary symlinks. Once those are in place a new process can be forked that uses dynamically linked programs and they will work as they should. This is a bad hack - the resulting BusyBox comes in at over 500kB, and so just about any uClibc savings are lost. This could be handled better with a purpose-built application, but since this is just a work-around until more of the boot enviroment can be replaced, it hardly seems worth doing. Right now I think the more productive route is to prune BusyBox and uClibc down a little. I know that the default builds for both include many features that have no use on this device.

Labels:

Saturday, July 01, 2006

Taking the Evening Off

On the off-chance that there's been a sudden in-flux of readers from mytinker, there may not be a wmamp/WMA11b update tonight. I'm getting together with a friend for an evening of junk-food, video games and with any luck Final Fantasy VII: Advent Children. Yes, I do in fact have other interests besides hacking - hacking the WMA11b just so happens to be the most blog-able.

I'm hoping that while I am out uClibc buildroot will run to completion and I will have a cross-compiling uClibc environment when I get back. If I do, I'm skeptical that I will be able to wait until tomorrow before I start trying to rebuild wmamp's support libraries and ultimately wmamp itself. Note to others who may try to follow this path: make sure that bash is your login shell, or the build process will fail. I could probably have had this working days ago if I wasn't so enamored of tcsh (no link, because as much as I love it, I don't want to hose someone else).

Labels:

This is How I Measure Success

It's three a.m. and Lee Aaron is playing.

This is a big deal, not because its Lee Aaron, but because something is playing at all. I have successfully ripped Apple's mDNSResponder out of wmamp and replaced it with a version of mdnsd (itself ripped from libopendaap). wmamp is just a little more Free. Not like I'm all militant about Free Software or anything, but I got to kill two birds with one stone here. Not only is mdnsd available under the GPL but it was also designed with embedded systems in mind (so I guess there are still some of us left). Here's how I measure success tonight: wmamp is running, and appears to be working as well as it ever did. (I have to give credit to those who worked on wmamp before me - all its multi-cast DNS code is contained in a single source file, so I wasn't hacking on half a dozen files all evening.) But more than not breaking it, wmamp is now smaller. I have an archived version of wmamp that's timestamped 6/29 19:29 - that would be the one that plays ogg files, but doesn't walk the album's playlist. The stripped binary is 218076 bytes. The canonical version of wmamp 0.6 builds to 213508 bytes. So I added a couple of kB to the program itself, and then dropped a 100kB decoder for oggvorbis in the image as well. The good news tonight is that after all my work wmamp weighs in at a measly 115600 bytes. Switching to mdnsd has reclaimed 102476 bytes. The net result is that wmamp just got ogg playback for free. That's a clear win in my books.

And speaking of walking the playlist, I was pondering the correct behaviour yesterday, thinking that I had probably introduced a bug that caused that last song to play ad nauseum. Today I read in a README or something (damned if I can find it just now) that it's always been like this. I should really get on that. All this other stuff is more interesting to me, but at some point the program's got to be usable as well. My personal goal is that it should be a better application than the original. This is no mean task, the stock software (for the WMA11b itself) is pretty good.

One other thing is starting to drive me nuts as well: I can't make the video look good on my widescreen TV. The output is standard 640x480, and my TV knows all kinds of tricks for stretching a 4x3 image to 16x9, but none of it looks any good. I really want to take a closer look at the video controller and see if I can't make it emit something more appropriate. I've worked with this processor before and I'm pretty sure it can accomodate me.

Labels:

Friday, June 30, 2006

I Need More Heavy Fuel

I thought I didn't have much to report tonight, but upon reflection it seems that I might have been wrong. I found (and fixed) the problem with ogg playback that caused it to play the same song over and over. Then I thought I had a problem when the first file in an album was an ogg file, but it turned out that mt-daapd was having database problems and could no longer find that particular file. Forcing it to update its database entry for that song ("Dire Straits - Calling Elvis") allowed the song to play again. The tip-off was when I logged into the wmaa11b and ran wmamp myself: whenever I tried to play this song it received a 404 from mt-daapd, but the file was clearly present in the directory on the server.

My other small accomplishment for the evening was fixing the basic playback functions. My earlier builds would not pause, stop, back up a track or skip ahead a track. That's working now, which is pretty sweet. I let them wma11b run for a while, playing The Cars Greatest Hits album. I wasn't really paying attention to the music, and then I noticed that it seemed to be playing the last song over and over. And it does. Once the list is complete it just keeps playing the last song. I figured this was probably another bug I'd introduced, but the wmamp 0.6 image that I have does the same thing. I would have expected the playlist to loop, or for play to simply end when the list was exhausted, but neither happens. I wonder what the right behaviour is...

I think I have learned why libopendaap isn't being used by wmamp (a question I was pondering in yesterday's entry): it's more than a little tricky to use, and the memory profile may not fit very well with the wma11b. When you make a song request with libopendaap it writes the data to a file descriptor. The fd could be a file, but the recommendation is to use a pipe. This is a cool idea - threads in libopendaap are pulling all the data over the network and all the playback thread has to do is take as much data as it needs from the pipe. The problem is that libopendaap will almost certainly pull megabytes of data in long before (in processor time) it is needed. The wma11b has only 16MB, and a lot of that is already being used by the kernel, the root filesystem and the application file system. This does not leave a lot for applications to run in. I seem to recall reading somewhere that after everything is loaded there's only 5-6MB available for application use. An entire mp3 buffered in memory suddenly means there's nothing available for the application to use any more. wmamp uses more of a just-in-time principle - the data is pulled over the network just before its needed, and it seems to work pretty well. What I do think is worthwhile is to replace Apple's mDNSResponder software with the mdnsd the libopendaap is using. I believe that will be my project for tomorrow night.

Labels:

Thursday, June 29, 2006

wmamp: ogg and mp3 work again

It took about 30 minutes of poking around tonight and I had mp3s playing again, and then probably another 30 minutes and ogg was working. It turns out that my ogg problems had to do with the fact that mt-daapd was sending me ogg files as Content-Type "audio/ogg" but wmamp was expecting "application/ogg". A quick compile and ogg works. What I'm seeing now is that it keeps playing the same song over and over, rather than progressing to the next song in the album, so I expect I've broken something else.

I made another discovery today - apparently I've never needed libopendaap - some code was lifted from the project to allow wmamp to authentication to iTunes, but that's it. Looking at the code now, I'm not sure why wmamp doesn't just use libopendaap - it has more features (like supporting playlists), and it uses an mDNS client that's designed for embedded systems.

And speaking of embedded systems, the wmamp image includes telnetd so that you can log in to the wma11b and do stuff (like killing and restarting wmamp so you can watch all the debug output it dumps to stderr). I spent a little time poking around the filesystem, and I noticed that the root filesystem contains a copy of uClibc - an implementation of the standard libc that's designed to be small (for, say, embedded systems like this). At less than 200kB it may not contain everything I'd like, but the concept is solid. Conversely, the image that Linksys provides to download contains a version of glibc that's over 3MB. I don't even know how they got it that big - the one I have is only about 1.1MB. But why have two different versions of libc at all? The only thing I can think of is that they needed a libc to boot, but glibc wouldn't fit, so they had to use uClibc. For the main application though, it was probably easier to develop with glibc, and so they took the hit. I'm sure a lot more RAM could be freed for application use if we were using uClibc instead. I'm hoping to be able to pursue this, but the disk requirements might exceed what I have here (have I mentioned that I'm doing all my development on a 200MHz Pentium machine with 32MB of RAM and about 8GB of disk space?). Hope springs eternal - maybe I can make it fit. Then I just have to coerce all the stuff I've already cross-compiled into building again against uClibc. Yeah, that's all.

Labels:

Wednesday, June 28, 2006

Mind Meld Part III: the Return of the Ogg

(Okay, that is the last of my lame attempts at clever titles.)

Yet another milestone has been reached: I was able to build a downloadable image containing the copy of wmamp 0.6 that I compiled myself and the darn thing ran. So at the very least I know I can build something that works.

Adding (or rather, re-adding) ogg Vorbis support is quite another story. James Pitts originally wrote wmamp (thank you, James!), and then Steve comes along and adds support for ogg vorbis files. According to Steve's blog, he pushed his ogg patches back to the maintainers of wmamp (whoever they were at the time, maybe James, or maybe these guys) and daapd. The funny thing is that none of the later versions of daapd or wmamp have this work, but the reasons are probably lost in the mists of last year. The work on daapd is moot for me since I have mt-daapd running, and it already has support for sending ogg files (if you explicitly enable it with --enable-oggvorbis, which I had not, so I had to rebuild it again last night). The changes to wmamp are still very relevant to me though, and so I figured I'd take a stab at merging them forward. Steve's patch applies cleanly to the wmamp-0.4 sources, so I created two versions: wmaamp-0.4-canon (canonical) and wmamp-0.4-ogg (with Steve's patch applied). Then I created two versions of wmamp-0.6 with the same names. Then comes the really hairy part: compare the differences between the two 0.4 source files (should only be the ogg changes) and try to make the same changes to the wmamp-0.6-ogg sources. I had to do it this way because the source for 0.6 has changed significantly from 0.4. There has been a lot of restructuring of the code, and most of the chunks of Steve's patch would not apply at all to 0.6 (some of the files are no longer there).

Oh yeah, I had to cross-compile Tremor too, but thankfully the xiph people had everything already set up so this was completely uneventful.

In an effort to make sure I was on the right path, I built an image using vorbis-0.4-ogg and downloaded it to the WMA11b, but it would segfault after selecting a DAAP server. I wasn't ready to give up yet though, so I got wmamp-0.6-ogg to compile and built an image with that. It does better then the 0.4 version - it dies only when I try to start playing a song. So that kinda blows, but it's still better then 0.4, and I have some idea of what is wrong. It probably didn't help that it was rather late in the evening when I was doing my merges and such, and so I expect that I made more than a couple of mistakes.

I can't believe all that I've been able to do in a couple of evenings. This is exactly why I love programming. More precisely, it's why I love programming with Free Software (or Open Source, if you prefer). I am starting to make this device my own. One day soon, I will grok the fullness of it.

Labels:

Monday, June 26, 2006

Mind Meld Part II - The Clienting

Woo hoo! wmamp builds! This is no small accomplishment. wmamp requires FreeType2, libhttpd, libid3tag, libmad, libopendaap and zlib just for starters, and they have to be cross-compiled for ARM. Oh yeah, about cross-compiling - there's this great Debian sub-project called emDebian that
has an apt repository with pre-built cross-compilers and a few libraries (like libc) to link against. Very handy indeed.

Luckily most of the aforementioned libraries cross-compile pretty easily, with the exception of freetype - it creates a program (apinames) to run that extracts data from the .o files, but apinames also gets cross-compiled and so it won't run and the build fails. Thankfully apinames is only a single source file, so it was a simple matter to compile it manually (and natively), copy the binary where it was needed and then run "make" again.

wmamp needs one more library - mDNSResponder from Apple. wmamp was built against version 58.8, but good luck finding it. The original author of wmamp - James Pitts - has archived a version here. What I don't understand yet is why this is necessary - libopendaap builds and uses another multicast-DNS library mdnsd, and so it seems that wmamp, if it needs direct access to an mdns API should be able to use that instead. I'm sure there's some duplication of functionality here that could be trimmed. mDNSResponder, BTW, cross-compiles just fine.

With all these libraries built and installed, it's time to build wmamp itself, and that's when things go wrong yet again - emDebian installed kernel headers for 2.6, but the WMA11b runs a 2.4 kernel, and at least one header file - fb.h - has changed in a way that is incompatible with wmamp. The solution is to unpack all the stuff Linksys provides here (warning, zipped tarball of truly unholy size), install it and create a local symlink to the kernel headers Linksys provides. All of this might not be strictly necessary, but until I know just how many changes were made to the standard 2.4.17 kernel for the WMA11b this was the safest and most expedient route to a successful compile.

And that's that. I still have to build a disk image with my wmamp to be sure what I've compiled runs, but a clean build is 90% of the work, IMHO.

In my first WMA11b post I neglected to mention a couple of things. First, turthalion was the guy who put me on to the WMA11b in the first place when he got one back in January, and when I told him about my weekend he kindly linked back to me, and then proceeded to (rightly) berate me for not providing the explicit commands to start and stop the Windows(TM) services that I had mentioned. I don't know why, but a lot of sites tell you to stop and then restart these services "and if you don't know how, then just reboot". No one seems to want to reveal the dark secrets of the "net" command for whatever reason. Well screw that. If you want to stop the services, use these commands:

net stop XWPCHostService
net stop XWPCApplicationLoaderService

To start them up again, use:

net start XWPCApplicationLoaderService
net start XWPCHostService

Stay tuned for Part III - adding ogg vorbis support (well, that and creating an image I can actually load onto the WMA11b).

Labels:

Sunday, June 25, 2006

Mind Meld

The better half is away for a while, and my WMA11b just showed up, so I figure I have a golden opportunity for some serious hacking. Out-of-the-box it's already pretty sweet - you fire it up and do a little configuration (stupidly, it defaults to a static IP address), attach it to your stereo system and install some software on your Windows(TM) PC and you're ready to listen to music (or look at pictures). The software on the WMA11b itself isn't bad, it looks good and the controls are sensible. The other software though, to be blunt, sucks.

Two Windows "services" are started on your machine, but they don't actually show up in the usual place. You have to know to use "net stop" and "net start" if you need to stop or start these without rebooting - that's just sad. I can't figure out if they were lazy or just didn't want people messing with them. Maybe stopping and starting them a lot causes problems over time or something. Also, some part of the software required the ".Net Framework". Now I already had ".Net Framework 2.0" installed for some other reason that escapes me just now, but the installer insists on installing ".Net Framework 1.0" (not even 1.1!) anyway. Who knows what kind of problems that's going to cause in the future.

But back to hacking. The most basic thing people do is to replace the "player" software. The WMA11b loads the player every time it starts up, so if you know what you are doing you can load all kinds of things on there. The most popular player is wmamp, which is also open source. The neat thing about it (vs. the standard software) is that it uses the iTunes DAAP protocol and can see any and all iTunes servers on your LAN. Even better, there are open source implementations of DAAP for use on Linux et al. Getting them up and running is another issue.

This guy recommends daapd but it requires howl which is no longer under development. The howl guys mentioned avahi which has a howl-compatibility layer, but as it turns out there is another DAAP server mt-daapd which already has patches to use avahi natively, so I figured I'd check that out. I downloaded the sources for avahi and mt-daapd, and dropped into dependency hell.

Back in the days when I first got into Linux I installed Debian because a) I was technically savvy and b) it had a reputation of being the hardest to install. I figured if I managed to get it installed I would have already learned a lot about Linux, and I was right. But more to the point, I've stuck with Debian for all these years because it has always worked well for me and the package management rocks. Debian also has a reputation for being a little (or a lot) behind the times when it comes to its packages. When I installed apache a couple of months ago I got Apache 1.3. Well, avahi needed some libraries that were newer than I had installed. Debian has an avahi package, but it's in testing/unstable (ie. will be in the next stable release). I decided to take a chance and upgrade to "testing" and grab the canned avahi rather than try to build it myself. As it turns out, this was probably the easiest part of the whole process. The upgrade went perfectly smoothly, but avahi wouldn't run. I kept getting an error telling me avahi wasn't allowed to open the socket it was trying to open. It took a long time, but I finally figured out that my "old" kernel - 2.4.18 - only allowed very privileged processes to open such a socket, and avahi was not built with such privileges. It took a while, but I was able to upgrade the kernel to 2.4.32, and avahi started the first time I successfully booted the new kernel. Cool!

mt-daapd needed to be patched (link found on avahi's site), and the patch applied cleanly, but it couldn't quite build. The new source file that allows mt-daapd to use avahi wasn't being compiled or linked into the source. A quick hack of the makefile and I had a working (well, running) iTunes server.

From the same page as the daapd recommendation comes a program called wmaloader that allows the player software - Linksys suppied or other - to be loaded from a linux box too. wmamp is no longer under development by the original author, but these folks have been doing a lot of good work. I grabbed the latest wmamp image from them and used wmaloader to get it installed. Success! Not only is the new software installed, but I can see mt-daapd, and I can see the iTunes server on the Windows(TM) machine.

So I consider phase one complete. The server-end software appears to be pretty mature, so I expect there's little work to be done there. Stay tuned for phase two - building wmamp.

Labels:

Wednesday, May 03, 2006

Finding the Root Cause

Like every other programmer, I spend a lot of time debugging. One of the hardest lessons I've had to learn is the value of finding the root cause. Put simply, the root cause is the bug in the program. Sounds easy enough, find the bug and fix it. But unless a program is very simple, the observed bug and the true bug may not be the same. The difference here is the difference between a stuffy nose and a cold. The observed problem is that you can't breathe easily because your nose isn't clear, but the real problem is a virus in your body. You can take something to clear up your nose, but it won't make your cold go away any faster.

There's a similar principle that is taught in first year psychology: if you perform an experiment, and you see a correlation between the two variables you are testing, the first reaction is to say that one causes the other. For example, let's say that a survey indicates that people who own pets also recover faster from heart surgery. The first conclusion one might draw from this is that having something to care for is motivating these people to get better. And this might be true, but what if there is a third factor that causes these people to heal faster and also have an affinity for pets - perhaps they are just more active in general than people who do not own pets. The only way to find out is to construct another experiment to that will eliminate other possible causes.

Finding the root cause can be hard, and it's made no easier when there's pressure (like a deadline) to get it fixed. It can be very tempting to slap a bandage on the problem and just fix the symptoms. My experience, however, has been that when this is done the bug invariably comes back again and again, appearing as different symptoms until you finally find the root cause and fix the real bug.

The whole "root cause" thing seems to be pretty popular in the scientific world. I have to wonder if it could be applied to other different fields of endeavour, like oh, I don't know, the business world perhaps.

About a month ago there was a flurry of articles about how Sony's UMD movie format was failing. Wal-mart is dropping them and studios are releasing fewer titles on UMD because for the most part they aren't selling (comedies are apparently the exception). I've heard a number of reasons for this: UMD only plays on the PSP and holding a PSP for two hours while watching a movie makes your arms sore are just two of the more common complaints. It's obvious that the former is true, and I've watched Spider-Man 2 on my PSP, so I can confirm from personal experience that the latter can also be true. But are these really the reasons why people aren't buying UMDs? I suppose it's possible, but then how does one explain the success of "Video Now!" and "Juicebox"? Unless you have kids (or nieces, nephews, etc), you may not be familiar with these products. Both of them are personal video players for kids. The screens are low-res (I don't think they're even 320x240), and the quality of the video is pretty bad. But, both of them retail for about $50, and the media is $5-10. And what can you get to watch on them? Well, they're for kids, so big surprise, it's mostly cartoons. Both have been around for a couple of years, they both use media that doesn't play on anything else, and they are apparently quite popular. So Sony wasn't really doing anything new. Mattel and Hasbro did it first, and seem to be making money at it.

Sony has outlined a two-part plan to try to revive UMD. Part 1: sell a device that allows you to hook up your PSP to your TV so you can watch your UMDs on "the big screen". Part 2: bundle the UMD and DVD for about $10 more than the DVD. IMHO, the TV adapter treats a symptom. If I want to watch a movie on my TV I'll just get the DVD so I can have surround sound, subtitles, commentary and other extras. Bundling is a good idea, but Sony's flubbed the execution. All the bundles coming out are movies that have already been released on DVD, and so anything I'm interested in I already own. Also, the "bundling" seems to be just shrinking-wrapping the DVD and UMD cases together. I was hoping for a single DVD case that held both. Will either of these things keep UMD a going concern? I suppose they might help, but I'm skeptical that they can turn it into a roaring, or even niche, success.

So if the various complaints Sony is trying to address are just the symptoms, what is the root cause? I have an idea, inspired by Hasbro and Mattel: UMD is perfectly viable if it's a good value. If the functionality is limited, the price should be as well. There's nothing wrong with discs that play only on the PSP, but they shouldn't cost as much as (never mind more than) a DVD. At $10 I'd be buying them, even if I already owned the DVD. As for the arm thing, well, I've seen an eight-year-old hold a Video Now! box (that weighs at least twice what the PSP weighs) for hours. If they can do it, so can I. Funny thing too - I don't think I've read a single comment about how hard it is to hold a PSP for two hours while playing a game.

Labels:

Friday, April 21, 2006

m-m-metablogging

Ah sweet, sweet linkage, and a gauntlet thown down too. Though frankly I can't see how a posting here could be more effective at inducing a blog posting than dropping into his office and saying "update your freakin' blog already!" as I am wont to do.

I've never needed to use the possessive form of zar', but then I'm not fond of writing about myself in the third person. Having now seen someone else deal with it I realise that it results in a bit of a mess. It's almost as twisted as the origins of the handle (which I won't go into as I expect the intersection of 2001: A Space Odyssey fans and Robotech fans is pretty much guaranteed to be the null set). A blog may be an exercise in vanity, but even I'm not so delusional as to think anyone would actually be interested in that. If I'm going to have a blog and pretend people are going to visit, I figure the least I can do is give an opinion like, say, how no one knows how to program any more (a.k.a. my very first post), what makes a good (or bad) remote, or how oncoming drivers on nearly-empty streets are trying to kill me. At least the hypothetical readers of such posts are likely to be entertained. Expounding on the genealogy of a four character alias seems more likely to, at best, drive people away.

Yes, those topics are an indication of things to come. You expected something else from a paranoid tech-junkie fan-boy? I have said, on many occasions, to many people, that the only thing I have going for me is my brain. I happen to like thinking. If you're foolish enough to come back here, I have every intention of making you think too. You have been warned. I guess I'll know if I've succeeded when the hate mail starts rolling in.

Labels:

Tuesday, April 18, 2006

Every Cycle is Sacred

Until about a year ago I'd spent a good four or five years doing Windows CE programming. Not application programming, but OS/OAL/bootloader programming. Now I'm working on code that runs on a 16 or 33MHz Dragonball (68328) processor and it's so much more satisfying.

The CE machines had clocks running at several hundred megaHertz, 32-64 megabytes of RAM and FLASH. The machines I'm working with now have only 256kB or 1MB of RAM and four or eight megabytes of FLASH. So how could I possibly be happier?

Every. Cycle. Counts. The fastest instruction takes four cycles, some take dozens (logical shifts). My first big project at this job involved choice between developing a new special-purpose algorithm or tuning a core routine. I compiled that core C routine to assembly and hand-optimised it. Lovingly hand-optimised it. The end result shaved 40% off the execution time. It's not that the compiler was bad, it actually did some very clever things, and I made sure I kept the good bits. Making this program run faster was interesting and challenging because I couldn't just throw a faster CPU or more RAM at the problem and make it go away.

Who does this anymore? Who knows how to do this anymore? Yes, I realise that hand-optimising assembly code for a non-stalling RISC processor is the fast path to insanity, but come on. Optimising isn't always about programming down to the bare metal, sometimes it just means thinking about the problem first. Just because you think you have cycles and RAM to burn doesn't mean you use any old BFI algorithm. Programmers should think more not less.

Labels: