Musicbox: Turning an old PC into a CD Jukebox

Installed Red Hat Fedora 2 from a download on September 4, 2004. Ran the up2date program on the same day. Because the cdda2wav program was giving ioctl failures, I also updated the kernel (on September 5th). Remember to install system tools and development tools and kernel development tools (so that you can recompile the kernel and add new applications).

To save startup time and memory, I navigated the start menu to 'System Settings/Server Settings/Services' and turned off ones that I didn't need (cpuspeed, cups, gpm, kudzu, mdmonitor (no RAID), netfs, nfs, nfslock, pcmcia, portmap, rpcgssd, rpcsvcgssd, sendmail). I left sshd running because I planned to edit the configuration and such through the network port from another computer. I navigated the start menu to 'Preferenced/CD and DVD' and turned off action on the insertion of Audio CD.

When the system first came up, the volume for the CD player and the sound were set to zero. Adjusting them made sound work on the computer. The same problem happened with the PCM volume control; in this case the control in the GUI was shown as pretty high even though the actual volume was at zero -- I must have a bum card that needs lots of attention. The 666MHz Pentium III machine with 128MB RAM is enough to play a CD at the same time that it is running "makewhatis" and doing the up2date package dependency checking and running a screen-saver; so far so good.

Setup

I made a directory named musicbox under my home directory, and in that directory created three subdirectories: bin, src, and cds. The bin directory holds the following shell scripts, which are used to do lots of the work for the musicbox; this directory was added to the path in my .bash_profile. The src directory holds source code for the C programs I wrote to help with this. The cds directory holds subdirectories for each album as described below.

musicbox_make_directories.txt: To make the directories that will hold the albums, I created a tcsh script, listed here. I ran it to create a tree of places to store the music files:

	csh
	cd
	mkdir musicbox/cds
	cd musicbox/cds
	make_directories.txt

This tree has a directory called 'by_id' that stores the songs from each CD that has been read based on its hexadecimal ID number. The cds tree also has directories named 0 through 9 at the top level. Underneath each of them is a set of directories named 'a' through 'z'. Underneath each of these, soft links will later be created that point to actual CD locations. This provided a large number of child nodes named with a digit and two letters: 0aa through 9zz. These will each hold one CD's worth of data, and form a tree of categories into which they can be placed that looks like a jukebox tree.

The tree has one more directory called "all" that has links to 0-9 within it. It is used to play all of the available songs.

Scripts and how it all works

All of the scripts and programs created for this project have names that begin with the prefix musicbox_. They are stored in the musicbox/bin directory, which should be added to the the PATH environment variable.

They share state in a directory pointed to by the musicbox_state_directory.txt script, which prints the full path to this directory (in my case, ~/musicbox/state). This script should be modified to point to the proper place. The musicbox_cds_directory.txt and musicbox_sounds_directory.txt files should likewise point to the proper locations.

Reading the CDs and playing them at the same time: I first tried reading the CDs into directories as WAV files and then planned to compress them to MP3 files later. After waiting five seconds to let the program fill something into the file, I would launch the musicbox_play.txt file and point it at the directory being written to. This worked great -- the writing always stayed ahead of the reading once it got started.

I then got the bright idea that I could probably compress them while reading them and just save them to MP3 files and never have to save the WAV files at all. This works splendidly most of the time (although it seems to make the reading of the CDs take even longer). Sometimes, however (in particular, consistently on the first song from a BB King album), the MPEG decompressor would fail with all sorts of errors about changing bitrates. I'm not sure what caused this to fail only on some files, but it made me come up with a new scheme whereby the files are copied both to the conversion program and copied into the /tmp/musicbox_wavplaydir temporary directory (as WAV files). The musicbox_play.txt program is aimed at the temporary directory, so it reads the raw WAV files. The temporary directory is wiped each time a new disc is inserted (after any previous play commands are killed) to avoid cluttering it up with old songs.

Command Control: A program called musicbox_kbdctl (compiled from code in the musicbox/src directory) runs the show, along with the musicbox_watch_for_disc.txt script. They are both started by musicbox_go.txt, which is launched from a line in /etc/rc.d/rc.local. They and the other scripts communicate based on files in the musicbox/state directory.

Other useful files

Source code for the keyboard control program is in the src directory. Executables needed (mostly scripts) are in the bin directory. Spreadsheet to make a print-out (very long) for all the titles is here, with a PDF here.

Useful commands and tidbits

cdda2wav is a sampling utility that dumps CD audio data into wav sound files. Note that you may be able to use this with the tee command to write to the speaker at the same time you are writing to a pipe that is either saving the file to disk or that is sending to a compression program.

