summaryrefslogtreecommitdiff
path: root/jack_workers.py
diff options
context:
space:
mode:
Diffstat (limited to 'jack_workers.py')
-rwxr-xr-xjack_workers.py372
1 files changed, 372 insertions, 0 deletions
diff --git a/jack_workers.py b/jack_workers.py
new file mode 100755
index 0000000..b341745
--- /dev/null
+++ b/jack_workers.py
@@ -0,0 +1,372 @@
+### jack_workers: worker functions for
+### jack - extract audio from a CD and encode it using 3rd party software
+### Copyright (C) 1999-2004 Arne Zellentin <zarne@users.sf.net>
+
+### This program is free software; you can redistribute it and/or modify
+### it under the terms of the GNU General Public License as published by
+### the Free Software Foundation; either version 2 of the License, or
+### (at your option) any later version.
+
+### This program is distributed in the hope that it will be useful,
+### but WITHOUT ANY WARRANTY; without even the implied warranty of
+### MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+### GNU General Public License for more details.
+
+### You should have received a copy of the GNU General Public License
+### along with this program; if not, write to the Free Software
+### Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+import sndhdr
+import signal
+import string
+import posix
+import array
+import fcntl
+import wave
+import time
+import pty
+import sys
+import os
+
+import jack_functions
+import jack_ripstuff
+import jack_helpers
+import jack_targets
+import jack_utils
+import jack_tag
+
+from jack_globals import *
+from jack_helpers import helpers
+from jack_init import F_SETFL, O_NONBLOCK
+
+def default_signals():
+ signal.signal(signal.SIGTERM, signal.SIG_DFL)
+ signal.signal(signal.SIGINT, signal.SIG_DFL)
+ signal.signal(signal.SIGQUIT, signal.SIG_DFL)
+ signal.signal(signal.SIGHUP, signal.SIG_DFL)
+ signal.signal(signal.SIGWINCH, signal.SIG_DFL)
+
+def start_new_process(args, nice_value = 0):
+ "start a new process in a pty and renice it"
+ data = {}
+ data['start_time'] = time.time()
+ pid, master_fd = pty.fork()
+ if pid == CHILD:
+ default_signals()
+ if nice_value:
+ os.nice(nice_value)
+ os.execvp(args[0], [a.encode(cf['_charset'], "replace") for a in args])
+ else:
+ data['pid'] = pid
+ if os.uname()[0] == "Linux":
+ fcntl.fcntl(master_fd, F_SETFL, O_NONBLOCK)
+ data['fd'] = master_fd
+ data['file'] = os.fdopen(master_fd)
+ data['cmd'] = args
+ data['buf'] = ""
+ data['otf'] = 0
+ data['percent'] = 0
+ data['elapsed'] = 0
+ return data
+
+def start_new_ripper(track, ripper):
+ "start a new DAE process"
+ helper = helpers[cf['_ripper']]
+ cmd = string.split(helper['cmd'])
+ args = []
+ for i in cmd:
+ if i == "%n": args.append(`track[NUM]`)
+ elif i == "%o": args.append(track[NAME].decode(cf['_charset'], "replace") + ".wav")
+ elif i == "%d": args.append(cf['_cd_device'])
+ elif i == "%D": args.append(cf['_gen_device'])
+ else: args.append(i)
+ data = start_new_process(args)
+ data['type'] = "ripper"
+ data['prog'] = cf['_ripper']
+ data['track'] = track
+ return data
+
+def start_new_encoder(track, encoder):
+ "start a new encoder process"
+ helper = helpers[cf['_encoder']]
+ if cf['_vbr']:
+ cmd = string.split(helper['vbr-cmd'])
+ else:
+ cmd = string.split(helper['cmd'])
+
+ args = []
+ for i in cmd:
+ if i == "%r":
+ args.append(`track[RATE] * helper['bitrate_factor']`)
+ elif i == "%q":
+ if helper.has_key('inverse-quality') and helper['inverse-quality']:
+ quality = min(9, 10 - cf['_vbr_quality'])
+ else:
+ quality = cf['_vbr_quality']
+ args.append("%.3f" % quality)
+ elif i == "%i":
+ args.append(track[NAME].decode(cf['_charset'], "replace") + ".wav")
+ elif i == "%o":
+ args.append(track[NAME].decode(cf['_charset'], "replace") + jack_targets.targets[jack_helpers.helpers[cf['_encoder']]['target']]['file_extension'])
+ else:
+ if jack_targets.targets[helper['target']]['can_pretag']:
+ if i == "%t":
+ if jack_tag.track_names:
+ args.append(jack_tag.track_names[track[NUM]][1])
+ else:
+ args.append("")
+ elif i == "%a":
+ if jack_tag.track_names:
+ if jack_tag.track_names[track[NUM]][0]:
+ args.append(jack_tag.track_names[track[NUM]][0])
+ else:
+ args.append(jack_tag.track_names[0][0])
+ else:
+ args.append("")
+ elif i == "%n":
+ args.append(`track[NUM]`)
+ elif i == "%l":
+ if jack_tag.track_names:
+ args.append(jack_tag.track_names[0][1])
+ else:
+ args.append("")
+ elif i == "%G":
+ if cf['_id3_genre'] >= 0: args.append(cf['_id3_genre'])
+ else: args.append('255')
+ elif i == "%g":
+ if cf['_id3_genre'] >= 0: args.append(jack_tag.genretxt)
+ else: args.append('Unknown')
+ elif i == "%y":
+ if cf['_id3_year'] > 0:
+ args.append(`cf['_id3_year']`)
+ else:
+ args.append('0')
+ else:
+ args.append(i)
+ else:
+ args.append(i)
+ data = start_new_process(args, cf['_nice_value'])
+ data['type'] = "encoder"
+ data['prog'] = cf['_encoder']
+ data['track'] = track
+ return data
+
+def start_new_otf(track, ripper, encoder):
+ "start a new ripper/encoder pair for on-the-fly encoding"
+ data = {}
+ data['rip'] = {}
+ data['enc'] = {}
+ data['rip']['otf'] = 1
+ data['enc']['otf'] = 1
+ enc_in, rip_out = os.pipe()
+ data['rip']['fd'], rip_err = os.pipe()
+ data['enc']['fd'], enc_err = os.pipe()
+ args = []
+ for i in string.split(helpers[ripper]['otf-cmd']):
+ if i == "%n": args.append(`track[NUM]`)
+ elif i == "%d": args.append(cf['_cd_device'])
+ elif i == "%D": args.append(cf['_gen_device'])
+ else: args.append(i)
+ data['rip']['start_time'] = time.time()
+ pid = os.fork()
+ if pid == CHILD:
+ default_signals()
+ os.dup2(rip_out, STDOUT_FILENO)
+ os.dup2(rip_err, STDERR_FILENO)
+ os.close(rip_out)
+ os.close(rip_err)
+ os.execvp(args[0], args)
+ # child won't see anything below...
+ os.close(rip_out)
+ os.close(rip_err)
+ data['rip']['pid'] = pid
+ data['rip']['cmd'] = helpers[cf['_ripper']]['otf-cmd']
+ data['rip']['buf'] = ""
+ data['rip']['percent'] = 0
+ data['rip']['elapsed'] = 0
+ data['rip']['type'] = "ripper"
+ data['rip']['prog'] = cf['_ripper']
+ data['rip']['track'] = track
+ if cf['_vbr']:
+ cmd = string.split(helpers[cf['_encoder']]['vbr-otf-cmd'])
+ else:
+ cmd = string.split(helpers[cf['_encoder']]['otf-cmd'])
+ args = []
+ for i in cmd:
+ if i == "%r":
+ args.append(`track[RATE] * helpers[cf['_encoder']]['bitrate_factor']`)
+ elif i == "%q":
+ if helper.has_key('inverse-quality') and helper['inverse-quality']:
+ quality = min(9, 10 - cf['_vbr_quality'])
+ else:
+ quality = cf['_vbr_quality']
+ args.append("%.3f" % quality)
+ elif i == "%o":
+ args.append(track[NAME].decode(cf['_charset'], "replace") + jack_targets.targets[jack_helpers.helpers[cf['_encoder']]['target']]['file_extension'])
+ elif i == "%d":
+ args.append(cf['_cd_device'])
+ elif i == "%D":
+ args.append(cf['_gen_device'])
+ else:
+ args.append(i)
+ data['enc']['start_time'] = time.time()
+ pid = os.fork()
+ if pid == CHILD:
+ default_signals()
+ if cf['_nice_value']:
+ os.nice(cf['_nice_value'])
+ os.dup2(enc_in, STDIN_FILENO)
+ os.dup2(enc_err, STDERR_FILENO)
+ os.close(enc_in)
+ os.close(enc_err)
+ os.execvp(args[0], args)
+ # child won't see anything below...
+ os.close(enc_in)
+ os.close(enc_err)
+ data['enc']['pid'] = pid
+ data['enc']['otf-pid'] = data['rip']['pid']
+ data['enc']['cmd'] = cmd
+ data['enc']['buf'] = ""
+ data['enc']['percent'] = 0
+ data['enc']['elapsed'] = 0
+ data['enc']['type'] = "encoder"
+ data['enc']['prog'] = cf['_encoder']
+ data['enc']['track'] = track
+ data['rip']['otf-pid'] = data['enc']['pid']
+
+ if os.uname()[0] == "Linux":
+ fcntl.fcntl(data['rip']['fd'], F_SETFL, O_NONBLOCK)
+ fcntl.fcntl(data['enc']['fd'], F_SETFL, O_NONBLOCK)
+ data['rip']['file'] = os.fdopen(data['rip']['fd'])
+ data['enc']['file'] = os.fdopen(data['enc']['fd'])
+ return data
+
+def ripread(track, offset = 0):
+ "rip one track from an image file."
+ data = {}
+ start_time = time.time()
+ pid, master_fd = pty.fork() # this could also be done with a pipe, anyone?
+ if pid == CHILD:
+ #debug:
+ #so=open("/tmp/stdout", "w")
+ #sys.stdout = so
+ #se=open("/tmp/stderr", "w+")
+ #sys.stderr = se
+ default_signals()
+
+# FIXME: all this offset stuff has to go, track 0 support has to come.
+
+ print ":fAE: waiting for status report..."
+ sys.stdout.flush()
+ hdr = sndhdr.whathdr(cf['_image_file'])
+ my_swap_byteorder = cf['_swap_byteorder']
+ my_offset = offset
+ if hdr:
+
+## I guess most people use cdparanoia 1- (instead of 0- if applicable)
+## for image creation, so for a wav file use:
+
+ image_offset = -offset
+
+ else:
+ if string.upper(cf['_image_file'])[-4:] == ".CDR":
+ hdr = ('cdr', 44100, 2, -1, 16) # Unknown header, assuming cdr
+#
+## assume old cdrdao which started at track 1, not at block 0
+ image_offset = -offset
+
+ elif string.upper(cf['_image_file'])[-4:] == ".BIN":
+ hdr = ('bin', 44100, 2, -1, 16) # Unknown header, assuming bin
+#
+## assume new cdrdao which starts at block 0, byteorder is reversed.
+ my_swap_byteorder = not my_swap_byteorder
+ image_offset = 0
+
+ elif string.upper(cf['_image_file'])[-4:] == ".RAW":
+ hdr = ('bin', 44100, 2, -1, 16) # Unknown header, assuming raw
+ image_offset = 0
+
+ else:
+ debug("unsupported image file " + cf['_image_file'])
+ posix._exit(4)
+
+ expected_filesize = jack_functions.tracksize(jack_ripstuff.all_tracks)[CDR] + CDDA_BLOCKSIZE * offset
+#
+## WAVE header is 44 Bytes for normal PCM files...
+#
+ if hdr[0] == 'wav':
+ expected_filesize = expected_filesize + 44
+
+ if abs(jack_utils.filesize(cf['_image_file']) - expected_filesize) > CDDA_BLOCKSIZE:
+ # we *do* allow a difference of one frame
+ debug("image file size mismatch, aborted. %d != %d" % (jack_utils.filesize(cf['_image_file']), expected_filesize))
+ posix._exit(1)
+
+ elif hdr[0] == 'wav' and (hdr[1], hdr[2], hdr[4]) != (44100, 2, 16):
+ debug("unsupported WAV, need CDDA_fmt, aborted.")
+ posix._exit(2)
+
+ elif hdr[0] not in ('wav', 'cdr', 'bin'):
+ debug("unsupported: " + hdr[0] + ", aborted.")
+ posix._exit(3)
+
+ else:
+ f = open(cf['_image_file'], 'r')
+#
+## set up output wav file:
+#
+ wav = wave.open(track[NAME].decode(cf['_charset'], "replace") + ".wav", 'w')
+ wav.setnchannels(2)
+ wav.setsampwidth(2)
+ wav.setframerate(44100)
+ wav.setnframes(0)
+ wav.setcomptype('NONE', 'not compressed')
+#
+## calculate (and seek to) position in image file
+#
+ track_start = (track[START] + image_offset) * CDDA_BLOCKSIZE
+ if hdr[0] == 'wav':
+ track_start = track_start + 44
+ f.seek(track_start)
+#
+## copy / convert the stuff
+#
+ for i in range(0, track[LEN]):
+ buf = array.array("h")
+ buf.fromfile(f, 1176) # CDDA_BLOCKSIZE / 2
+ if not my_swap_byteorder: # this is inverted as WAVE swabs them anyway.
+ buf.byteswap()
+ wav.writeframesraw(buf.tostring())
+ if i % 1000 == 0:
+ print ":fAE: Block " + `i` + "/" + `track[LEN]` + (" (%2i%%)" % (i * 100 / track[LEN]))
+ sys.stdout.flush()
+ wav.close()
+ f.close()
+
+ stop_time = time.time()
+ read_speed = track[LEN] / CDDA_BLOCKS_PER_SECOND / ( stop_time - start_time )
+ if read_speed < 100:
+ print "[%2.0fx]" % read_speed,
+ else:
+ print "[99x]",
+ if hdr[0] in ('bin', 'wav'):
+ print "[ - read from image - ]"
+ else:
+ print "[cdr-WARNING, check byteorder !]"
+ sys.stdout.flush()
+ posix._exit(0)
+ else: # we are not the child
+ data['start_time'] = start_time
+ data['pid'] = pid
+ data['fd'] = master_fd
+ data['file'] = os.fdopen(master_fd)
+ data['cmd'] = ""
+ data['buf'] = ""
+ data['type'] = "image_reader"
+ data['prog'] = "builtin"
+ data['track'] = track
+ data['percent'] = 0
+ data['otf'] = 0
+ data['elapsed'] = 0
+ return data
+