Playing audio CDs, part 11 - Why isn't my sample ready for prime time?

As I mentioned in my previous post, the code I've provided will play back audio CDs.  But it's not ready for prime time yet.

There are four major problems with the code.

First off, the error handling isn't 100% up-to-snuff.  In particular, there are several error checks that are missing (if an allocation fails in the constructor of the CDRomReadData function, for example).  The error handling in general isn't robust enough for production code, and if there are failures during the read loop, the memory used to hold buffers is leaked.  That was an intentional omission to keep the size of the code down.

Next, the code totally ties up the callers thread.  That means it can't easily be adopted for use except in very limited scenarios.  And it's totally unsuitable for use in a Windows application (or a non windows application that uses STA apartment model COM objects).  For production systems that almost always a complete non starter.

Third, the code as written is utterly laptop unfriendly.  Laptops have a rather different power consumption profile than desktop systems.  For a laptop, battery life is king.  And, because of the laws of physics, that means that anything that involves moving parts is bad.  The bigger the moving part, the worse it is.  So writing code that involves moving heads on a hard disk is bad, but writing code that keeps a relatively heavy CD spinning in a CD drive is even worse.  For a laptop, it's far better to either spin the CD up once and transfer the entire audio data into memory, or to read the CD at 1x - slowly stepping the heads forward.  The absolute worst thing you can do is to spin up the drive and let it spin down again repeatedly - it doesn't take that much power to keep the disc spinning, but starting the disc up is horribly expensive.

The final version of this sort-of does the 1x read thingy - it moves the heads slowly forward.  But that means that the CD is spinning for the entire length of the track, which leads to increased power consumption.  With two or three buffers, this isn't that bad (again, it doesn't take too much power to keep the drive spinning).  But if you increase the CDROM_READAHEAD_DEPTH too high, you can actually get into a situation where playing back the audio samples takes so long that the CD drive decides to spin down the disk.  And that means that the next read, the drive spins up again.   And that's bad.  On a laptop, the "read it all into memory" version may actually be better from a power standpoint (it may cause the system to page however, which is bad - there are always trade-offs).

The fourth reason that this code isn't ready for prime time is what is known as "stitching".

You see, audio CDs were never designed to be played in a computer.  Instead, they were intended to be played back in a commercial CD player, with a simple track next/track previous command.  On those devices, it wasn't critical that data be able to be read reliably.

So it turns out that if you ask a CD-ROM drive to read bytes on the audio CDfrom block 253962 to 253972, you might actually get the contents of blocks 253961 to 253971, or the contents of blocks 253963 to 253973.  You can't predict what the actual data that's read from the disk will be. This limitation doesn't happen to CD-ROM disks because CD-ROMs were designed for accurate location.

Because you can't reliably read the data, you need to "stitch" together the samples you read with the samples that you last read.  That involves DSP and sample matching logic that's beyond my ability to describe in the context of a blog post.  But essentially the idea is that you match the samples at the start of the incoming data block with the samples at the end of the previous data block and look for overlaps.  If you find overlaps, then you slide the incoming block over until the overlapped samples line up.

If you don't really care about full fidelity rendering, then this probably doesn't matter.  But if you do, then you need to care about this issue.

Apple's got a pretty good description of their implementation of DAE and stitching (which showed up in Mac OS9) in their Tech Note 1187

And to be fair - Anonymous posted a pretty good description of the stitching issue in their comment on the first post in this series.