People seem to like the idea of being able to say things like "cat file.wav > /dev/dsp". But a sound card is a complex thing that can be configured in umpteen different ways; what if the file has a different sample rate than the card is currently running at? Even if you gerry-rigged a kernel driver that could handle this case (by, for example, noticing when the file had been open()'d and expecting the next bytes to be a WAV header, which it uses to reconfigure the card), you're left with something that doesn't have even the most basic functionality that you'd expect from a sound player; for example, "pause."
So what has representing /dev/dsp as a file really bought you? It doesn't help you know what devices are available, because there's tons of crap in /dev that doesn't necessarily have a kernel driver loaded for it. It's not that useful as an end-user interface for selecting a sound card, because users don't want to choose between /dev/dsp and /dev/dsp2, they want to see nice names like "Internal Speaker" or "Apogee Duet."
Files give you a simple API for lots of common operations, which is nice, but the fact that all files are not created equal means that operations sometimes don't work like you expect. For example, how would you guess that "tail -f" is implemented? If you're like me, you would have expected that it works by calling poll/select/etc. and waiting for the file to become read-ready. But this approach doesn't actually work for monitoring growing files; the select() will just return immediately with read-ready even if you're at EOF. So what "tail -f" actually does is a loop that alternates between sleep() and fstat(). I'm serious! http://git.savannah.gnu.org/gitweb/?p=coreutils.git;a=blob;f...