i'm attaching a demo of what it takes to write your own merging buffer in
python.  it took me 4 hours (moderately experienced programmer new to
python).  there are some tricky things to look out for, and i had the
advantage of a built-in threadsafe PriorityQueue.  i have to set my own
internal latency to around 10ms in order to keep the missed timestamps
around 1-3%, and i lose whatever fancy driver/hardware optimizations apple
might have in place (did they get "active midi timing" from emagic?).  if
portmidi took care of this, large classes of applications might be able to
avoid concurrency altogether, and timing-sensitive applications would be
feasible in slower dynamic/interpreted environments (and those that don't
expose threading).  these issues definitely seem like perfect things for
portmidi to save users from.
-e
import pypm
import threading
import time
import bisect
import Queue

def allNotesOff(m):
    for i in range(16):
        for j in range(2**7-1):
            m.Write([[[0x90 + i,j,0],0]])

def mean(x):
    return sum(x)/len(x)

def std(x):
    m=mean(x)
    return mean(map(lambda y:abs(y-m),x))

class midiBuffer(threading.Thread):
        def __init__(self):
                self.stop=False
                self.incoming=Queue.PriorityQueue() #threadsafe
                self.outgoing=[]
                threading.Thread.__init__(self)
                
        def run(self):
            latency=1 #latency is in ms, 0 means ignore timestamps
            dev = 0
            MidiOut=pypm.Output(dev, latency)

            myLatency=10
            t=0

            class report():pass
            rpt=report()
            rpt.sleeps=0
            rpt.latencies=[]
            
            try:
                while not self.stop:
                    try:
                        while True:
                            s,e=self.incoming.get_nowait()
                            if s<=t:
                                MidiOut.Write(e)
                            else:
                                #since PriorityQueue has no peek method, transfer events to a peekable format
                                self.outgoing.insert(bisect.bisect(map(lambda x:x[0],self.outgoing),s),[s,e])
                    except Queue.Empty: pass

                    while pypm.Time()<=t:
                        rpt.sleeps+=1
                        time.sleep(.0003) #prevent proc hog                    
                        
                    t=pypm.Time()
                    while len(self.outgoing)>0 and t>=self.outgoing[0][0]-myLatency:
                        rpt.latencies.append(self.outgoing[0][0]-t)
                        MidiOut.Write(self.outgoing.pop(0)[1])
            finally:
                print "sleeps: " + str(rpt.sleeps)
                misses=len(filter(lambda x:x<0,rpt.latencies))
                print "latencies (ms, negative=late): mean=" + str(mean(rpt.latencies)) + " std=" + str(std(rpt.latencies)) + " misses=" + str(misses) + " of " + str(len(rpt.latencies)) + " (" + str(100*misses/len(rpt.latencies)) +"%)"
                allNotesOff(MidiOut)
                del MidiOut

def playChord(buff,notes,chan,vel,dur,onset):
    ChordList = []
    for i in range(len(notes)):
        ChordList.append([[0x90 + chan,notes[i],vel], onset])
    buff.incoming.put((onset,ChordList))
    ChordList = []
    offset=onset+dur*1000
    for i in range(len(notes)):
        ChordList.append([[0x90 + chan,notes[i],0],offset])
    buff.incoming.put((offset,ChordList))   

def test():
        pypm.Initialize()
        b=midiBuffer()
        b.start()
        
        tempo=120
        dur=60.0/tempo/2
        notes=[60, 63, 66, 70]
        n=0
        while n<100:
                t=pypm.Time() 
                playChord(b,[notes[0]-12],1,127,dur,t)
                playChord(b,notes,0,60,dur,t)
                n+=1
                while pypm.Time()<t+2*dur*1000: pass

        b.stop=True
        b.join()
        pypm.Terminate()
        
test()
_______________________________________________
media_api mailing list
media_api@create.ucsb.edu
http://lists.create.ucsb.edu/mailman/listinfo/media_api

Reply via email to