#!/bin/sh IFS=' ' VERSION="0.6f" ########################################################################### # makeflac-0.6f # Jason Kaufman # 2007-01-26 # # Well so far a lot of stuff is hardcoded and can't be removed. This is is # due to the fact that this started out as a quick hack to rip 50 CDs in one # day. Nowadays, it can do much more (tagging-wise) and has better (but # far from perfect) error-checking. It does almost everything I want a # ripper to do, but it probably doesn't do everything YOU want it to do. # You have been warned. # # REQUIRES: cdda2wav, cdparanoia, flac, metaflac # # BEGIN CONFIGURABLE OPTIONS # These must be properly set or makeflac will break # CD-ROM device to rip from DEVICE="/dev/cdrom" # Number of CPUs in system MAX_ENCODE_PROCS=2 # The file in each album directory where track names are stored TRACKNAMES=".tracknames" # END CONFIGURABLE OPTIONS # Usage: cd Artist_Name/Album_Title; vim ${TRACKNAMES}; makeflac ALBUM_DATE # e.g., cd Cult,_The/Electric; vim .tracknames; makeflac --date 1987 # ELABORATION OF USAGE # #1. Artist_Name/Album_Title # # makeflac at this point assumes an underscore-delimited, non-escaped- # punctuation-containing directory structure. You must execute makeflac # from within this directory # # e.g. Cult,_The/Electric is legal. # e.g. Foobar/('My_Super-133t Mix') is probably not. # Don't cry if your illogical scheme doesn't work with makeflac # #2. vim ${TRACKNAMES} # # Place the name of each track, one to a line in this file. Do not # leave blank lines in the file. Punctuation is OK, a filter will sanitize # it when it names the flac, while retaining the original form for tagging. # NOTE: If your album has multiple discs, specify the tracks in different # ${TRACKNAMES} like ${TRACKNAMES}1 (the first disc), ${TRACKNAMES}2 # (the second disc), and so on. # #3. makeflac --date ALBUM_DATE # At this point all you need is the date of the album release. # # SUMMARY # 1. mkdir and cd to Artist_Name/Album_Title # 2. edit a ${TRACKNAMES} for each disc # 3. insert CD # 4. makeflac ALBUM_DATE # # Screwing with anything after this will probably break makeflac ############################################################################ SOURCEMEDIA=CD SINGLE=1 export INFOFILE=".info" export LOGFILE=".log" rm -f ${LOGFILE}; export ALBUM=`basename ${PWD} | sed -e 's/_/\\ /g'` export ARTIST=`basename \`eval "dirname ${PWD}"\` | sed -e 's/_/\\ /g'` export ESYMB="*** Error:" ############################################################################### ############################################################################### # Args: $1: number of seconds # Reqs: expr function compute_time () { QTIME=${1} # Compute hours (better not take days) QHOURS=`expr ${QTIME} / 3600` Q1=`expr ${QHOURS} \* 3600` QTIME=`expr ${QTIME} - ${Q1}` # Compute minutes QMIN=`expr ${QTIME} / 60` Q1=`expr ${QMIN} \* 60` QTIME=`expr ${QTIME} - ${Q1}` # Compute seconds QSEC=${QTIME} if test ${QHOURS} -gt 0; then echo -n "${QHOURS}h" fi if test ${QMIN} -gt 0; then echo -n "${QMIN}m" fi echo -n "${QSEC}s" } ############################################################################### ############################################################################### function version_info () { echo "makeflac-${VERSION}"; echo "Jason Kaufman " echo "" } ############################################################################### ############################################################################### # Args: $1: track number to encode (1, 2, 3, ... , 99) function encode_track () { trap 'exit 1;' 1 2 3 15 if test -z "${1}"; then echo "${ESYMB} Not enough options passed to encode_track" exit 1; fi COUNT=${1} if test ${COUNT} -lt 10; then TRACKNUMBER=0${COUNT} else TRACKNUMBER=${COUNT} fi TMP_LOG=".${COUNT}.log" # ISRC for this track if test -f ${INFOFILE}; then ISRC=`grep -m 1 "${COUNT} ISRC" ${INFOFILE} | cut -d: -f4 | sed -e 's/ //g' -e 's/\^M//g'` fi # Setting up some comments for encoding A_LINE=`sed -n ${COUNT}p < ${TRACKNAMES}` T_ARTIST=`echo ${A_LINE} | cut -d '\' -f1 | sed -e 's/^ *//' -e 's/\ \+$//'` T_TITLE=`echo ${A_LINE} | cut -d '\' -f2 | sed -e 's/^ *//g'` if test "x${T_TITLE}" != "x${T_ARTIST}"; then TITLE=${T_TITLE} ARTIST=${T_ARTIST} else TITLE=${T_ARTIST} fi STITLE=`echo ${TITLE} | sed -e 's/ /_/g' -e 's/_(.*//' -e 's/#//g' -e "s/'//g" -e 's/\!//g' -e 's%/%_-_%g' -e 's/\.//g' -e 's/\?//g' -e 's/^(.*)_//' -e 's/\&/And/g' -e 's/\:/_-_/g' -e 's/_\+/_/g' -e 's/;/,/g'` ENCODING="Encoded on: `date +%Y-%m-%d:%H%M`" echo ">>> Encoding Track ${COUNT} as ${TRACKNUMBER}-${STITLE}.flac with info:" >> ${TMP_LOG} echo "ALBUM: ${ALBUM}" >> ${TMP_LOG} echo "ARTIST: ${ARTIST}" >> ${TMP_LOG} if test -n "${CDINDEX_ID}"; then echo "CDINDEX: ${CDINDEX_ID}" >> ${TMP_LOG} CDINDEX_ID_OPT="--tag=CDINDEX=${CDINDEX_ID}" fi if test -n "${CDDB_ID}"; then echo "CDDB: ${CDDB_ID}" >> ${TMP_LOG} CDDB_ID_OPT="--tag=CDDB=${CDDB_ID}" fi if test -n "${COMMENT}"; then echo "COMMENT: ${COMMENT}" >> ${TMP_LOG} COMMENT_OPT="--tag=COMMENT=${COMMENT}" fi if test -n "${DATE}"; then echo "DATE: ${DATE}" >> ${TMP_LOG} DATE_OPT="--tag=DATE=${DATE}" fi if test -n "${DISCNUMBER}"; then echo "DISCNUMBER: ${DISCNUMBER}" >> ${TMP_LOG} DISCNUMBER_OPT="--tag=DISCNUMBER=${DISCNUMBER}" fi echo "Encoding: ${ENCODING}" >> ${TMP_LOG} if test -n "${ISRC}"; then echo "ISRC: ${ISRC}" >> ${TMP_LOG} ISRC_OPT="--tag=ISRC=${ISRC}" fi if test -n "${LOCATION}"; then echo "LOCATION: ${LOCATION}" >> ${TMP_LOG} LOCATION_OPT="--tag=LOCATION=${LOCATION}" fi if test -n "${MCN}"; then echo "MCN: ${MCN}" >> ${TMP_LOG} MCN_OPT="--tag=MCN=${MCN}" fi if test -n "${SOURCEMEDIA}"; then echo "SOURCEMEDIA: ${SOURCEMEDIA}" >> ${TMP_LOG} SOURCEMEDIA_OPT="--tag=SOURCEMEDIA=${SOURCEMEDIA}" fi echo "TITLE: ${TITLE}" >> ${TMP_LOG} echo "TRACKNUMBER: ${TRACKNUMBER}" >> ${TMP_LOG} echo "" >> ${TMP_LOG} flac -V -8 --delete-input-file -s --tag=ALBUM="${ALBUM}" ${ALBUM_ID_OPT} --tag=ARTIST="${ARTIST}" ${ARTIST_ID_OPT} ${CDINDEX_ID_OPT} ${CDDB_ID_OPT} ${COMMENT_OPT} ${DATE_OPT} ${DISCNUMBER_OPT} --tag=ENCODING="${ENCODING}" ${ISRC_OPT} ${LOCATION_OPT} ${MCN_OPT} ${SOURCEMEDIA_OPT} --tag=TITLE="${TITLE}" --tag=TRACKNUMBER="${TRACKNUMBER}" ${TRM_OPT} -o ${TRACKNUMBER}-${STITLE}.flac track${TRACKNUMBER}.cdda.${FILE_EXT} && echo "*** Done encoding Track ${COUNT}" } ############################################################################### ############################################################################### # Args: $1: name of the process to count function get_num_pids () { if test -z "${1}"; then echo "get_num_pids: invalid number of arguments" fi echo "`ps -C ${1} | sed -e 's/^ *//g' -e '/^PID/d' | wc -l | sed -e 's/^ *//g'`"; } ############################################################################### ############################################################################### # Args: $1: track number to wait on function wait_for_track () { if test -z "${1}"; then echo "wait_for_track: invalid number of arguments" fi if test ${1} -lt 10; then THE_NUM=0${1} else THE_NUM=${1} fi FIRST_DATE=0 LAST_DATE=1 while test ${LAST_DATE} -gt ${FIRST_DATE}; do if test -f track${THE_NUM}.cdda.wav; then FIRST_DATE=`date -r track${THE_NUM}.cdda.wav +%s` else FIRST_DATE=0 fi sleep 3; if test ${FIRST_DATE} -ne 0; then LAST_DATE=`date -r track${THE_NUM}.cdda.wav +%s` else LAST_DATE=1 fi done } ############################################################################### ############################################################################### # Parse arguments while test ${#} -gt 0; do case "${1}" in --album ) ALBUM=${2}; shift; shift ;; --artist ) ARTIST=${2}; shift; shift ;; --cddb ) CDDB_ID=${2}; shift; shift ;; --cdindex ) CDINDEX_ID=${2}; shift; shift ;; --comment ) COMMENT=${2}; shift; shift ;; --date ) DATE=${2}; shift; shift ;; --device ) DEVICE=${2}; shift; shift ;; --discs ) NUM_DISCS=${2}; SINGLE=0; shift; shift ;; -h | --help ) version_info; cat << EEOOFF --album <> Specify an album name here to override the directory structure. Useful if the album title contains spaces or other characters that require escaping. This corresponds to the ALBUM vorbis comment. --artist <> Specify an artist name here to override the directory structure. This corresponds to the ARTIST vorbis comment. --cddb <> Specify a CDDB-ID, e.g. "c10bc80e". This corresponds to the CDDB vorbis comment. --cdindex <> Specify a CDINDEX, e.g. "3DS3cxoLgbTkdMA4UvTz7ArVuFc-". This corresponds to the CDINDEX vorbis comment. --comment <> Add an uncategorized vorbis comment here. This corresponds to the COMMENT vorbis comment. --date <> Release date of album, most often this is the release year in YYYY-MM-DD format. This corresponds to the DATE vorbis comment. --device <> CD-ROM device to use for ripping --discs <> Number of discs in the album -h --help Display this help --location <> Location where audio was recorded. This corresponds to the LOCATION vorbis comment. --mcn <> Specify an MCN, e.g. "0075678356728". This corresponds to the MCN vorbis comment. --skip-rip Use this if you ran makeflac to rip your disc(s), but you still need to encode them. --source <> Specify the source of the audio, e.g. "CD", "MD", "MIC", etc. This corresponds to the SOURCEMEDIA vorbis comment. --tracks <> Specify ranges of tracks to rip or encode (use cdparanoia-style ranges) - NOTE: Make sure your .tracknames reflects the ranges you have specified, e.g. if you have a 10-track disc and use --tracks -9: then you must have ONLY 9 names in .tracknames. Use a ':' (colon) to specify track ranges for each album e.g. --tracks 2-9:-10: Means do tracks 1-9 and 11-14 on disc 1, 1-10 on disc 2, ALL tracks on disc 3. -v --version Show release information EEOOFF exit 1 ;; --location) LOCATION=${2}; shift; shift ;; --mcn ) MCN=${2}; shift; shift ;; --skip-rip ) SKIP_RIP="true"; shift ;; --source ) SOURCE=${2}; shift; shift ;; --tracks ) echo "The --tracks option is currently busted."; exit 1;; # CDPARANOIA_OPTS="${2}"; DO_RANGE="true"; shift; shift ;; -v | --version ) version_info exit 1 ;; * ) break ;; esac done # Error-check some arguments if test -n "${NUM_DISCS}"; then if test ${NUM_DISCS} -lt 1; then echo "${ESYMB} \"${NUM_DISCS}\" is an invalid number of discs" exit 0 fi else NUM_DISCS=1 fi # Make sure we have the required apps OLD_IFS="${IFS}" IFS=' ' APPS_LIST="cdda2wav cdparanoia flac" for AN_APP in ${APPS_LIST}; do whereis ${AN_APP} | cut -d: -f2 | grep "${AN_APP}" 2>&1>/dev/null if test ${?} -eq 1; then echo "${ESYMB} Could not find '${AN_APP}'" MISSING_APP="yes" fi done if test -n "${MISSING_APP}"; then echo "${ESYMB} One or more programs missing ... exiting" exit 1 fi IFS="${OLD_IFS}" ### Start the big loop DISCS_DONE=0 FINAL_TOTAL=0 START_TIME=`date +%s` version_info while test ${DISCS_DONE} -lt ${NUM_DISCS}; do let DISCS_DONE+=1 export DISCNUMBER=${DISCS_DONE} ### If already have all the WAVs, then we can skip all of this if test x"${SKIP_RIP}" != "xtrue"; then # We don't need a subdir if there's only one disc if test ${NUM_DISCS} -gt 1; then mkdir d${DISCS_DONE} && cd d${DISCS_DONE} cp ../${TRACKNAMES}${DISCS_DONE} ${TRACKNAMES} fi ### BEGIN Need the Audio CD Section ### echo -n "*** cdda2wav is retrieving info ... " cdda2wav -D ${DEVICE} -g -J -N -v catalog,trackid,sectors 2>${INFOFILE} printf "done\n\n" fi ### END of Need the Audio CD section ### ### BEGIN ${INFOFILE}-specific code ### if test -f ${INFOFILE}; then # MCN detection if test -z "${MCN}"; then MCN=`grep -m 1 "catalog number" ${INFOFILE} | cut -d: -f2 | sed -e 's/ //g'` if ! test `echo ${MCN} | grep -e [0-9]`; then MCN= fi fi # CDDB detection if test -z "${CDDB_ID}"; then CDDB_ID=`grep -m 1 "CDDB" ${INFOFILE} | cut -d: -f2 | sed -e 's/ //g' -e 's/0x//'` fi # CDINDEX detection if test -z "${CDINDEX_ID}"; then CDINDEX_ID=`grep -m 1 "CDINDEX" ${INFOFILE} | cut -d: -f2 | sed -e 's/ //g'` fi # LEADOUT LEADOUT=`grep -m 1 "Leadout" ${INFOFILE} | cut -d: -f2 | sed -e 's/ //g'` fi ### END ${INFOFILE}-specific code ### if test -f ${TRACKNAMES}; then NUM_TRACKS=`cat ${TRACKNAMES} | wc -l` else echo "${ESYMB} ${TRACKNAMES} containing tracknames does not exist"; exit 1; fi # Making WAVs if we want a range if test -z "${SKIP_RIP}"; then if test -n "${DO_RANGE}"; then THESE_OPTS="-- `echo ${CDPARANOIA_OPTS} | cut -d':' -f${DISCS_DONE}`" echo "--- Ripping Disc ${DISCS_DONE} of ${NUM_DISCS}" cdparanoia -d ${DEVICE} -z -B ${THESE_OPTS} # Moving tracks if necessary TMP_COUNT=1 for A_FILE in `ls *.wav`; do if test ${TMP_COUNT} -lt 10; then mv ${A_FILE} track0${TMP_COUNT}.cdda.wav else mv ${A_FILE} track${TMP_COUNT}.cdda.wav fi let TMP_COUNT+=1 done fi fi # Removing stupid silent track (if any) rm -f track00*; # Detect what kind of lossless files we're compressing if we're skipping the # rip if test -n "${SKIP_RIP}"; then FILE_LIST=`ls *.wav` FILE_EXT="wav" if test -z "${FILE_LIST}"; then FILE_LIST=`ls *.aiff` FILE_EXT='aiff' if test -z "${FILE_LIST}"; then echo "${ESYMB} Could not find any WAVs or AIFFs to encode" exit 1; fi fi else FILE_EXT="wav" fi COUNT=1 RIP_COUNT=1; echo " > Ripping Track ${RIP_COUNT}" cdparanoia -d ${DEVICE} -z -e ${RIP_COUNT} track01.cdda.wav 2>/dev/null; echo "> > Done ripping Track 1" let RIP_COUNT+=1; while test ${COUNT} -le ${NUM_TRACKS}; do if test ${RIP_COUNT} -le ${NUM_TRACKS}; then if test `get_num_pids cdparanoia` -lt 1; then echo " > Ripping Track ${RIP_COUNT}" if test ${RIP_COUNT} -lt 10; then (cdparanoia -d ${DEVICE} -z -e ${RIP_COUNT} track0${RIP_COUNT}.cdda.wav 2>/dev/null && echo "> > Done ripping Track ${RIP_COUNT}" &) else (cdparanoia -d ${DEVICE} -z -e ${RIP_COUNT} track${RIP_COUNT}.cdda.wav 2>/dev/null && echo "> > Done ripping Track ${RIP_COUNT}" &) fi let RIP_COUNT+=1; fi fi if test `get_num_pids flac` -lt ${MAX_ENCODE_PROCS}; then wait_for_track ${COUNT}; while test `get_num_pids flac` -ge ${MAX_ENCODE_PROCS}; do sleep 1 done echo ">>> Encoding Track ${COUNT}" encode_track ${COUNT} & let COUNT+=1; else sleep 1; fi done if test ${NUM_DISCS} -gt 1; then if test ${DISCS_DONE} -lt ${NUM_DISCS}; then cd ../ echo "*** Done with Disk ${DISCS_DONE}, insert the next disk" echo "*** and press any key to continue" read JUNK && unset ${JUNK} fi fi done NEW_COUNT=1 while test ${NEW_COUNT} -le ${NUM_TRACKS}; do if test -f .${NEW_COUNT}.log; then cat .${NEW_COUNT}.log >> ${LOGFILE} rm .${NEW_COUNT}.log let NEW_COUNT+=1 fi done while test `get_num_pids flac` -gt 0; do sleep 3 done # No need to add the DISCNUMBER tag if there's only one disc if test ${SINGLE} -eq 1; then metaflac --remove-tag=DISCNUMBER *.flac fi echo ">>> Adding ReplayGain to all tracks" if test ${NUM_DISCS} -eq 1; then metaflac --add-replay-gain *.flac else cd ../ metaflac --add-replay-gain d*/*.flac fi echo "*** Done adding ReplayGain to all tracks" rm -f .*.log END_TIME=`date +%s`; FINAL_TOTAL=`expr ${END_TIME} - ${START_TIME}`; printf "\n*** Total Elapsed Time: " printf "\t[" echo "`compute_time ${FINAL_TOTAL}`]"