Re: Python music sequencer timing problems
John O'Hagan wrote: > On Wed, 10 Dec 2008, badmuthahubbard wrote: >> I've been trying to get the timing right for a music sequencer using >> Tkinter. First I just loaded the Csound API module and ran a Csound >> engine in its own performance thread. The score timing was good, >> being controlled internally by Csound, but any time I moved the mouse >> I got audio dropouts. >> It was suggested I run the audio engine as a separate process, with >> elevated/realtime priority and use sockets to tell it what to play, >> and that way, too, people could set up servers for the audio on >> different CPUs. But I've found that the method I came up with for >> timing the beats/notes is too slow (using threading.Timer on a >> function that calls itself over and over), and the whole thing played >> too slowly (and still gave me noise when moving the mouse). I've been >> using subprocesses, but I'm now wondering if sockets would or could >> make a difference. >> >> The overall goal is this: when the user wants to audition a piece, >> create an audio engine process with elevated/realtime priority. This >> engine also has all the synthesis and sound processing rules for the >> various instruments, due to the way Csound is structured. Set up a >> scheduler- possibly in another process, or just another thread- and >> fill it with all the notes from the score and their times. Also, the >> user should be able to see a time-cursor moving across the piece so >> they can see where they are in the score. As this last bit is GUI, >> the scheduler should be able to send callbacks back to the GUI as well >> as notes to the audio engine. But neither the scheduler nor the audio >> engine should wait for Tkinter's updating of the location of the time- >> cursor. Naturally, all notes will have higher priorities in the >> scheduler than all GUI updates, but they won't necessarily always be >> at the same time. >> >> So, I have a few ideas about how to proceed, but I want to know if >> I'll need to learn more general things first: >> 1. >> Create both the scheduler and the audio engine as separate processes >> and communicate with them through sockets. When all events are >> entered in the scheduler, open a server socket in the main GUI process >> and listen for callbacks to move the cursor (is it possible to do this >> using Tkinter's mainloop, so the mouse can be moved, albeit >> sluggishly, at the same time the cursor is moving continuously?); the >> audio engine runs at as high priority as possible, and the scheduler >> runs somewhere between that and the priority of the main GUI, which >> should even perhaps be temporarily lowered below default for good >> measure. >> >> or >> >> 2. >> Create the audio engine as an elevated priority process, and the >> scheduler as a separate thread in the main process. The scheduler >> sends notes to the audio engine and callbacks within its own process >> to move the GUI cursor. Optionally, every tiny update of the cursor >> could be a separate thread that dies an instant later. >> >> 3. >> Closer to my original idea, but I'm hoping to avoid this. All note >> scheduling and tempo control is done by Csound as audio engine, and a >> Csound channel is set aside for callbacks to update the cursor >> position. Maybe this would be smoothest, as timing is built into >> Csound already, but the Csound score will be full of thousands of >> pseudo-notes that only exist for those callbacks. Down the road I'd >> like to have notes sound whenever they are added or moved on the >> score, not just when playing the piece, as well as the option of >> adjusting the level, pan, etc. of running instruments. >> > > Hi Chuckk, > > I've recently been fooling with something involving timing and synchronising > multiple note-on/note-off events, and also tried threading and subprocesses > without much success - out of the box, the high-tempo precision wasn't there. > > But I had more luck with this approach, if it's not way too simple for your > purpose (I'm not using a gui, for example); in psuedocode: > > from time import time, sleep > > start = time() > for event in music: > duration=len(event) #Really, the length of the event > play(event) > while 1: > timer = time() > remaining = start + duration - timer > if remaining < 0.001: > break > else: > sleep(remaining / 2) > stop(event) > start += duration > > IOW, just check the time, wait half the remaining note duration, check the > time again, etc, till you've reached your desired precision level (in this > case 0.001 sec). The halving of each succesive sleep() means that there is > only a handful of calls to time() per note, 5-10 depending on the tempo. (Of > course it could be any fraction, I just pulled that out of a hat; it would > probably be better too if the fraction decremented as the deadline > approached). > > Even with this naive algorithm, I'm getting accuracy o
Re: Tkinter unbinding
Roger wrote: > I've done a lot of googling for this topic and I fear that it's not > possible. I have a widget that is overloaded with several bindings. > I want to be able to unbind one method form the same Event without > destroying all the other bindings to the same event that's associated > to the same widget. > > For example: > > import Tkinter > > def test(): > print 'test' > > def test2(): > print 'test2' > > root = Tkinter.Tk() > funcid1 = root.bind("<1>", lambda e: test()) > funcid2 = root.bind("<1>", lambda e: test2(), add='+') > root.unbind("<1>", funcid2) > root.mainloop() > > When run neither <1> binding will exist against the root because the > unbind will unbind all the functions associated with that event. > However, in this example, I only want to unbind test2 not test1. > > Any help is greatly appreciated. Thanks! > Roger. I believe you've discovered a bug. Aside from recommending trying wxWidgets, here's the source of the unbind function in Tkinter.py: def unbind(self, sequence, funcid=None): """Unbind for this widget for event SEQUENCE the function identified with FUNCID.""" self.tk.call('bind', self._w, sequence, '') if funcid: self.deletecommand(funcid) --- First, it replaces all bindings for the sequence with the empty string, i.e., it deletes all bindings for that event unconditionally. THEN it calls deletecommand() with the funcid, who knows what that does. My Tcl is not so sharp. I have an idea for a workaround, let me see if it works... -Chuckk -- http://mail.python.org/mailman/listinfo/python-list
Re: Tkinter unbinding
Bad Mutha Hubbard wrote: > Roger wrote: > >> I've done a lot of googling for this topic and I fear that it's not >> possible. I have a widget that is overloaded with several bindings. >> I want to be able to unbind one method form the same Event without >> destroying all the other bindings to the same event that's associated >> to the same widget. >> >> For example: >> >> import Tkinter >> >> def test(): >> print 'test' >> >> def test2(): >> print 'test2' >> >> root = Tkinter.Tk() >> funcid1 = root.bind("<1>", lambda e: test()) >> funcid2 = root.bind("<1>", lambda e: test2(), add='+') >> root.unbind("<1>", funcid2) >> root.mainloop() >> >> When run neither <1> binding will exist against the root because the >> unbind will unbind all the functions associated with that event. >> However, in this example, I only want to unbind test2 not test1. >> >> Any help is greatly appreciated. Thanks! >> Roger. > > I believe you've discovered a bug. Aside from recommending trying > wxWidgets, here's the source of the unbind function in Tkinter.py: > > def unbind(self, sequence, funcid=None): > """Unbind for this widget for event SEQUENCE the > function identified with FUNCID.""" > self.tk.call('bind', self._w, sequence, '') > if funcid: > self.deletecommand(funcid) > > --- > First, it replaces all bindings for the sequence with the empty string, > i.e., it deletes all bindings for that event unconditionally. THEN it > calls deletecommand() with the funcid, who knows what that does. My Tcl > is not so sharp. > I have an idea for a workaround, let me see if it works... > -Chuckk Alas, my workaround doesn't work either. Tkinter.py also states that calling bind with only an event sequence will return all bindings for that sequence; I was thinking I could then remove the function in question from that list and call bind again with each of the functions in the remaning list as argument. I had high hopes. The return value of calling bind with no target function is just about Tcl nonsense: #!/usr/bin/env python import Tkinter def test(event): print 'test' def test2(event): print 'test2' root = Tkinter.Tk() funcid1 = root.bind("<1>", test) funcid2 = root.bind("<1>", test2, add='+') print funcid1, funcid2 bound = root.bind('') print "bound:", bound #root.unbind("<1>", funcid=funcid2) root.mainloop() --- Note that I took out the lambdas and gave event arguments to the functions; if you did that on purpose, because you need to call the same functions without events, then just ignore that... SO, the other workaround, which I've used, is to bind the event to a generic function, and have that generic function conditionally call the functions you want. I'll go back and try to make an example from your example. -Chuckk -- http://mail.python.org/mailman/listinfo/python-list
client-server socket script that crashes on Mac
Hi. I know I have some bugs in this, but I'm trying to find out one thing in particular: why the accept() method in the server never returns in OSX. It announces that it is waiting, and the client announces that it is attempting to connect, and both print the same port number, but it just hangs. On Linux, it connects, sends the data, and terminates. I run the command: python ./client.py There are two files below (I was unable to attach a zip). Any ideas? Thanks. -Chuckk ## client.py #!/usr/bin/env python import os import threading import sys import subprocess import socket #import select import Queue import time class client(object): def __init__(self): self.cbport = 5899 self.outport = 5880 self.playing = 0 self.allowed2play = 1 self.play() time.sleep(1) self.stop() def waitforconnect(self, sock, q): conn = sock.accept() q.put(conn) def delegatecallbacks(self, sock): cbtext = '' while self.playing == 1: #if select.select((sock,),(),())[0]: try: cbtext += sock.recv(32) while cbtext.count('CB'): cb, cbtext = cbtext.split('CB', 1) print self.__class__.__name__, "-", "Callback:", cb if cb == 'END': print self.__class__.__name__, "-", "END received" self.stop() except: print self.__class__.__name__, "-", "Callback Socket Unavailable" def play(self): if self.allowed2play: self.allowed2play = 0 if True: cbwait = socket.socket(socket.AF_INET, socket.SOCK_STREAM) count = 0 while count < 3: try: cbwait.bind(('127.0.0.1', self.cbport)) print self.__class__.__name__, "-", 'Callback Port: %s' % str(self.cbport) count = 3 except: self.cbport += 1 count += 1 if count == 3: print self.__class__.__name__, "-", "NO PORTS AVAILABLE FOR CALLBACK" cbwait.listen(2) self.outsock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) #thread to wait for callback to connect q = Queue.Queue() wait = threading.Thread(target=self.waitforconnect, args=(cbwait, q)) wait.start() count = 0 while count < 1: tmpsoc = socket.socket(socket.AF_INET, socket.SOCK_STREAM) try: tmpsoc.bind(('127.0.0.1', self.outport)) print self.__class__.__name__, "-", 'outport:', self.outport count = 1 del tmpsoc except: self.outport += 1 count += 1 print self.__class__.__name__, "-", "count:", count del tmpsoc self.server = subprocess.Popen((sys.executable, 'server.py', str(self.outport), str(self.cbport))) #success = self.server.poll() #if success: #print self.__class__.__name__, "-", success #break connected = False tries = 1 while not connected: try: self.outsock.connect(('127.0.0.1', self.outport)) print self.__class__.__name__, "-", 'outsock connected to:', self.outport connected = True except: print self.__class__.__name__, "-", "outsock failed to connect to:", self.outport, tries, "times" tries += 1 if tries >= 10: raise connectionError time.sleep(.1) try: self.cbsock = q.get()[0] except: pass while wait.isAlive(): try: self.cbsock = q.get()[0] except: pass #cleanup dead thread if not wait.isAlive(): del wait self.playing = 1 threading.Thread(target=self.delegatecallbacks, args=(self.cbsock,)).start() self.outsock.sendall('inputmsgENDMESSAGE') def stop(self): if self.playing == 1: try: self.playing = 0 #print self.__class__.__name__, "-", "Stop" self.outsock.sendall('ENDMESSAGEstopENDMESSAGE') #print self.__class__.__name__, "-", 'closing...' #print self.__class__.__name__, "-", self.server.poll() ended = self.server.comm