Home

Advertisement

oscar
I composed the following in response to DOM = Frame buffer. It turned into more of an essay than a comment, and insomuch as I think I made a few worthwhile points amidst my rambling, and to justify the cost in time and energy diverted from the Lisp game hacking I ought to be working on, I've decided to repost it here, with minor edits and additions.



That one data structure can always be decomposed into multiple data structures with equivalent semantics doesn't imply it's unquestionably incorrect not to separate them. That two things can exist separately doesn't mean it's wrong to combine them, particularly when they are intrinsically related and the alternative is maintaining redundant state which must be kept synchronized. That's the problem with dogmatic proclamations. Understanding where to draw the line is crucial to optimizing both machine efficiency (via less storage and runtime effort) and programmer efficiency (via less code and fewer bugs). The complexity of a system grows exponentially with each such layer of decomposition. Is the stopping point arbitrary? I claim it is an engineering compromise, informed by the anticipated value of additional modularity, with the prerequisite that one structure can be mapped onto another without loss of necessary information, and "necessary" here meaning things which you are already or can reasonably foresee using, not any arbitrary property which you might invent.

Certainly you would not check pixel values to determine if a menu is open. The mapping from semantic elements to rendered pixels is complex and seldom invertible. You might reasonably ask the UI toolkit if the menu is open. You can keep track of this yourself, but you must anticipate every way the menu might become closed: both by the operating system, or by other code in your application - perhaps written by someone else - that bypasses your intended function for changing this state. If you don't insist on recording this information yourself, you write less code, and with a single definitive source there is no possibility of inconsistency. This is precisely what the referenced article argues against, though it may be a case of a poor or overly simplified example.

An aside: The pixel example is, by coincidence, relevant to a point I've been thinking about lately. Game systems from the early 80s did something that seems unthinkable by modern standards of software hygiene: collision detection was often handled directly by the video hardware. Semantics and presentation, completely conflated. Yet, as a programmer, the choice is that of checking a few flag bits indicating collisions between objects and/or the background, or performing a separate pass through the objects to check for collisions yourself. It makes sense to use the information if the hardware provides it, and it makes sense for the hardware to provide it, because it's just a few extra AND gates and flags to add there, versus an expensive and complex loop in software. This choice imposes constraints on the application, yet often it was the correct one. Where it wasn't, a price was paid in complexity and (potentially) performance. To my surprise, just when I thought this kind of coupling was relegated to the past, it emerged in modern systems with a new twist: the hardware occlusion query. Meanwhile, I think about efficient 2D collision detection in sparse environments. In the past, I've used spatial hashing. In the future, I may use a quadtree. It pleased me to realize that, in the service of detecting collisions, a quadtree can also contain all the information needed to render the objects, storing pointers to transformed rectangles of pixels in the leaves, and it's conceivable that simple hardware could output video directly from the tree with no intervening framebuffer (though perhaps less simple if it tries to make the most of the available memory cycles). Indeed, a quick search indicates direct scan conversion of quadtrees has been researched in the past.

All that said, hiding application state in the DOM tree sound like a dubious proposition. However, is menu visibility truly important application state, or just an irrelevant property of the menu's presentation? Core application logic should only care when a menu item is selected, and not about the menus themselves at all, as the most starry-eyed proponents of "Model-View-Controller" architecture will insist. That patterns or philosophy can guide the way without always being reified as code and data - often in excruciating detail - is a point too often overlooked, and the cause of much unnecessary labor and suffering. This may be the common folly of a C++ design pattern cultist and a dynamic language enthusiast: one idolizing grandiose generalizations with extraneous code; the other enshrining often-irrelevant information while insulating himself in idealized reflections of the outside world, in the name of consistency, introspection, or simplicity.

McPixel, a frivolous lisp hack.

  • Jan. 31st, 2010 at 2:25 PM
oscar
Yesterday, I had the idea that it would be easy and entertaining to hack together a toy application for drawing and animating pixel art. Some hours later, McPixel was born. It provides an editable grid of pixels, a color palette, and allows you to string sequences of images together into animations. It's a toy, but someone might find it amusing. Obligatory screenshot:

McPixel

Features:
  • 80's retro McCLIM user interface.
  • No documentation or online help.
  • Maximally frustrating color sliders, using IHS colorspace.
  • Intuitive modeless drawing UI, achieved by including only a pencil tool.
  • Saves/loads files only in its own format, based on unportable s-expressions.
  • Requires X11 and SBCL with threads, to keep out the riffraff.
  • User-editable brush shape.
  • Remap Color and Silhouette commands, for tracing previous frames.
  • Realtime animated preview plays while you work.
I'm not sure I'll ever put this to productive use, if a productive use for such a thing even exists, but there you have it.

