How To: Rip Your CDs (For The Lazy Geek)

 

When was the last time you listened to a CD? That’s right, years ago. And yet you probably have a couple racks full of the suckers wasting space in your living room. It’s time to rip them once and for all, throw them in a box, and dump them in the basement (note: the less ethical might want to sell them after ripping.)

There are some commercial CD ripping services around; they seem to come in at around $1.20 per CD for decent quality rips. Depending on how many CDs you have, this may be a good bet. Check out Rip Digital or Moon Dog Digital for a couple of options.

If you’re interested in fine tuning the results, then there’s another solution. The following howto is geared at Linux users. I appreciate and will incorporate any suggestions you have for improvement.

You’ll need a CD jukebox; I bought a PowerFile C200 from Ebay for $99. These monsters used to retail for $1800 when they first came out. It has a front loading slot, two internal DVD ROM readers, and will hold 200 CDs. It connects to your PC using a 6->6 pin firewire cable. There are probably alternate jukeboxes that’ll do the job just as well; check out the MTX Compatibility List to make sure the device you’re looking at is supported.

Installing the software

You will need to install mtx, goobox, timeout and abcde (and dependencies, e.g. normalize-audio, lame, etc etc); on Ubuntu, these are all in standard repositories, so apt-get is your friend.

Configuring the jukebox

When you connect the jukebox, you should see some activity in /var/log/messages; verify that nothing looks amiss. For some reason, I had to connect the firewire cable after powering on for the device to be recognized.

Creating Udev Rule For The Jukebox Device

First, you need to configure udev so that the jukebox device is named what mtx expects it to be. (Not strictly necessary, as mtx accepts a device name on the command line, but easier to deal with.)

The jukebox will initially mount on /dev/sgN; the first thing you need to do is figure out N. To do this, type:

for device in `ls /dev/sg*`; do echo $device; sudo mtx -f $device inquiry; done

One of the devices will be listed as: “Product Type: Medium Changer”. For the purposes of this howto, assume that its name is /dev/sg7.

Now you need to get udev to dump info about this device, so that you can add a filter for it in the udev rules. To do this, type:

udevadm info --query=all --attribute-walk --name=/dev/sg7

Look for the block that contains ATTRS{ieee1394_id}, e.g.

KERNELS=="51:0:3:0"
...
ATTRS{vendor}=="Escient "
ATTRS{model}=="Powerfile C200 "
ATTRS{rev}=="079 "
...
ATTRS{ieee1394_id}=="003060f200003898:3:2"

Now you’re ready to create the udev rule that’ll assign a predictable device name. To do this, create the file /etc/udev/rules.d/05-jukebox.rules, with the contents as follows:

SUBSYSTEM=="scsi_generic",ATTRS{ieee1394_id}=="003060f200003898:3:2", SYMLINK="changer"

Note that the ieee1394_id should be swapped out to match the one you obtained for your device, above. Now, turn off your jukebox, and turn it back on. It should be mapped to /dev/changer, which is what mtx expects by default. You can now type:

sudo mtx status

…to see the status of the jukebox.

udevadm trigger --action=change

…will tell udev to pick up the rules.

Figuring Out The CD Drives’ Devices (/Modifying Udev Rules)

You should be able to pull a similar trick to the above to get the names of the CD drives’ devices, but I couldn’t make it work. So the following is a quick hack.

ls /dev/disk/by-id/ -al | grep ieee

…will give you something like:


lrwxrwxrwx 1 root root 9 2010-01-30 17:10 ieee1394-003060f200003898:1:0 -> ../../sr3
lrwxrwxrwx 1 root root 9 2010-01-30 17:10 ieee1394-003060f200003898:2:1 -> ../../sr2

…, equating to /dev/sr2 and /dev/sr3. You’ll need these in a second. (Note that this is far from ideal, as the device names may change when you powercycle the jukebox.)

Update

I figured out the trick after all. After you do the dry run (below), you should see lines like this in /etc/udev/rules.d/70-persistent-cd.rules:


SUBSYSTEM=="block", ENV{ID_CDROM}=="?*", ENV{ID_MODEL}=="DVD-ROM_SD-M1212", ENV{ID_REVISION}=="1R22", SYMLINK+="cdrom%n", ENV{GENERATED}="1"
SUBSYSTEM=="block", ENV{ID_CDROM}=="?*", ENV{ID_MODEL}=="DVD-ROM_SD-M1212", ENV{ID_REVISION}=="1R22", SYMLINK+="dvdrom%n", ENV{GENERATED}="1"
... and again for the second drive

Modify as follows (change generated to 0, add a symlink):