When I tried to run this, it failed and asked me to run 'cdrecord -scanbus' to figure out which to use; cdrecord failed, probably because I wasn't root at the time. Running 'cdrecord -scanbus' as root produced scscidev ATA, devname ATA, bus -2, target -2, lun -2. It also produced a warning: 'Using badly designed ATAPI via /dev/hd* interface'. It ended up working when I ran 'cdda2wav -t 1 -D /dev/cdrom'; this also stopped the CD player from making and progress while it was happening, in fact it stalled the player even when it was done.It seemed to read not at full speed. Stopped the CD player and reran; cdda2wav provides lots of useful info (number of tracks, discid) and then ran slowly again. Adding '-n 50' did not change the speed. Adding '-S 32' did not seem to change the speed by much if at all. Running 'cdda2wav -B -D /dev/cdrom' started copying into files in the local directory, audio_01.inf and audio_01.wav for the first track, and then higher numbers for the other tracks. The .inf files were text files that included the discid and other information. It still copied fairly slowly (maybe a minute per track, not making the CDROM spin up to maximum speed). Note that you can use the '-e' parameter to echo it to the soundcard at the same time; this makes it even slower, only recording the file in real time as it is played out.

Running with '-P 0 -n 100 -l 200' made things much faster, but this caused problems. There was some noise in the songs (even in the .WAV files). Setting "-P 1" rather than "-P 0" on the cdda2wav fixed the problem; of course, it also makes the reading time radically longer. Well, you can't have everything...

sox is a command line program that can convert most popular audio files to most other popular audio file formats. It can read from standard input (file name '-') and write to standard output if needed. It can write to /dev/audio (I suppose), but can it read directly from the CD? It cannot read directly from the CD. It does not include an MP3 encoder by default; you need to have the lame libraries to do that. There is a play program included with sox that I use to send the audio files to the audio device. It can directly play WAV files; MP3s have to be decoded first (see lame, below).

lame is a program to convert from WAV format to MP3 format. It can be found on SourceForge here. I downloaded version 3.94b from www.tucows.com under Linux/Multimedia. Built it and installed by following the INSTALL info file and doing: './config ; make'. Copied 'lame' into musicbox/bin. Lame can also decode the files, which can then be sent to the play program using: 'lame --quiet --decode FILENAME.mp3 - | play -t wav -'. Lame can be run as a filter, so that the compression happens during the cdda reading process by piping the cdda2wav output into lame. I used the "-h" option, which does higher-quality encoding. The processor load only goes to 34% or so when encoding, which does not hinder concurrent playback.

aumix is a command-line program to set audio parameter; alsamixer is another one for the ALSA soundcard driver. This is what I needed to set the volume levels of the playback. The particular commands I found to be useful included: 'aumix -w 100' sets the PCM volume to 100%. 'aumix -v XXX' sets the main volume to XXX% (0 to 100). I used this before each playback because the sound card on the PC I was using seemed to randomly switch the volumes for various parts of the mixer. It turns out that the command-line interface doesn't let you specify everything you want, so I use it with the -S mode to save a settings file, then copy and edit the file, then use the "-f infile" interface to load the settings from that edited file, pulling the file into a script and re-creating it as needed in /tmp.

One time when I booted it up with a CD in the drive, it started downloading and set the volume and started playing, but no sound came out. Aumix showed that the PCM was 100 and the speaker was 60 and the balances were good. Play was running, and sox under it. Just no sound. Using aumix in interactive mode, I adjusted all of the gains up and down and didn't get any sound. Aha! Adjusting the balance on the PCM made the sound suddenly work! It isn't clear how to set the balance from the command-line using aumix. When aumix ran again automatically, it screwed the balance up again and I had to fix it manually! Needed to switch to the file-based interface.

