Mat's Playground

cd.BurnAudioCD

Description

This script creates gapless (no gaps between the tracks, useful for live recordings) cds. It will burn all audio (mp3, ogg, wav) files from the current directory. On runtime, you have to confirm the selected files and specify burning speed.

Then the script will automatically convert the files to wav, create the cd's TOC file and burn the cd. When it is down, it will delete all temporary files.

Note: Although this script will display the total running time of the files to be burned, it will not check whether the files will fit onto the CD.

Required programs

Download

Source code

Hide line numbers Expand lines
  1#!/bin/sh
  2# the next line restarts using tclsh \
  3exec tclsh "$0" "$@"
  4
  5
  6#
  7# needed programs:
  8#  cdrdao
  9#  lame
 10#  ogg123
 11#  ogginfo
 12#  mp3info
 13#  shntool
 14#
 15
 16# settings
 17set config(device)    "/dev/cdrom1"
 18set config(driver)    "generic-mmc"
 19set config(max_speed) "48"
 20
 21
 22#
 23# temporarely, only allow root to use this script
 24#
 25if {[exec /usr/bin/whoami] != "root"} then {
 26  puts "this script can only be run by root"
 27  exit
 28}
 29
 30
 31#
 32# which files to burn?
 33#
 34set listOfFiles [lsort [glob -nocomplain -types f *.{ogg,mp3,wav}]]
 35
 36#
 37# nothing to do
 38#
 39if {[llength $listOfFiles] == 0} then {
 40    puts "No files to burn.."
 41    unset listOfFiles
 42    exit
 43}
 44
 45
 46#
 47# determine running time of the given file
 48#
 49proc fileLength {file} {
 50    switch [file extension $file] {
 51        ".mp3" {
 52            set length [exec mp3info -p "%S" $file]
 53        }
 54
 55        ".ogg" {
 56            set text [exec ogginfo $file]
 57
 58            set secs 0
 59            foreach line [split $text "\n"] {
 60                set line [string trim $line]
 61
 62                if {[string range $line 0 15] == "Playback length:"} then {
 63                    set line [string trim [join [lrange [split $line ":"] 1 end] ":"]]
 64
 65                    # line has now the form "4m:17s", now convert to seconds
 66                    foreach i [split $line ":"] {
 67                        set val [string range $i 0 end-1]
 68                        set mod [string index $i end]
 69
 70                        if {[string index $val 0] == "0" && [string length $val] > 1} then {set val [string range $val 1 end]}
 71
 72                        if {$mod == "s"} then {set secs [expr $secs + $val]}
 73                        if {$mod == "m"} then {set secs [expr $secs + ($val * 60)]}
 74                        if {$mod == "h"} then {set secs [expr $secs + ($val * 60 * 60)]}
 75
 76                        unset val mod i
 77                    }
 78                    break
 79                }
 80            }
 81            set length $secs
 82            unset line text secs
 83        }
 84
 85        ".wav" {
 86            set lines [split [exec shntool len $file] \n]
 87            # first line is the table heading
 88            # second the first file
 89            # total length of all files
 90            # we did only specify one file, so we take the 2nd line
 91
 92            set length [lindex [split [string trim [lindex $lines 1]] " "] 0]
 93
 94            # shninfo returns the length nicely formatted, we need seconds here
 95            set length [split $length ": ."]
 96
 97            set min  [lindex $length 0]
 98            set sec  [lindex $length 1]
 99            set mili [lindex $length 2]
100
101            if {[string index $sec 0] == "0"} then {set sec [string index $sec 1]}
102            if {$mili != "00"} then {incr sec}
103
104            set length [expr $sec + ($min * 60)]
105
106            unset lines min sec mili
107        }
108    }
109
110    return $length
111}
112
113#
114# converts seconds into 'nice' time values
115#
116proc transform_secs {in} {
117
118    set m [expr $in / 60]
119    set s [expr $in % 60]
120
121    if {$m < 10} then {set m "0$m"}
122    if {$s < 10} then {set s "0$s"}
123
124    # hrs, if necessary
125    if {$m > 60} then {
126        set h [expr $m / 60]
127        set m [expr $m % 60]
128        if {$h < 10} then {set h "0$h"}
129        if {$m < 10} then {set m "0$m"}
130        set m "$h:$m"
131    }
132
133    return "$m:$s"
134}
135
136#
137# waits for exactly one keypress and returns this key
138#
139proc getKey {} {
140    # eof is MIN - see termio(7)
141    exec stty -icanon eof \001
142    set R [read stdin 1]
143    scan $R "%c" V
144    # Reset the tty, assuming eof was ^D
145    exec stty icanon eof \004
146
147    return $R
148}
149
150
151#
152# display all files to be burned
153#
154
155puts "\tTrack\tFile"
156puts "\t-----\t----"
157
158set nr 1
159set length 0
160
161foreach file $listOfFiles {
162    if {$nr < 10} then {set nrTmp "0$nr"} else {set nrTmp $nr}
163
164    puts "\t $nrTmp:\t$file"
165
166    set length [expr $length + [fileLength $file]]
167    incr nr
168    unset file nrTmp
169}
170unset nr
171
172puts "\n\t Total: [transform_secs $length]"
173
174puts -nonewline "\nBurn these tracks? (y/N): "
175if {[getKey] != "y"} then {
176    unset listOfFiles
177    puts ""
178    exit
179}
180
181
182#
183# which drive speed?
184#
185puts -nonewline "\nBurn at max speed? (y/N): "
186if {[getKey] == "y"} then {
187    puts ""
188    set burnSpeed $config(max_speed)
189} else {
190    puts -nonewline "\nWhich speed?: "
191    set burnSpeed [string trim [read stdin 2]]
192}
193
194if {![string is integer $burnSpeed] || $burnSpeed > $config(max_speed) || [expr $burnSpeed % 2] != 0} then {
195    puts "Unsupported speed!"
196    unset burnSpeed listOfFiles
197    exit
198}
199
200
201#
202# final confirmation
203#
204puts -nonewline "\nStart to burn? (y/N): "
205set ans ""
206while {$ans != "y" && $ans != "n"} {
207    set ans [getKey]
208}
209
210if {$ans != "y"} then {
211    unset ans burnSpeed listOfFiles
212    puts ""
213    exit
214}
215unset ans
216
217
218#
219# let the burning begin...
220#
221
222# converting all non-wav files
223puts "\n\nPreparing files .."
224
225set tmpFiles {cd.toc}
226for {set i 0} {$i < [llength $listOfFiles]} {incr i} {
227    set file [lindex $listOfFiles $i]
228    set ext  [file extension $file]
229
230    if {$ext == ".wav"} then {
231        # no conversion necessary
232        unset file ext
233        continue
234    }
235
236    #
237    # convert files
238    #
239    puts "  Converting $file .."
240
241    if {[file exists $file.wav]} then {
242        puts "  ERROR: $file.wav already exists"
243        unset listOfFiles tmpFiles burnSpeed i file ext
244        exit
245    }
246
247    if {$ext == ".ogg"} then {
248        exec ogg123 --quiet -d wav $file -f $file.wav
249    }
250
251    if {$ext == ".mp3"} then {
252        catch {exec lame --quiet --decode $file $file.wav}
253    }
254
255    set listOfFiles [lreplace $listOfFiles $i $i "$file.wav"]
256    lappend tmpFiles $file.wav
257
258    unset file ext
259}
260unset i
261
262# creating TOC file
263puts "Creating toc file .."
264
265set fd [open cd.toc w+]
266puts $fd "CD_DA"
267puts $fd ""
268
269foreach i $listOfFiles {
270    puts $fd "TRACK AUDIO"
271    puts $fd "AUDIOFILE \"$i\" 0"
272    puts $fd ""
273    unset i
274}
275
276close $fd
277unset fd
278
279
280#
281# burning!!
282#
283puts "Burning at [set burnSpeed]X ..\n"
284
285catch {exec cdrdao write    --device $config(device) --driver $config(driver) --speed $burnSpeed -n -v 1 cd.toc >@stdout 2>@stderr}
286#catch {exec cdrdao simulate --device $config(device) --driver $config(driver) --speed $burnSpeed -n -v 1 cd.toc >@stdout 2>@stderr}
287
288
289
290
291#
292# remove temporary wave files
293#
294puts "\nRemoving temporary files .."
295foreach i $tmpFiles {
296    file delete $i
297    unset i
298}
299
300
301
302
303unset burnSpeed listOfFiles tmpFiles
304exit