SUBSYSTEM=="block", ENV{ID_CDROM}=="?*", ENV{ID_MODEL}=="DVD-ROM_SD-M1212", ENV{ID_REVISION}=="1R22", SYMLINK+="cdrom%n powerfilecd%n", ENV{GENERATED}="0"
SUBSYSTEM=="block", ENV{ID_CDROM}=="?*", ENV{ID_MODEL}=="DVD-ROM_SD-M1212", ENV{ID_REVISION}=="1R22", SYMLINK+="dvdrom%n powerfiledvd%n", ENV{GENERATED}="0"
... and again for the second drive

Finally, refresh udev again:

udevadm trigger --action=change

Dry Run

Let’s walk through this once manually so that you understand what the script is doing, and can adjust it as you need.

Load A CD


sudo mtx load 1
sdparm -C eject /dev/sr3

This tells the jukebox to load the disk in slot 1 into the default drive (drive 0). As an extension to the script, if you wanted, you could use both drives at once. I just use one drive for simplicity.

The sdparm eject command counterintuitively is needed to make the drive show up. I’m not sure why; somebody on the mtx user list gave me this vital piece of the puzzle. Fire up goobox to see if you chose the right device for the drive that has a CD in it; if not, repeat the sdparm command for the other device to verify.

Defeating Automount

If you’re using Gnome, it’ll auto-mount the CD and pop up the application for playing audio CDs; this is obviously annoying (for the purposes of ripping dozens or hundreds of CDs), so launch gconf-editor and modify apps/nautilus/preferences/media_automount* to be false. Restore these settings after you’re done ripping.

Rip The CD


abcde -d /dev/sr3 -NV

abcde is an excellent program which handles the job of CD ripping, cddb metadata lookup, normalization, etc. You will want to check the results after running the above to see if it meets your needs (file size, audio codec quality, etc.) There are lots of customizations you can make to abcde via the options in /etc/abcde.conf. Some that I found to be useful:


ACTIONS=cddb,read,normalize,encode,tag,move,clean
LAMEOPTS='--preset standard'
OUTPUTTYPE="mp3"
MAXPROCS=3
BATCHNORM=y

Unload The CD


sdparm -C stop /dev/sr3
sudo mtx unload 1

This is simply the inverse of the first step.

The Script

This script will let you rip a series of CDs in a batch. Simply load them into the player, kick off the script (run with sudo; the mtx commands need it), and come back the next morning to a folder full of mp3s. NUM should be set to the number of CDs that you have loaded; you can find this out using the mtx status command. If you want to resume from a given CD, change the FROM parameter. The script accounts for some error conditions I encountered; e.g. scsi_read errors (indicating scratched disks etc) don’t kill cdparanoia (which keeps chugging along for hours on the same track), so to save time I just kill abcde altogether if this happens.

Note that you can continue to load CDs into the jukebox while the script is running.

Version 1

Click to view. This version may be simpler to use as a base for your own needs.

#!/bin/bash

FROM=39
NUM=200
TIMEOUT=2400
DEV=/dev/powerfilecd1

function log {
    echo `date` $1
}

function cleanup {
    pkill abcde
    rm -fr abcde*
    echo Rip of cd $1 failed >> error.log
    # todo, kill cdparanoia for the track, and/or reinvoke abcde for next track
}

function monitorrip {
    {
	while read line
	    do
		log "$line"
		# cdparanoia keeps chundering forward even if there's a scsi_read error
		# this slows things down unacceptably, so we'll just kill the job and move forward
		echo "$line" | grep "scsi_read error" && cleanup $1 && break
		# cdparanoia sometimes seems to get stuck, so run abcde within timeout
		echo "$line" | grep "Timeout: aborting command" && cleanup $1 && break
	    done
    } | tee
}

function ripcd {
    # send stdout from abcde to the monitorrip function
    # run within timeout
    timeout $TIMEOUT abcde -d $DEV -NV 2>&1 | monitorrip $1
}

function loadcd {
    date
    # load the cd into the drive, and send the sdparm eject command to make udev notice
    mtx load $1
    sdparm -C eject $DEV
}

function unloadcd {
    date
    # send sdparm and eject to flush the cd devices
    sdparm -C stop $DEV

    # not strictly necessary, but your desktop environment might be automounting,
    # so this will force a umount if applicable
    eject $DEV
    sleep 7

    mtx unload $1
}

function rip {
   for n in `seq $FROM $NUM`
   do
	echo ____________________________________________________________
	echo RIPPING CD $n
	echo ____________________________________________________________
	date
	loadcd $n $1
	ripcd $n
	unloadcd $n $1
   done
} 

function reset {
    echo Resetting jukebox position
    # this shouldn't be necessary but seems to fix an error condition
    mtx first
    mtx unload 1
}

reset
rip | tee output$1.log

Version 2

Changelog

  • Try to recover from offlined devices by bouncing them
  • Drive parameter specified on mtx commands
  • More consistent logging
  • Better error handling
#!/bin/bash