Update: I've added the ability to export an animated GIF file, using Xach Beane's Skippy library. Here's the "turtle.anim" example file, exported as a GIF:
McTurtle

Tags:

Romhack idea

  • Jan. 27th, 2010 at 7:59 AM
oscar
Super Mario Bros. is a classic game. It has held up amazingly well over the years, and is still as fun today as it was in the 80s. Unfortunately, I've played the first few levels of the game so many times that I wouldn't mind if I never see them again. The serial nature of the game made sense in an 80s arcade mindset, but today, most people playing Super Mario Bros. probably aren't out to finish the game, and likely have already finished the game in the past, instead just wanting to play for a little while before moving on to other things.

My idea, then, is to hack the SMB1 cart to make it better suited to the casual player. I take inspiration from, of all things, Quake 3: Arena. First, add a level select screen. I'm sick of endlessly replaying World 1. Second, some stat keeping to encourage replay. It should at least record the time to completion and score accumulated during the level. It might be worthwhile to give the player only one life to use during a run through a level, to make things more interesting, and stop them from gaming the scoring system. Naturally, add save ram and a battery so this data is preserved. Finally, more levels. You could integrate the whole of SMB2j, and possibly fan-made levels from the internet.

On the technical side, move it over to an MMC1/3/6 board with a battery, and you'll have plenty of space to cram in various hacked versions of the games. Write the main menu, make some minor hacks to the games to integrate them with the scoreboard, and you're set. Shouldn't be too difficult, although it isn't something I want to spend time on right now. Perhaps something like this already exists, among the hundreds of SMB romhacks that no one has time to dig through.

Tags:

SDL Quake

  • Jan. 11th, 2010 at 6:25 PM
oscar
Lately I've had a renewed interest in graphics programming, and I went back to basics, rereading everything I could find about the original Quake, and simultaneously brushing up on some (relatively speaking) more recent developments, like how to write shaders (via OpenGL's fragment program extension), normal mapping, etc. Inevitably the mood struck me to play the original Quake. All I have available to me at present is a laptop running Linux. Luckily, I still have the Linux version of Quake: The Offering (Quake 1 plus both mission packs) installed, passed along between successive Linux installs over the past decade, like a family heirloom. Of course none of the official binaries run anymore, binary compatibility on Linux being mostly nonexistent, so I set about finding a suitable engine port.

I was surprised that a quick "apt-cache search" didn't find anything. Looking around the web, there are a number of interesting engine ports, but most of them looked undesirable from the perspective of someone wanting to play the game more or less as it was originally made, not with upgraded rendering technology, new models/textures, or the other cool things that most of the engine ports indulge in. New features thus became a turn-off, and I went for the most plain-sounding engine port I could, SDLQuake.This had a few problems, which I fixed:
  • Autotools bit rot
  • Use SDL 1.2 (check for SDL 1.0.9 failed)
  • Fix config.cfg loading (it tried to reload from a different directory than it had been saved, losing my key bindings every time)
  • Fix garbled sound (set sample rate to 48 KHz by default - ALSA still sucks)
  • UDP netcode seemed to be doing a blocking instead of non-blocking read.
Also a few gratuitious changes:
  • Start in fullscreen by default
  • +mlook at startup
