[alsa-devel] pyalsa: synchronizing queue with MIDI clock events
Christopher Arndt
chris.arndt at web.de
Fri Jul 17 14:54:00 CEST 2009
Hi everybody,
this is my first post on this list, so please excuse me, should it not
be the right place to post this.
I started exploring programming the ALSA sequencer via the pyalsa Python
interface, i.e the alsaseq module. I wrote a small utility that reads
MIDI events from one port, filters/processes them and writes it to
another. This allows for routing by channel, note range, controller
number etc. and to filter out, replace or add MIDI events.
Now to synchronize the queue tempo to incoming MIDI clock events, to be
able implement synchronized delays or arpeggiators, I use the method in
the code shown below. This works ok, but I notice that the measured BPM
oscillates +-1 BPM around the value displayed by the clock source (my
synth's sequencer) if I measure/average only a few (~10) ticks or do not
round the result to an integer value.
Is this the right approach? What is a sensible number of ticks to take
into account for measurement? Should I use another reference timer than
Python's time.time() function? Is pyalsa generally suitable for this
kind of application even on older/weaker hardware (e.g. a NSLU2)?
Any comments or suggestions for improvement would be very much appreciated!
Chris
class MidiProcessor(object):
def run(self):
# This is simplified to only show the general logic
while True:
events = self.sequencer.receive_events(
timeout=RECEIVE_TIMEOUT, maxevents=1)
for event in events:
if event.type == SEQ_EVENT_CLOCK:
self.sync_queue(event)
else:
# do other MIDI processing / routing
def sync_queue(self, event):
"""Sync queue tempo to incoming MIDI clock events."""
# list to collect the timestamps of the last few ticks
lt = self._last_ticks
lt.append(time.time())
ltlen = len(lt)
if ltlen > 1:
# calculate & set bpm: calculate difference between
# the times the last few ticks were received and average
# all results
avg_delta = sum(
[y-x for x,y in zip(lt, lt[1:])]) / (ltlen-1)
# tick length is a 24th of a quarter note
bpm = round(60 / avg_delta / 24)
if bpm != self.bpm:
self.bpm = bpm
self.sequencer.queue_tempo(self.queue,
tempo=int(6e7 / self.bpm), ppq=self.ppq)
# only remember last 24 received ticks
# (length of a quarter note)
if ltlen > 24:
lt.pop(0)
More information about the Alsa-devel
mailing list