--- cplay.orig 2006-06-24 22:27:28.000000000 +0200 +++ cplay-1.49.patched3 2006-06-26 17:16:27.000000000 +0200 @@ -1,12 +1,15 @@ #!/usr/bin/env python # -*- python -*- -__version__ = "cplay 1.49" +__version__ = "cplay 1.49 mplayer with speed-control and equalizer" """ cplay - A curses front-end for various audio players Copyright (C) 1998-2003 Ulf Betlehem +Patched by Daniel Michalik to use mplayer with +speed-control and equalizer. + 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 @@ -63,7 +66,20 @@ # ------------------------------------------ XTERM = re.search("rxvt|xterm", os.environ["TERM"]) -CONTROL_FIFO = "%s/cplay-control-%s" % (os.environ.get("TMPDIR", "/tmp"), os.environ["USER"]) +CONTROL_FIFO = "%s/cplay-control-%s-%i" % (os.environ.get("TMPDIR", "/tmp"), os.environ["USER"], os.getpid()) +MPLAYER_FIFO = "%s/mplayer-control-%s" % (os.environ.get("TMPDIR", "/tmp"), os.environ["USER"]) + +EQUALIZER_ROCK = "3:3:3:2:0:-1:-1:0:0:1" +EQUALIZER_FLAT = "0:0:0:0:0:0:0:0:0:0" +EQUALIZER_HEAVY = "3:3:3:2:0:0:0:0:0:1" + +EQUALIZERS = [ + (EQUALIZER_ROCK, "rock"), + (EQUALIZER_FLAT, "flat"), + (EQUALIZER_HEAVY, "heavy"), + #"12:12:12:12:0:0:0:0:1:2", "invalid - for testing only") + ] +SPEED_OFF = 0.005 # ------------------------------------------ def which(program): @@ -290,7 +306,7 @@ keymap.bind([1, '^'], app.seek, (0, 0)) # C-a keymap.bind([5, '$'], app.seek, (-1, 0)) # C-e keymap.bind(range(48,58), app.key_volume) # 0123456789 - keymap.bind(['+', '='], app.inc_volume, ()) + keymap.bind(['+'], app.inc_volume, ()) keymap.bind('-', app.dec_volume, ()) keymap.bind('n', app.next_song, ()) keymap.bind('p', app.prev_song, ()) @@ -300,6 +316,11 @@ keymap.bind('Q', app.quit, ()) keymap.bind('q', self.command_quit, ()) keymap.bind('v', app.mixer, ("toggle",)) + keymap.bind('[', app.speed_decr, ()) + keymap.bind(']', app.speed_incr, ()) + keymap.bind('=', app.speed_reset, ()) + keymap.bind('e', app.eq_next, ()) + keymap.bind('E', app.eq_prev, ()) def command_quit(self): app.do_input_hook = self.do_quit @@ -519,27 +540,29 @@ self.name = _("Help") self.keymap.bind('q', self.parent.help, ()) self.buffer = string.split(_("""\ - Global t, T : tag current/regex - ------ u, U : untag current/regex - Up, Down, k, j, C-p, C-n, Sp, i : invert current/all - PgUp, PgDn, K, J, ! : shell ($@ = tagged or current) - Home, End, g, G : movement - Enter : chdir or play Filelist - Tab : filelist/playlist -------- - n, p : next/prev track a : add (tagged) to playlist - z, x : toggle pause/stop s : recursive search - BS, o : goto parent/specified dir - Left, Right, m, ' : set/get bookmark - C-f, C-b : seek forward/backward - C-a, C-e : restart/end track Playlist - C-s, C-r, / : isearch -------- - C-g, Esc : cancel d, D : delete (tagged) tracks/playlist - 1..9, +, - : volume control m, M : move tagged tracks after/before - c, v : counter/volume mode r, R : toggle repeat/Random mode - <, > : horizontal scrolling s, S : shuffle/Sort playlist - C-l, l : refresh, list mode w, @ : write playlist, jump to active - h, q, Q : help, quit?, Quit! X : stop playlist after each track + Global t, T : tag current/regex + ------ u, U : untag current/regex + Up, Down, k, j, C-p, C-n, Sp, i : invert current/all + PgUp, PgDn, K, J, ! : shell ($@ = tagged or current) + Home, End, g, G : movement [, ], = : decr, incr, reset speed + Enter : chdir or play + Tab : filelist/playlist Filelist + n, p : next/prev track -------- + z, x : toggle pause/stop a : add (tagged) to playlist + s : recursive search + Left, Right, BS, o : goto parent/specified dir + C-f, C-b : seek forward/backward m, ' : set/get bookmark + C-a, C-e : restart/end track + C-s, C-r, / : isearch Playlist + C-g, Esc : cancel -------- + 1..9, +, - : volume control d, D : delete (tagged) tracks/playlist + c, v : counter/volume mode m, M : move tagged tracks after/before + <, > : horizontal scrolling r, R : toggle repeat/Random mode + C-l, l : refresh, list mode s, S : shuffle/Sort playlist + h, q, Q : help, quit?, Quit! w, @ : write playlist, jump to active + e, E : next, prev equalizer X : stop playlist after each track """), "\n") +#TODO: Edit to fit 80x25! # ------------------------------------------ class ListEntry: @@ -687,7 +710,9 @@ if pid == 0: try: os.execv(argv[0], argv) except: os._exit(1) - pid, r = os.waitpid(pid, 0) + # Bugfixed: compare bugreport #375060 at Debian BTS + try: pid, r = os.waitpid(pid, os.WNOHANG) + except os.error: pass sys.stderr.write("\nshell returned %s, press return!\n" % r) sys.stdin.readline() app.win_root.update() @@ -1190,12 +1215,14 @@ self.buf = '' self.tid = None - def setup(self, entry, offset): + def setup(self, entry, offset, speed, equalizer): self.argv = string.split(self.commandline) self.argv[0] = which(self.argv[0]) for i in range(len(self.argv)): if self.argv[i] == "%s": self.argv[i] = entry.pathname if self.argv[i] == "%d": self.argv[i] = str(offset*self.fps) + if self.argv[i] == "%x": self.argv[i] = str(speed) + if self.argv[i] == "equalizer=%e": self.argv[i] = "equalizer=" + equalizer self.entry = entry if offset == 0: app.progress(0) @@ -1206,6 +1233,8 @@ return self.argv[0] def play(self): + #for i in range(len(self.argv)): + #sys.stderr.write(self.argv[i]) self.pid = os.fork() if self.pid == 0: os.dup2(self.stdin_w, sys.stdin.fileno()) @@ -1312,6 +1341,37 @@ self.set_position(head, head+tail, [head, tail]) # ------------------------------------------ +class TimeOffsetPlayerMplayer(Player): + re_progress = re.compile("\((?: (?P