If I spend any more time on this, there's a few more easy things left to do:
  • Compile a glquake executable (right now it's only compiling the software-rendered version)
  • Support for streaming the ripped soundtrack from .ogg or .mp3 files.
I'd have hacked the music code in immediately were it not for the small inconvenience of having to run the audio at 48 KHz instead of 44.1 KHz (the rate that songs ripped from the CD will be at) due to whatever ALSA stupidity causes the audio to be corrupted on this machine (it's audible, but there's a lot of crackling, like samples are missing or clipped). While Quake is happy to put its 11 KHz sound effects through the grinder with no proper resampling (it upsamples using a fixed-point "nearest sample" approach), that would really make the music sound like crap. Of the decoder libraries I know how to use off the top of my head, libmpg123's resampling sounds like garbage, and libvorbisfile doesn't offer it at all. I don't have a 44.1KHz -> 48KHz resampling routine lying around, and always make simple DSP code out to be harder than it really is, so I put off hacking in support from the music. The original SDL port  will play the music from CD, if you have it in your drive (I didn't, and found myself playing Quake to the soundtrack of a "Learn Thai Language" CD instead, which didn't fit very well). In other words, I'm lazy, and didn't want to spend a couple extra hours sorting the music out. Sorry!

Anyway, maybe someone will find this useful - or, perhaps, someone will think I'm a moron for screwing with this instead of using one of the other Quake engine ports. Either way, I've thrown the code up on Github here.
oscar
Recent versions of NASM appear to have shed the instruction set reference which was traditionally an appendix to the manual. In its place is a completely useless list of instructions with no information about them. This is unfortunate, as I've probably made more use of that reference more than NASM itself, and I don't know of anything suitable to replace it - I keep the Intel manuals around, of course, but it might take 20 seconds to find something in the awkward 700 page PDF files, whereas I can do "info nasm" and find the relevant page via incremental search in a few seconds. Several years ago, when I spent much more time crawling around in x86 machine code, the NASM manual was my first stop whenever I had any questions about instruction encodings (although I found a few errors, and sent patches which probably disappeared into the spam-ridden abyss of the nasm list on sourceforge without being applied). I used it often enough that I had either a script or an alias that would run info and open directly to that section. I don't know why it was eliminated (I'll guess the developers didn't feel like maintaining it anymore), but it's a shame.

I'll have to find an old version of NASM and figure out how to extract this section, as there really isn't any substitute.

Tags:

Shuffletron bug: dirent.dt_type

  • Dec. 13th, 2009 at 1:15 AM
oscar
Shuffletron, despite its toyish demeanor, makes an effort to do certain things correctly and efficiently which are critical to its being useful as a music player. To that end, it bypasses the hopeless Common Lisp pathname system in favor of using strings and POSIX system calls, and sidesteps the increasingly prevalent and often misguided "UTF-8 or bust!" mentality in favor of a more robust Unix-like approach of being encoding-agnostic by passing 8-bit characters transparently.

In particular, I made a point of ensuring that the library scan (a recursive walk of the directory) completes as quickly as possible, within reason. My benchmark was the combination of find and grep, which not only traverses the entire tree but does some useful processing on the result. It's not possible to reach that speed without writing excessively low-level Lisp code, but I got close enough that I was happy with the result. It may sound surprising for a task like directory traversal, but any cute processing your Lisp's filesystem interface does - consing/converting strings, decoding characters, building pathnames, converting C structures into nicer Lisp structures, etc. - quickly eats away at this peak performance. Those system calls run a lot faster than I first expected, once the relevant kernel caches are warmed up.

I've been playing with sshfs this evening and wanted to try using it to access my music library on the other side of the planet from a local Shuffletron instance (with appalling but sufficient latency and bandwidth, despite ostensibly being multi-megabit connections on both sides). Oddly, Shuffletron couldn't find any files - it said the library was empty. I soon recalled a potential corner I had cut in the filesystem scanning code.

While scanning the directory tree, you have to figure out whether each entry is a file, directory, or something else. Originally I had done this by calling stat and checking the st_mode field (I'd borrowed the functions from some code that had other reasons to stat the file). While converting the code from sb-posix to osicat-posix for portability to CCL (and beyond!), I cleaned it up to use the struct dirent.d_type field instead, obviating the call to stat. man 2 stat says this about the d_type field:

Other than Linux, the d_type field is available mainly only on BSD systems. This field makes it possible to avoid the expense of calling stat(2) if further actions depend on the type of the file. If the _BSD_SOURCE feature test macro is defined, then glibc defines the following macro constants for the value returned in d_type:

It's available on Linux (except for old versions)? Great, my program only works on Linux anyway, I'll use it! So went my reasoning. I had some misgivings on account of the note "If the file type could not be determined, the value DT_UNKNOWN is returned in d_type," but it seemed to work well enough, and figured I'd just move on rather than write the extra eight lines (!) of code to deal with what appeared to be an obscure case.

With sshfs, I finally found that obscure case. The dtype is always set to DT_UNKNOWN, and shuffletron can't get past the root directory of the library. Despite having written a (simple) FUSE filesystem myself, it isn't immediately clear how you can make that work from the filesystem's side, but it is clear that it was time to add a fallback to calling stat. Having done so, Shuffletron now happily scans my music library over sshfs, which would be great, except that it also takes something like 15 minutes walk the directory tree (even using find!), because every directory appears to involve several trips over the internet. Hundreds of directories times a few seconds each.. not good (this same thing annoys me when copying multiple small files via SCP, incidentally). Just goes to show that presenting something (a computer many thousands of miles away) as something it's not (a local filesystem) can only work so well. If only there were an interface (is there an interface?) to indicate the desired access pattern, so that sshfs could pull all that information quickly in a single batch and keep it in its cache.

This little fix will be present in the forthcoming Shuffletron 0.0.5 release, along with other great features that I've been dutifully testing since July or so. :)

Game Design Essentials: 20 RPGs

  • Dec. 11th, 2009 at 10:27 PM
oscar

Although it's nearly half a year old, I only decently happened upon John Harris's article Game Design Essentials: 20 RPGs. It discusses the evolution of the CRPG/JRPG genres from war gaming and paper role playing games, surveying many the most influential early series in the genre.

I'm a relative newcomer to the roguelike genre. Aside from some childhood exposure to Gateway of Apshai (a crude action game with Rogue influences), I didn't play any roguelike games until taking up nethack sometime in 2006 or 2007. Subsequently the genre has made a big impression on me, providing a solution to certain major problems in game design. Harris appears to agree, as he writes about Nethack:


Nethack is a roguelike, and so I'm required to say something about one of those games' most controversial features: permadeath. (Okay, I admit it -- I've been leading up to this.) Since Ultima and Wizardry, but unlike pen-and-paper games to this day, players are allowed, and even encouraged, to save games and return to them if things go badly, a design characteristic that makes it almost impossible for anything really bad to happen to the player's characters.

I make no secret the fact that I consider this one of the most pernicious aspects of CRPG gaming, that permanent disadvantages acquired during the course of play cannot be used by a designer because the player will simply load back to the time before the disadvantage occurred. Admittedly, the prevalence of this attitude comes from some older games that could easily be made unwinnable if the player wasn't careful.

However, it's reached the point where "adventuring" in an RPG rarely feels risky. Gaining experience is supposed to carry the risk of harm and failure. Without that risk, gaining power becomes a foregone conclusion.

It has reached the point where the mere act of spending time playing the game appears to give players the right to have their characters become more powerful. The obstacles that provide experience become simply an arbitrary wall to scale before more power is granted; this, in a nutshell, is the type of play that has brought us grind, where the journey is simple and boring and the destination is something to be raced to.

Nethack and many other roguelikes do feature experience gain, but it doesn't feel like grind. It doesn't because much of the time the player is gaining experience, he is in danger of sudden, catastrophic failure. When you're frequently a heartbeat away from death, it's difficult to become bored.

Later, he strikes at the heart of why roguelike game mechanics are so compelling:


The theory I have about the engagingness of roguelikes is that they are a form of random narrative generator. The ups and downs of the player's progress becomes that of the story of the character, who has a relatively plain backstory in order to allow the player to invest more of his own personality into him. Most of these stories end in death, but so do our own, and it makes the few stories that make it to the end even more memorable.

The randomness and difficulty of the game, additionally, help to take responsibility for the failure away from the player. The player knows it's hard so any progress he makes is a sign of valor and heroism, instead of poor decision-making or foolish risk-taking. These kinds of stories aren't made up by a script writer; they come from the player's own actions, so they feel more "real."

Also, on JRPGs and Final Fantasy's decent into absurdity:


All RPGs traffic in abstractions. To some degree, an RPG can only be as successful as the extent to which he causes the player to ignore how arbitrary it all is. One of the signs of the aging of the JRPG genre is how its games have, recently, become less careful about how blatantly made-up their various systems are.

I'm a huge fan of the original Final Fantasy and became less fond of each successive game in the series, for roughly the same reasons that lead me to claim that Wing Commander 1 is far better than its sequels. Both franchises could be described as "stagnant and wallowing in its own cinematic pretensions," as Harris says of Final Fantasy. The intrusion of increasingly overwrought stories steal ownership of the character(s) from the player and destroys replayability.


If that sounds like your thing, give the article a read. It's quite long. I enjoyed it, and can't wait to read the rest of the series.

Tags:

Attempting to be grown up..

  • Dec. 11th, 2009 at 9:33 PM
oscar

I've long searched for poignant phrase to capture how I feel about a certain style of programming. I've finally spotted one courtesy of Dadhacker: "an attempt to be “grown up”. From this post:



Someone had read the Software Patterns book, because about every third source file implemented a factory; I’m pretty sure there were factories for making factories. Now there are perfectly good reasons to write factories, fine and wonderful reasons to have abstract instantiation, but what was going on in the bulk of this company’s code was fear, realized with indirection and thousands of lines of creative procrastination, and an attempt to be “grown up” with stuff copied from a popular book on design without doing any actual design.

Tags:

Nesemu release 4

  • Jul. 7th, 2009 at 3:31 AM
oscar
I've pushed out a new version of my NES emulator sources, flushing out the last six months of accumulated changes. It has been so long that quite honestly have no idea what most of the changes are versus the previous release, except that they don't improve emulation accuracy at all (in fact, I suspect it has been steadily regressing since release 2.

I intend for this to be the last release with an ambiguous license. Right now I am constrained by the terms of the M6502 code, which has some "no commercial use" clause in it, preventing me from applying my preferred MIT-style license. I began writing a replacement 6502 core with no strings attached, but that effort has taken a six month hiatus. Rewriting the CPU core will also get me one step closer to cycle-accuracy - close enough that if I were really serious about it, I'd have to rewrite the video... again. Bleh.

Here are a few changes I do remember:
  • Keyboard input now works correctly when there's no joystick connected
  • Stripe recording (see http://ahefner.livejournal.com/13165.html)
  • "Screenshot movie mode" (toggled by Control-F12)
  • FUSE filesystem support, disabled by default (see http://ahefner.livejournal.com/14789.html)
The source tarball lives here: http://vintage-digital.com/hefner/software/nes/nes-emu-4.tar.gz

Also, I've started including the git repository in its tarball, tripling its size. I think this is terribly inconsiderate, but I deleted the old git repository on the web server, so I feel like I have to back it up somewhere. I still don't understand these mixed messages from the git enthusiast crowd, where on the one hand they claim that pushing to a central repository isn't really done, but then go off and appear to use github for exactly that purpose. Whatever. They are strange people.

Adventures in Lisp application deployment

  • Jul. 5th, 2009 at 6:56 AM
oscar

Previously I mentioned my simple music player in CL, Shuffletron. Shuffletron uses save-lisp-and-die on SBCL or save-application on CCL to create a standalone executable, and is launched by a wrapper script that invokes it with rlwrap when available. It depends on several foreign libraries (more than I expected, in fact), and there were an alarming number of problems with the binaries I'd originally posted, all related to these library dependencies. I think I've solved them, though not necessarily in an optimal way.

The first problem, observed and fixed before the publically released binaries, was an issue where cffi-grovel (on behalf of Osicat) created a shared library with some auto-generated C wrappers for various functions, which the saved executable now expected to reopen at startup. I didn't realize this until my first user reported it dying at startup with an error about not finding /home/hefner/clbuild/source/osicat/posix/wrappers.so, followed by "a lot of lisp-looking gook." One solution would have been to include the library with the program and install it where SBCL could find it, but at the time I was opposed to including extra libraries with the program, so instead I took the C source to this library and linked it into SBCL's runtime executable. This has the unfortunate consequence that I have to build the binaries from a customized SBCL, but it solved the immediate problem.

The next problem was simple - on machines without the libc6 development package installed, the program failed at startup, unable to load librt. This was a simple matter of Osicat not using the best choice of name by which to load the library, a problem which has since been fixed. To be doubly certain, I modified the build script to close this library before saving the executable, because I wasn't using any functions from it anyway.

The most aggravating problems I encountered were with the libmpg123 library. Some users reported "undefined alien function" errors whenever they tried to play a song. Others reported a scary error about stack alignment as soon as the problem started. To some extent I haven't solved these (in the sense that Mixalot users and people building from source may still get bitten by them) so much as hacked around them for the sake of the binary release. There were two basic problems:

  1. On 32-bit Linux, with newer versions of libmpg123 than my Debian-running laptop happens to have, the compile-time choice of large file support breaks binary compatibility with the libmpg123 library, wreaking havoc with the FFI bindings. Specifically, if large file support is enabled, a number of symbols change names, e.g., mpg123_open becomes mpg123_open_64. This is irritating and not how I'd have done it (for instance, I don't think the version number of the shared library changes to indicate the break in compatibility), but I could've easily worked around it in mpg123-ffi by detecting which version is present at the time the library is initialized. I very nearly did so, were it not for the following problem:

  2. On 32-bit Linux, due to the desire to have properly aligned data when using SSE instructions, recent versions of GCC provide support for aligning stack frames along larger boundaries, such as 16 bytes. The mpg123 developers seem to take this support as sufficient justification to ignore the platform ABI, introducing dubious stack alignment checks at every entry point to the library. This is not something I know how to work around from the CFFI binding in a reasonable fashion, so for the time being I've given up on adapting to such hostile versions of the library. Fortunately, I've absorbed enough trivia eavesdropping on the SBCL hackers on IRC to know that Darwin uses 16 byte alignment for precisely the same reason, so SBCL must support it, and after a few minutes searching I hacked the align-stack-pointer macro in SBCL's x86 backend to enable the 16 byte alignment on Linux as well, which appears to work around the problem (at the cost of my build environment getting even stranger). I also rebuilt the libmpg123 library with --disable-largefile and, just to be thorough, --disable-aligncheck, and set my CFLAGS to -mstackrealign, two measures which I think should've sufficed to solve the issue even without a hacked SBCL.

The 32-bit Linux/x86 binary now includes a rebuilt version of libmpg123, renamed libmixalot-mpg123, with these changes. The 64-bit binary doesn't have any of these problems, and should Just Work, but you have to provide your own libmpg123 as before. My mistake, of course, was using libmpg123, but I wasn't aware of libmad when I made that choice, and I'm not enthusiastic about rewriting otherwise perfectly working code on account of these issues (although I probably will, sooner or later). My current binaries, in conjunction with Shuffletron 0.0.3 (featuring various minor improvements), should be free of these problems, but still haven't been as widely tested as I'd like. The lesson to me, obvious in retrospect, is to test these things on a more diverse set of configurations than my own machines (particularly when they all run the same release of Debian), even when it seems so simple that nothing could go wrong.

Tags:

Miscellaneous Lisp Hackery

  • Jul. 1st, 2009 at 1:49 AM
oscar
Here's a few things I've been working on lately which may be of broader interest:

Bordeaux-FFT is a small library for computing the FFT/IFFT on complex data, originally written by Robert Strandh (and/or Sylvain Marchand and Martin Raspaud), with contributions by Paul Khuong and myself. Last summer I did some audio processing experiments, originally using the FFT code from Sapaclisp. Robert helpfully volunteered his FFT implementation, which I cleaned up slightly and have been cheerfully employing in various audio hacks ever since. Several versions have changed hands through email, lisppaste, and my web server, so I've finally come around to collecting the changes, writing a brief manual, and tarring up a release. It's surprisingly fast, particularly with Paul Khuong's recent work on SBCL, although I don't know how it stacks up against some of the highly tuned assembly language implementations out there. I use it along with my fledgling task queuing code to grind out batches of FFTs across four CPU cores.

Shuffletron is a simple music player in CL, with a few interesting features. I've been running this program full time for several weeks now as my preferred music player. It snuck on to the lisp subreddit before I really announced it, and I'm sure folks on IRC are already sick of hearing about it, so I won't say much. I began with plans for a much more ambitious player, with a fancy graphical interface, and wrote the first version of this one Saturday realizing I needed something simple to put the audio code through its paces while I wrote the full player, intending to include this one as an example program with Mixalot. It worked better than expected, so I ended up fleshing out the feature set instead and decided that it was really all I needed. The code is lean and mean.

I've had a number of problems trying to produce redistributable binaries of this which I believe I've finally solved (although new binaries are forthcoming). I hope to write about these later.

Mixalot is the audio back-end of Shuffletron, factored into its own system(s) because it might be useful for other purposes. It includes a mixer which pulls audio from any number of streamer objects and outputs them to ALSA. It also includes FFI definitions for libmpg123 with some helpers for decoding MP3 files and reading ID3 tags, and a streamer class for decoding and playing MP3 files in real time. The libmpg123 portions are usable independently of the audio mixing/output code.

Tags:

eMusic misses the mark

  • Jun. 17th, 2009 at 7:38 PM
oscar
I'm a couple weeks behind in noticing the announcement by eMusic of their subscription plans and the imminent arrival of the Sony Music back catalog and its promised roster of vapid radio detritus. The cost, apparently, is that they're raising prices by 50% across the board (or more like 100% for the longtime members with grandfathered subscription plans from the early days of the service), to roughly $0.40 per song. I've been a devoted customer of eMusic for several years now, eagerly burning through 200 downloads at a time on one of the $50/month "Connoisseur" plans for much of that time, yet I thought their prices were already too high. This latest move is too much for me to bear, and I immediately used my remaining downloads and cancelled my subscription.

Perhaps eMusic feels that access to the wider catalog of music will offset the increased cost. If they think their existing subscribers will understand, they are gravely mistaken. Given the option, I'd rather Sony Music go out of business, their back catalog be destroyed in a fire, and their CEO choke to death on his breakfast. This article puts it nicely:

Most eMusic fans I've heard from are real music nuts, and are there to sample a wide range of music from relatively unknown cutting-edge acts, not to download music they could find anywhere. Imagine the clerks in High Fidelity suddenly being told that their favorite mail-order distributor is raising prices, but in exchange will now let them order ABBA and Chili Peppers records just like the chain stores in the mall.

In my brief investigations so far, I haven't found another music service that is competitive with eMusic, even (perhaps) at their new price point. I don't consider that an argument for staying with eMusic rather than defecting to another service, though. Given that I consider $0.40/song roughly twice what I'd call a reasonable price, I instead take it as an argument for not buying music online at all. It isn't like there aren't alternatives.

Tags:

Linux Audio..

  • May. 28th, 2009 at 11:32 PM
oscar
..is a disaster. If the ALSA developers had any honor, they'd fall on their swords, or hurl themselves from the nearest bridge / tall building. dmix is broken and apparently it's never going to get fixed, which leaves us the alternatives of either using crappy fly-by-night sound servers to hide the problem, or accepting that more than one application playing sound at a time is just too difficult for Linux in 2009. Nevermind that a few years ago, when I used OSS and the emu10k driver for an SB Live (that is, before the Linux kernel developers deprecated OSS), mixing was happily handled by the driver behind the scenes without the quirks or insanely high (and unpredictable) CPU usage that dmix entails, I wasn't forced to run half my software though the "aoss" wrapper, and games like Quake 3 and Return to Castle Wolfenstein actually worked with sound. Simultaneously, dmix seems the only way to coax sound out of ALSA that isn't a complete pain in the ass, but the illusion of workingness is soon shattered the first time you catch it spending 40% CPU to (purportedly) resample from 44.1 KHz to 48 KHz. There's surely a bug here, as the CPU usage is completely unpredictable, and repeatedly opening/closing the device will yield wildly varying results.

I hate it. All I wanted to do was write a simple goddamn music player.

Tags:

Emulator introspection with FUSE

  • May. 20th, 2009 at 5:23 AM
oscar
This evening's hack was to write a FUSE filesystem in my NES emulator. I've made it export a virtual filesystem that gives you files for the various RAM and ROM areas of the machine, and you can read and write to them. This could be useful for debugging my homebrew stuff. You can also point a hex editor at the 6502's RAM and edit bytes to cheat, dd /dev/urandom into video ram to glitch the display, etc. Surely peering inside applications through the filesystem is an extension of the Unix way. Here's what you get:

hefner@lightworks:~$ ls -l /tmp/nesfs
total 0
-rw-rw-rw- 1 root root   8192 Dec 31  1969 chr-rom
-rw-rw-rw- 1 root root    256 Dec 31  1969 oam
-rw-rw-rw- 1 root root  32768 Dec 31  1969 prg-rom
-rw-rw-rw- 1 root root   2048 Dec 31  1969 ram
-r--r--r-- 1 root root     22 Dec 31  1969 rom-filename
-r--r--r-- 1 root root     15 Dec 31  1969 rom-hash
-rw-rw-rw- 1 root root   8192 Dec 31  1969 sram
-rw-rw-rw- 1 root root  16384 Dec 31  1969 vram


I suppose I could fix the other fields in the stat structure. This is the first time I've done an ls -l here to notice. The FUSE examples I started from didn't bother. :)

This will appear in the next version of my NES emulator, although I can't say when that will be. Until I feel like spending an unpleasant hour or several learning to do merging with Git, probably.

How Rich Countries Die

  • May. 2nd, 2009 at 3:19 PM
oscar
It's a month and a half old, but I enjoyed reading this blog post by Philip Greenspun, How Rich Countries Die, itself examining the book The Rise and Decline of Nations: Economic Growth, Stagflation, and Social Rigidities. I particularly enjoyed this paragraph:

"A cashflow approach is much more effective for figuring out where we’re headed.  Money flows out to the folks on Wall Street who bankrupted their firms, to schoolteachers who’ve failed to teach their students, to government workers who feel that simply showing up to work is a heroic achievement, to executives and union workers in America’s oldest and least competitive industries.  If times are tough and money is tight, that means almost nothing is left over for productive investment.  What would have been a short recession will turn into a long depression and decades of higher taxes and slow growth to pay for all of the cash ladled out.  Special interest groups will continue to gain in power."
 


Live Distro Swap

  • Apr. 24th, 2009 at 5:08 AM
oscar
I've successfully downgraded my Debian machine from Squeeze (testing) to Lenny. I'd never really meant to upgrade to Squeeze in the first place, but my machine has been set to track testing for a while now. I was more than a little suspicious when it decided it wanted to download over a gigabyte of new packages, but I'm a fool and I was flailing around trying to get something working, so I figured "what the hell". Anyway, big mistake. I've been able to keep using the machine, but the domain resolver is on the fritz, sshd is seemingly in a coma, Firefox Iceweasel is crashing considerably more often than usual, and my printing (which I only got around to configuring two weeks ago) broke. I'm not bitter, but this is a little more bleeding edge than I'm interested in, so it was time to downshift into Debian stable.

Apparently people have downgraded systems from testing to stable using APT, but this sounded tricky. Just changing the sources list and doing dist-upgrade doesn't work, and apparently people who have done this have to pin a bunch of packages and somehow fool it into downgrading. Given that most of the info Google turns up on this is seven years old, I didn't want to risk it. Normally I'd just do another install into a spare partition, but having had such a smooth ride with Debian for so long (I ran the same install for something like five years, until finally moving on to 64-bit), this is the first Linux machine in years I didn't bother making such a partition on.

Instead, I installed Debian stable into a subdirectory (/lenny) using debootstrap, chrooted in to install packages, and prepped the /etc directory. Now I was ready to switch systems. I got the statically linked version of busybox and, while still in X11 with music, Firefox, and IRC going, did the following:

lightworks:/# mkdir sqeeze
lightworks:/# mv bin emul/ etc lib lib32 lib64 sbin usr var squeeze/
lightworks:/# cd lenny/
lightworks:/lenny# mv bin emul/ etc lib lib32 lib64 sbin usr var /      
lightworks:/lenny#

Now I'm running Debian stable. Everything is working just fire, except I had to restart firefox, and install some xfont packages for emacs. This is one of those rare instance where I'm genuinely pleased with my operating system. There's some loose ends to tie up, like configuring X11 (I'm still running the one from sqeeze; I'll fix it next time I quit X), and some miscellaneous packages to track down, but at this point I think I'll declare this stunt a success.


Tags:

Managing tasks

  • Apr. 2nd, 2009 at 3:54 PM
oscar

I'm fiddling with some C code, experimenting with different approaches to various subtasks, some of which depend on previous tasks. Results are valid only for the duration of the current frame. Performance matters, so I tend to comment pieces I'm not currently working on unless they're needed. For the time being, the data is often only displayed as debugging output, but sometimes the debug display is turned off, making the computation wasteful. Up to this point I'd been juggling the dependencies manually, calling each computation in order from a central point, but finally I've gotten restless to automate things. Here's some cute C preprocessor fun I bashed out a few minutes ago:



struct task {
    char *name;
    int last_time;
    int locked;
    void (*taskfn) (void);
};

#define deftask(name) \
  void task_do_##name (void); \
  struct task name = {#name, -1, 0, task_do_##name}; \
  noinline void task_do_##name (void)

static inline void require_task (struct task *task, int time)
{
    assert(task != NULL);
    if (task->last_time < time) {
        if (task->locked) {
            fprintf(stderr, "Fatal: Circular dependency on task \"%s\".\n", task->name);
            exit(1);
        }
        task->locked = 1;
        task->taskfn();
        task->last_time = time;
        task->locked = 0;
    }
}

I interface this with my code and the global frame_number as follows:


#define using(name) require_task(&name, frame_number)

Next I define various tasks. Some depend on others:


deftask(track_motion)
{
    .. some code here ..
}

deftask(feature_matrix)
{
  .. more code here ..
}

deftask(grid_alignment)
{
    using(track_motion);
    .. yet more code..
}

Code elsewhere also relies on the 'using' macro to ensure computations are up to date. The components communicate through global state. If they didn't, and the style were more functional, I supposed I'd be blogging about how I'd implemented memoization instead. Eventually, I might rig this up to farm the computation out to other threads (though this doesn't seem like such a huge win unless I go back to eagerly computing things in advance of needing them). I'd need a parallel 'using' operator.

Tags:

Two ways out of a recession

  • Mar. 19th, 2009 at 8:46 PM
oscar
What's the Federal Reserve's solution to stimulating the economy? My two interpretations (and I Am Not An Economist), each more or less malicious, and both probably inevitable regardless of which (if either) is intended, are as follows:

Plan A: Inject massive amounts of money with the aim of creating a new bubble, letting us coast by with another five or six years of illusory prosperity before it all blows up again in an even bigger and more spectacular fashion, leaving it to the next guy to sort out the mess, just like Alan Greenspan did to Bernanke.

Plan B: Destroy the currency! Massive inflation! Double, even triple the price of every imported good. What better way to get more people working than to force them to get second jobs (or first jobs, if they're wealthy or retired). Let the proles claw their way out of the pit while politicians fight to take credit for "solving the crisis," and Bernanke rests on the seventh day after reaffirming that his powers of economic destruction are not completely impotent.


Barring some miracle of science, like the invention of cheap and bountiful fusion power, I don't forsee a economic recovery for the United States. The collusion of corporate interests and a reckless and parasitic government is finally going to kill the host. At best, I can imagine stagnation in the near term. After that, a long and unpleasant decline, punctuated by occasional collapses in the house of cards constructed by government and financial interests, as the delusiion of perpetual exponential growth steadily erodes and eats alive both the financial system and (with its impossibly large debts) the US government. All the while, dwindling supplies of petroleum and nuclear fuel will push the chances of recovery and growth further and further out of reach, until civilization as we know it cracks under the pressure and dissolves. For extra fun, toss in the wild card of destructive climate change, if you believe in that sort of thing.

This scenario is horrifying, so convince me otherwise. Central bankers and politicians are playing god, and every move they make seems destined to trade the future for their own short term gain. It isn't necessarily maliciousness, just stupidity, desperation, and shortsightedness. Our leaders are going out screaming and flailing -- in quicksand.

Tags:

NES Timelapse Hack

  • Mar. 16th, 2009 at 1:58 AM
oscar
I had a silly idea tonight that for a game like Super Mario Bros. where the scrolling follows the player, and the player is usually in the center of the screen, collecting a single column of the screen per frame and combining them horizontally would produce a sort of timelapse image where the player would be seen as a blurred stripe moving through the scenery, which would be stretched or squashed according to the player's speed. I think I've seen something like this before, but in this era of information overload and the futility of trying to find anything in Google, I figured I'd just go ahead and hack it into my emulator rather than searching for prior art. Here's an example from Super Mario 3:




It's a bit rough (and that's one of the better portions), but you can see the path of Mario hopping from point to point. I tried this with several games: Super Mario Bros. 1, Super Mario Bros. 3, Duck Tales, and Life Force. In the latter case the game scrolls at a fixed rate, so you get a sort of shabby automap.

Tags: