--- cplay.orig 2006-06-24 22:27:28.000000000 +0200 +++ cplay-1.49.patched2 2006-06-25 14:54:27.000000000 +0200 @@ -1,12 +1,15 @@ #!/usr/bin/env python # -*- python -*- -__version__ = "cplay 1.49" +__version__ = "cplay 1.49 speed-control" """ 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. + 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 @@ -64,6 +67,9 @@ # ------------------------------------------ XTERM = re.search("rxvt|xterm", os.environ["TERM"]) CONTROL_FIFO = "%s/cplay-control-%s" % (os.environ.get("TMPDIR", "/tmp"), os.environ["USER"]) +MPLAYER_FIFO = "%s/mplayer-control-%s" % (os.environ.get("TMPDIR", "/tmp"), os.environ["USER"]) +EQUALIZER = "2:2:2:1:-1:-3:-3:-1:-1:-1" +SPEED_OFF = 0.005 # ------------------------------------------ def which(program): @@ -290,7 +296,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 +306,9 @@ 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, ()) def command_quit(self): app.do_input_hook = self.do_quit @@ -539,6 +548,7 @@ <, > : 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 + [, ], = : decr, incr, reset speed """), "\n") # ------------------------------------------ @@ -1190,12 +1200,13 @@ self.buf = '' self.tid = None - def setup(self, entry, offset): + def setup(self, entry, offset, speed): 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) self.entry = entry if offset == 0: app.progress(0) @@ -1312,6 +1323,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): @@ -1375,6 +1417,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.unlink(MPLAYER_FIFO) + 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 +1456,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 +1489,12 @@ 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 def cleanup(self): try: curses.endwin() @@ -1443,6 +1508,11 @@ except IOError: pass + try: + if os.path.exists(MPLAYER_FIFO): os.unlink(MPLAYER_FIFO) + except IOError: + pass + def run(self): while 1: now = time.time() @@ -1474,23 +1544,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): 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 +1572,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 +1581,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) @@ -1525,6 +1596,26 @@ try: self._mixer(cmd, arg) except Exception, e: app.status(e, 2) + def speedchg(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.speedchg(0, SPEED_OFF) + + def speed_decr(self): + self.speedchg(0, -SPEED_OFF) + + def speed_reset(self): + self.speedchg(1, 0) + + def _mixer(self, cmd, arg): try: import ossaudiodev @@ -1650,6 +1741,7 @@ # ------------------------------------------ PLAYERS = [ + TimeOffsetPlayerMplayer("mplayer -af equalizer=" + EQUALIZER + " -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),