FROM=1 # which CD to start from
NUM=200 # how many CDs are loaded
TIMEOUT=2400 # how many seconds a CD rip can take before we kill it

DEV=/dev/powerfilecd2 # the device to use for ripping
DRIVE=1 # the mtx drive number matching the device, either 0 or 1
DEV_CHANGER=/dev/sch0 # this is the device lsscsi reports... TODO why doesn't /dev/changer work?
UNKNOWN=Unknown_Artist-Unknown_Album # what abcde will name an unknown album

function log {
    echo `date` $@
}

function cleanup {
    pkill abcde
    rm -fr abcde*
    if [ $# == 1 ]
    then
        log Rip of cd $1 failed >> error.log
    fi
    # todo, kill cdparanoia for the track, and/or reinvoke abcde for next track
}

function online_device {
    # bounce the drive device
    # TODO extract device codes to param
    log Bouncing the drive SCSI device $BUS_DRIVE
    echo "scsi remove-single-device $BUS_DRIVE" > /proc/scsi/scsi
    echo "scsi add-single-device $BUS_DRIVE" > /proc/scsi/scsi
    sleep 5
}

function online_changer {
    # bounce the changer device
    # TODO extract device codes to param
    log Bouncing the changer SCSI device $BUS_CHANGER
    echo "scsi remove-single-device $BUS_CHANGER" > /proc/scsi/scsi
    echo "scsi add-single-device $BUS_CHANGER" > /proc/scsi/scsi
    sleep 10
}

function monitorrip {
    {
        while read line
            do
                log "$line"
                # cdparanoia keeps chundering forward even if there's a scsi_read error
                # this slows things down unacceptably, so we'll just kill the job and move forward
                echo "$line" | grep "scsi_read error" && log "SCSI Read Error" && cleanup $1 && echo 1 > rip_status && break
                # cdparanoia sometimes seems to get stuck, so run abcde within timeout
                echo "$line" | grep "Timeout: aborting command" && log "Timeout Error" && cleanup $1 && echo 1 > rip_status && break
                # if the drive disappears
                echo "$line" | grep "CDROM device cannot be found" && log "Device Offline Error" && online_device && cleanup && echo 1 > rip_status && break
                echo "$line" | grep "Perhaps there's no CD in the drive" && log "Device Offline Error" && online_device && cleanup && echo 1 > rip_status && break
                # completion
                echo "$line" | grep "Finished." && log "Complete" && echo 0 > rip_status && break
            done
    } | tee 

    # wait for abcde process to finish
    while [ ! -f rip_status ]
    do
       sleep 1
    done

    read ret < rip_status
    log Return code $ret from rip monitor
    return $ret
}

function ripcd {
    # send stdout from abcde to the monitorrip function
    # run within timeout
    log "timeout $TIMEOUT abcde -d $DEV -NV 2>&1 | monitorrip $1"
    timeout $TIMEOUT abcde -d $DEV -NV 2>&1 | monitorrip $1
    success=$?
    log "Done ripping"
    # if abcde has not identified it, then make sure the unknown/unknown won't be overwritten next time
    [ -d $UNKNOWN ] && mv $UNKNOWN $UNKNOWN_$1
    echo Return code $success from rip cd
    return $success
}

function mtx_cmd {
    # run an mtx command
    log mtx $@
    mtx $@
    ret=$?
    log mtx return code $ret
    return $ret
}

function mtx_wrap {
    # Try an mtx command three time - if the first or second fails, try resetting the changer
    mtx_cmd $@
    [ $? == 0 ] && log OK && return 0
    [ $? == 1 ] && log Trying again && online_changer && mtx_cmd $@
    [ $? == 0 ] && log OK && return 0
    [ $? == 1 ] && log Trying again && online_changer && mtx_cmd $@
    [ $? == 0 ] && log OK && return 0
    [ $? == 1 ] && log Exiting because changer is screwed up && exit 1
}

function loadcd {
    # load the cd into the drive, and send the sdparm eject command to make udev notice
    mtx_wrap load $1 $DRIVE
    sdparm -C eject $DEV
    [ $? == 15 ] && log Cannot find drive && online_device && sdparm -C eject $DEV
    [ $? == 15 ] && log Still cannot find drive - giving up && exit 15
}

function unloadcd {
    # send sdparm and eject to flush the cd devices
    sdparm -C stop $DEV

    # not strictly necessary, but your desktop environment might be automounting,
    # so this will force a umount if applicable
    eject $DEV
    sleep 7

    mtx_wrap unload $1 $DRIVE
}

function rip {
   #if [ $1 == 1 ]
   #then
   #   let FROM+=1
   #fi

   for n in `seq $FROM $NUM`
   do
        log  ____________________________________________________________
        log RIPPING CD $n
        log ____________________________________________________________

        loadcd $n $1
        ripcd $n
        # Retry the rip in case it was an intermittent error
        [ $? == 1 ] && log "CD rip failed, retrying once" && ripcd $n
        unloadcd $n $1
   done
} 

function reset {
    log Resetting jukebox position
    # this shouldn't be necessary but seems to fix an error condition
    mtx_wrap first $DRIVE
    mtx_wrap unload 1 $DRIVE
}

function parse_lsscsi {
    {
        while read line
        do
            echo "$line" | grep "$1" | sed "s%\[\([0-9]*\):\([0-9]*\):\([0-9]*\):\([0-9]*\).*$1%\1 \2 \3 \4%"
        done
    } | tee
}

function resolve_device_name {
    dev=$(readlink $1)
    [ $? == 1 ] && echo $1 && return 0
    echo /dev/$dev
}

function validate {
    log Changer $DEV_CHANGER ${BUS_CHANGER:?"Changer is not initialized"}
    log Drive $DEV ${BUS_DRIVE:?"Drive is not initialized"}
    err=0
    [ BUS_CHANGER == "" ] && err=1
    [ BUS_DRIVE == "" ] && err=1
    [ err == 1 ] && echo "Changer and/or drive devices are not initialized. Exiting" && exit 1
}

dev=$(resolve_device_name $DEV)
BUS_DRIVE=$(lsscsi 2>&1 | parse_lsscsi $dev)
dev=$(resolve_device_name $DEV_CHANGER)
BUS_CHANGER=$(lsscsi 2>&1 | parse_lsscsi $dev)

validate
reset
rip | tee output$1.txt

FAQ

Hot damn, the changer is not being recognized?

Start by tailing /var/log/messages. Turn off the jukebox, and let it rest for 10 seconds (this may be superstition, or not long enough; but it seems to be long enough to reset internal state.) Unplug the firewire cable from the back of the jukebox. Turn it on, and verify that the carousel does one complete circuit. Plug the firewire cable into one of the other slots.

Now watch /var/log/messages. You’ll see each of the devices be recognized and initialized; the drives first, and then the changer. In a perfect world, all will come up, and you will not see any “Device offlined” messages.

If the device is not recognized (i.e. you don’t see any activity in /var/log/messages) then you have bigger issues.

Verify using lsscsi to see if all three devices are present.

Like ZOMG, it is being offlined consistently.

This happens when you turn on the jukebox when there is an audio CD in one of the drives. Why this should matter I don’t know. The trick as follows.

Watch /var/log/messages until you see a log like:

ch 16:0:3:0: Attached scsi changer ch0

Immediately, in another terminal, issue:

sudo mtx status

Having done this, quickly review the status, and verify which CD is in the drive. I.e. at the top of the output, you’ll see something like:

Data Transfer Element 0:Empty
Data Transfer Element 1:Full (Storage Element 18 Loaded):VolumeTag =

Now issue (for the above example):

sudo mtx unload 18 1

Now that the CD is ejected, you can reset the machine (again) via the turn off / unplug / turn on / plug back in method above.

Holy mother of all things craven, the wretched thing won’t eject the CD and is still being offlined.

When this happens you need to eject everything from the carousel, and then repeat the procedures above. I think this happens when the device gets confused about what’s where in the carousel, and forcing it to empty it is sufficient to get back on track. To do this, hit the eject button on the front of the device, so that it says “discs” rather than “disc”. It’ll pop out each CD in sequence. One done, reset, then issue mtx unload as above, then reset again, and reload some CDs. Annoying, yes…

The carousel sometimes goes crazy and spins up repeatedly.

This does not appear to harm anything. The script should cope with taming this state when it gets to the next CD to be ripped. If it annoys you, cancel the script, manually unload the CD, and reset the device per above.

OK, I’m ready to go officespace on the device. It keeps being offlined, and/or spins constantly.

Your device probably has a slightly loose connection somewhere that’s causing trouble when the device spins initially. Turn it off, unplug it, flip it over and unscrew all of the (20+) screws. Lift the lid. Watch out for the exposed power supply – i.e. do not touch it. Test the various control boards to see if there’s movement on them (on mine, the CD control boards were loose; I wedged them in place using a piece of polystyrene.) Test all of the cable connections to see if any are loose. I found that some of the tape adhesive had become loose and the connections were loose underneath; you might benefit by pulling off the tape (gently) and pushing the connections. You can reattach three of the feet of the device to the plastic struts and run it without the metal cover without harm, as long as the surface underneath is non-conductive / static generating – a good way to experiment with different connections.

mtx inventory doesn’t work.

Yeah, I wish it did. mtx eject also doesn’t work.

Are there any secret modes?

You betcha. Disconnect the device from your computer, power it off and on again. Press RIGHT RIGHT LEFT RIGHT RIGHT, and it should change the display to read “Service Mode”. Now you can cycle through service modes using the Mode button (particularly useful: force eject a disk from a drive.)