From jeremy@cnri.reston.va.us Thu Jan 28 14:59:02 1999 Date: Tue, 19 Jan 1999 14:21:14 -0500 From: Jeremy Hylton To: MPG123@TU-CLAUSTHAL.DE Subject: Re: Frontend documentation? The tk3play frontend is basically ASCII commands over stdin/stdout already, isn't it? The commands are just encoded using several integer constants, e.g. "3" is quit and "10 NN" is jump to frame NN. I wrote a small Python script that lets me drive mpg123m from a Python script or interactive session using the tk3play interface. The interface is straightforward: All commands are sent as a string with two numbers; they get parsed by calling scanf("%d %d"...). The first number is a command and the second number is an argument to that command; not all commands use the argument slot but you always need to send it. The player regularly prints output in the same format. Look at the routine tk3play_handlemsg to see the switch used to decode the message. It's pretty close to documentation. You might also find the Python interface I wrote helpful; it's attached. Jeremy [ Part 2: "mp3.py" ] """(crude) Python interface to mpg123 via the tk3play control interface Message format is "%d %d" where the first number is one of the MSG_ codes and the second command depdendent. """ import popen2 import select import string import sys import threading import time # from enum on control_tk3play.h: MSG_CTRL = 0 MSG_BUFFER = 1 MSG_SONG = 2 MSG_QUIT = 3 MSG_NEXT = 4 MSG_RESPONSE = 5 MSG_FRAMES = 6 MSG_INFO = 7 MSG_TIME = 8 MSG_SONGDONE = 9 MSG_JUMPTO = 10 MSG_HALF = 11 MSG_DOUBLE = 12 MSG_SCALE = 13 # data elements for MSG_CTRL PLAY_STOP = 0 PLAY_PAUSE = 1 FORWARD_BEGIN = 2 FORWARD_STEP = 3 FORWARD_END = 4 REWIND_BEGIN = 5 REWIND_STEP = 6 REWIND_END = 7 # Decoder modes MODE_STOPPED = 0 MODE_PLAYING_AND_DECODING = 1 MODE_PLAYING_OLD_DECODING_NEW = 2 MODE_PLAYING_NOT_DECODING = 3 MODE_PLAYING_OLD_FINISHED_DECODING_NEW = 4 MODE_PAUSED = 5 class Status(threading.Thread): def __init__(self, player): threading.Thread.__init__(self) self.setDaemon(1) self.player = player def run(self): while 1: try: print self.player.time, self.player.frame except AttributeError: pass time.sleep(1) class MP3Player(threading.Thread): def __init__(self): threading.Thread.__init__(self) # self.from_player, self.to_player = popen2.popen2("mpg123m -b 1024", 0) self.from_player, self.to_player = popen2.popen2("mpg123m", 0) self.resp_fileno_l = [self.from_player.fileno()] self.running = 1 self.paused = 0 self.frame = None self.dispatch = {} def __del__(self): if self.running: self.quit() def _send(self, type, data=0, extra=''): self.to_player.write("%d %d\n%s" % (type, data, extra)) def quit(self): self._send(MSG_QUIT) buf = self.from_player.read() # XXX should check that buffer returns "5 3.0000" self.to_player.close() self.from_player.close() self.running = 0 def song(self, path): self._send(MSG_SONG, extra="%s\n" % path) # MSG_CTRL family def stop(self): self._send(MSG_CTRL, PLAY_STOP) def pause(self): self._send(MSG_CTRL, PLAY_PAUSE) if self.paused: self.paused = 0 else: self.paused = 1 def jumpto(self, frameno): self._send(MSG_JUMPTO, frameno) # XXX not clear that half or double do anything def half(self): self._send(MSG_HALF) def double(self): self._send(MSG_DOUBLE) def scale(self, scale): self._send(MSG_SCALE, scale) def run(self): while 1: try: r, w, x = select.select(self.resp_fileno_l, [], [], 1.0) except select.error: print "player controller interrupted" self.quit() sys.exit(0) if r: self.handle_input(self.from_player.readline()) def handle_input(self, str): if not str: return elts = string.split(str) type = string.atoi(elts[0]) try: self.dispatch[type](elts[1:]) except KeyError: if type == MSG_INFO: meth = self.dispatch[type] = self.handle_info elif type == MSG_BUFFER: meth = self.dispatch[type] = self.handle_buffer elif type == MSG_FRAMES: meth = self.dispatch[type] = self.handle_frames elif type == MSG_TIME: meth = self.dispatch[type] = self.handle_time else: print "unhandled type", type return meth(elts[1:]) def handle_info(self, elts): self.bitrate, self.freq, self.stereo, self.type, self.sample \ = map(string.atoi, elts) def handle_buffer(self, elts): self.mem_used = int(string.atof(elts[0])) def handle_frames(self, elts): self.frame = int(string.atof(elts[0])) def handle_time(self, elts): self.time = string.atof(elts[0]) if __name__ == "__main__": import sys p = MP3Player() p.start() p.song(sys.argv[1]) s = Status(p) s.start()