\d+):)?(?:(?P\d+):)?(?P\d+).\d\).*\((?: (?P

\d+):)?(?:(?P\d+):)?(?P\d+).\d\)") + + def parse_buf(self): + match = self.re_progress.search(self.buf) + if match: + if match.group('h1'): + h1 = string.atoi(match.group('h1')) + else: h1 = 0 + if match.group('h2'): + h2 = string.atoi(match.group('h2')) + else: h2 = 0 + if match.group('m1'): + m1 = string.atoi(match.group('m1')) + else: m1 = 0 + if match.group('m2'): + m2 = string.atoi(match.group('m2')) + else: m2 = 0 + if match.group('s1'): + s1 = string.atoi(match.group('s1')) + else: s1 = 0 + if match.group('s2'): + s2 = string.atoi(match.group('s2')) + else: s2 = 0 + + #h1, m1, s1, h2, m2, s2 = map(string.atoi, match.groups()) + head = h1*3600+m1*60+s1 + tail = h2*3600+m2*60+s2-head + self.set_position(head, head+tail, [head, tail]) + +# ------------------------------------------ class NoOffsetPlayer(Player): def parse_buf(self): @@ -1356,13 +1416,18 @@ "voldown" : app.dec_volume, "quit" : app.quit} self.fd = None + # Bugfixed: Handle errors creating fifo try: if os.path.exists(CONTROL_FIFO): - os.unlink(CONTROL_FIFO) - os.mkfifo(CONTROL_FIFO, 0600) - self.fd = open(CONTROL_FIFO, "rb+", 0) - except IOError: - # warn that we're disabling the fifo because someone raced us? + self.fifocreated = 0 + app.status(_("Fifo %s exists, disabling control-fifo") % CONTROL_FIFO, 3) + else: + app.status(_("Control-Fifo: %s") % CONTROL_FIFO, 3) + self.fifocreated = 1 + os.mkfifo(CONTROL_FIFO, 0600) + self.fd = open(CONTROL_FIFO, "rb+", 0) + except OSError: + sys.stderr.write("Error: mkfifo " + CONTROL_FIFO) return def handle_command(self): @@ -1375,6 +1440,25 @@ def backward(self): app.seek(-1, 1) + +# ------------------------------------------ +class MPLAYERControl: + def __init__(self): + global MPLAYER_FIFO + self.fd = None + # try: + import random + while os.path.exists(MPLAYER_FIFO): + MPLAYER_FIFO = "%s/mplayer-control-%s-%i" % (os.environ.get("TMPDIR", "/tmp"), \ + os.environ["USER"], random.randint(0, sys.maxint)) + os.mkfifo(MPLAYER_FIFO, 0600) + self.fd = open(MPLAYER_FIFO, "wb+", 0) + # except IOError: + # warn that we're disabling the fifo because someone raced us? + # return + + def send(self, arg): + self.fd.write(arg + "\n") # ------------------------------------------ class Application: @@ -1395,6 +1479,7 @@ self.input_keymap.bind(['\a', 27], self.cancel_input, ()) self.input_keymap.bind(['\n', curses.KEY_ENTER], self.stop_input, ()) + self.speed = 1.0 def setup(self): if tty: @@ -1427,9 +1512,14 @@ self.player = PLAYERS[0] self.timeout = Timeout() self.play_tid = None + self.speed_tid = None self.kludge = 0 self.win_filelist.listdir() self.control = FIFOControl() + self.mplayercontrol = MPLAYERControl() + self.speed = 1.0 + self.eq_cur = 0 + self.equalizer = EQUALIZERS[self.eq_cur][0] def cleanup(self): try: curses.endwin() @@ -1439,8 +1529,16 @@ print # remove temporary files try: - if os.path.exists(CONTROL_FIFO): os.unlink(CONTROL_FIFO) - except IOError: + if self.control.fifocreated: + if os.path.exists(CONTROL_FIFO): os.unlink(CONTROL_FIFO) + except OSError: + sys.stderr.write("Could not remove fifo " + CONTROL_FIFO) + pass + + try: + if os.path.exists(MPLAYER_FIFO): os.unlink(MPLAYER_FIFO) + except OSError: + sys.stderr.write("Could not remove fifo " + MPLAYER_FIFO) pass def run(self): @@ -1474,23 +1572,24 @@ if self.control.fd in r: self.control.handle_command() - def play(self, entry, offset = 0): + def play(self, entry, offset = 0, speed = 1.0): self.kludge = 0 self.play_tid = None if entry is None or offset is None: return + self.speed = speed self.player.stop(quiet=1) for self.player in PLAYERS: if self.player.re_files.search(entry.pathname): - if self.player.setup(entry, offset): break + if self.player.setup(entry, offset, self.speed, self.equalizer): break else: app.status(_("Player not found!"), 1) self.player.stopped = 0 # keep going return self.player.play() - def delayed_play(self, entry, offset): + def delayed_play(self, entry, offset, speed = 1.0): if self.play_tid: self.timeout.remove(self.play_tid) - self.play_tid = self.timeout.add(0.5, self.play, (entry, offset)) + self.play_tid = self.timeout.add(0.5, self.play, (entry, offset, speed)) def next_song(self): self.delayed_play(self.win_playlist.change_active_entry(1), 0) @@ -1501,7 +1600,7 @@ def seek(self, offset, relative): if not self.player.entry: return self.player.seek(offset, relative) - self.delayed_play(self.player.entry, self.player.offset) + self.delayed_play(self.player.entry, self.player.offset, self.speed) def toggle_pause(self): if not self.player.entry: return @@ -1510,7 +1609,7 @@ def toggle_stop(self): if not self.player.entry: return if not self.player.stopped: self.player.stop() - else: self.play(self.player.entry, self.player.offset) + else: self.play(self.player.entry, self.player.offset, self.speed) def inc_volume(self): self.mixer("cue", 1) @@ -1547,6 +1646,38 @@ app.status(_("%s volume %s%%") % (name, get(channel)[0]), 1) mixer.close() + def speed_chg(self, set, offset): + if set == 0: + self.speed += offset + self.mplayercontrol.send("speed_incr " + str(offset)) + if set != 0: + self.speed = set + self.mplayercontrol.send("speed_set " + str(set)) + app.status(_("Speed: %s%%") % (self.speed * 100), 1) + time.sleep(0.05) + + def speed_incr(self): + self.speed_chg(0, SPEED_OFF) + + def speed_decr(self): + self.speed_chg(0, -SPEED_OFF) + + def speed_reset(self): + self.speed_chg(1, 0) + + def eq_chg(self, offset): + self.eq_cur = (self.eq_cur + offset) % (len(EQUALIZERS)) + self.equalizer = EQUALIZERS[self.eq_cur][0] + app.status(_("Equalizer: %s(%s)") % (EQUALIZERS[self.eq_cur][1], self.equalizer), 1) + self.toggle_stop() + self.toggle_stop() + + def eq_next(self): + self.eq_chg(1) + + def eq_prev(self): + self.eq_chg(-1) + def show_input(self): n = len(self.input_prompt)+1 s = cut(self.input_string, self.win_status.cols-n, left=1) @@ -1650,15 +1781,16 @@ # ------------------------------------------ PLAYERS = [ - FrameOffsetPlayer("ogg123 -q -v -k %d %s", "\.(ogg|flac)$"), - FrameOffsetPlayer("splay -f -k %d %s", "(^http://|\.mp[123]$)", 38.28), - FrameOffsetPlayer("mpg123 -q -v -k %d %s", "(^http://|\.mp[123]$)", 38.28), - FrameOffsetPlayer("mpg321 -q -v -k %d %s", "(^http://|\.mp[123]$)", 38.28), - TimeOffsetPlayer("madplay -v --display-time=remaining -s %d %s", "\.mp[123]$"), - NoOffsetPlayer("mikmod -q -p0 %s", "\.(mod|xm|fm|s3m|med|col|669|it|mtm)$"), - NoOffsetPlayer("xmp -q %s", "\.(mod|xm|fm|s3m|med|col|669|it|mtm|stm)$"), - NoOffsetPlayer("play %s", "\.(aiff|au|cdr|mp3|ogg|wav)$"), - NoOffsetPlayer("speexdec %s", "\.spx$") + TimeOffsetPlayerMplayer("mplayer -af equalizer=%e -speed %x -ss %d -input file=" + MPLAYER_FIFO + " %s", "\.(m4a|ogg|mp3|wav|wma)$") + #FrameOffsetPlayer("ogg123 -q -v -k %d %s", "\.(ogg|flac)$"), + #FrameOffsetPlayer("splay -f -k %d %s", "(^http://|\.mp[123]$)", 38.28), + #FrameOffsetPlayer("mpg123 -q -v -k %d %s", "(^http://|\.mp[123]$)", 38.28), + #FrameOffsetPlayer("mpg321 -q -v -k %d %s", "(^http://|\.mp[123]$)", 38.28), + #TimeOffsetPlayer("madplay -v --display-time=remaining -s %d %s", "\.mp[123]$"), + #NoOffsetPlayer("mikmod -q -p0 %s", "\.(mod|xm|fm|s3m|med|col|669|it|mtm)$"), + #NoOffsetPlayer("xmp -q %s", "\.(mod|xm|fm|s3m|med|col|669|it|mtm|stm)$"), + #NoOffsetPlayer("play %s", "\.(aiff|au|cdr|mp3|ogg|wav)$"), + #NoOffsetPlayer("speexdec %s", "\.spx$") ] def VALID_SONG(name):