/etc/inittab is the file that sets how the computer boots and set the getty programs running on the consoles. I modified the default run-level to 3 from 5 (don't run X to save memory). The next step is to remove the mingetty job from tty1 so that I can run the keyboard-controlled program in /etc/rc.d/rc.local and have it read from the keyboard at boot time. There is also a line in this file that says what to do when control-alt-delete is pressed: I changed it from reboot to shutdown so that it acts like a remote "off" switch for the jukebox.

/etc/rc.d/rc.local To let my programs play their output, I became root and changed permissions on /dev/audio and /dev/dsp so that the group and user read/write permissions were set. To let me read from the CD device, I had to become root and go see where the cdrom was (/dev/hdc in my case), then 'chmod 666 /dev/hdc'. To adjust volume properties, I had to set permissions on /dev/mixer. All of this has to be done after each reboot, so it should happen in /etc/rc.d/rc.local. This is also where the keyboard-reading program should be run.

Job control: When it is time to kill a script that has called the "play" command that is going through a list, we need to kill the entire process group using "kill -TERM -pid", where the "-" in front of the PID means "all processes in the group. If you want to kill only the play process and leave the parent script going (so that it skips to the next song), look for a job called "sox", which "play" apparently calls to do its work for it.

Redirecting stdio: To let the musicbox_kbdctl program read from the keyboard, I disable the mingetty() running on /dev/tty1 (as described under /etc/inittab), set permissions on /dev/tty1 so that anyone can read and write it, and then start the kbdctl program with its input redirected from that port (in musicbox_go.txt).

The CD player runs when an audio CD is put into the drive. I want to figure out how to make my programs run when this happens. It looks like this may be something that Gnome is doing (gnome-cd gets run, and there is some kind of factory running on the CD with a Gnome name. Yep -- it can be configured under per-user preferences in the Gnome control panel for what program to run when an audio CD is inserted. I turned this off in the control panel and then have the program that watches for CD insertion try to look up the disc id once a second to notice when a CD has been inserted.

The sound card on the motherboard replays blips of sound when starting things; maybe the first buffer from last time or the last buffer or something. Try padding every sound with silence. Try making a script that drops the volume to zero and then plays a small silent buffer and then sets the volume to what it should be; run this script before saying anything -- maybe the volume control script should do all of this mess. Also run it between playing multiple songs. ARGH!!!! It seems like it plays this horrible burp at the END of the sound, because when I turn down the volume ahead of time it doesn't help! How horrible! After getting everything to work, I removed the monitor and rebooted. Then, the programs seemed to run, and the permissions on the devices seem correct, but no sound comes out. Unplugging the jack doesn't help. Setting the volume didn't help. Just before this, I had modified the play command to use a skip_burp program, which I then incorporated into the volume control program. Lame was running. Sox was running. Not clear what the problem could be. Actually, it is playing -- just VERY VERY QUIETLY. If I got right up next to it, I could hear it a teeny bit. The problem was that the PCM balance was in a wacky state (I found this by playing around in interactive mode). Very wierd: When I do two aumix settings one right after the other, with the first having things 40/60 and the second 100/100 on the channels I think I want, I get a brief burst of sound and then quiet: when I set the second set to do 100/90, I get the same thing. When I just use the 40/60, it seems to be as loud as it used to be with 100/100... Making it 50/50 quiets it all down again. Making it 80/50 is also very quiet. Making it 80/90 sets the volume to about what I would expect 50/50 to do. Setting it to 1/1 bumps the volume back up again. What in the WORLD is going on? The hardware dump of the settings after doing the 1/1 setting has the line1 set to 100/100 (others are 0 or 1, as set). This persists even if I set the line1 to 0/0 in the configuration file. It also has video set at 100/100. On the interactive interface, the volumes of the Vol and PCM channels (the ones that seem to matter) jump around when the balance is adjusted. This is just insanity! If, on the command line, I set the volume using 'aumix -vXXX ; aumix -v1' it seems to set the volume to the XXX value, where it goes from 0 to 100 -- as if there is a lag in the settings; however, setting it to 1 after being set to 1 does nothing. Insanity -- time to buy another sound card and stick it in... That fixed it!

60Hz hum coming from the machine; more noticible downstairs with an unshielded cable. Varies greatly when wiggling the connector and moving the cable, so it looks like shielding problem. It was; a better cable fixed the problem.

If you skip more songs than have been sucked in when a new disc is installed, it of course stops playing.

XXX It should say something when it fails to read a disc in the middle so the person knows why it got spit out. It also needs to unlink it from the tree when it is deleted.

XXX Want to figure out how to make the power switch do shutdown rather than power-off. It looks like APCI is used for this, and I had shut off the daemon; doh! Had a look at the Howto. It turns out that the ACPI daemon was set up to do this by default (I had shut it off the first time I installed Linux -- a mistake). There is a sample file in the /etc/acpi/events directory that makes it do this. This didn't work automatically; the power switch still turns it off hard. Check the BIOS to make sure it is using the correct power management (ACPI vs. APM).

XXX Turn off kudzu and cups in the startup scripts.

Useful documentation

The Open Sound documentation at http://www.opensound.com/pguide/oss.pdf has a description of how the open sound driver API and hardware controls work.