MidiScheduler — wiki
Musical note-event scheduler
Simplifies making musical rhythms and chords in pygame. It combines the start and end timing of the note from separate midi-on midi-off commands to one note-event.
## Pauli Laine December 2010
## Distribute and modify freely
import pygame,random,time
import pygame.midi
from pygame.locals import *
pygame.init() #initialize
time.sleep(0.2)
pygame.midi.init()
output_id = pygame.midi.get_default_output_id()
print 'output', output_id
MidiOut = pygame.midi.Output(0,0)
class Scheduler:
def __init__(self):
## Here we have a small scheduling ring buffer with 4 bars each with 192 resolution
## 192 resolution seems to be good enough for normal purposes
self.sixe = int(192 * 4)
self.onlist = []
## Note_ons and note_offs in separate lists makes sorting non-necessary
for n in range(self.sixe):
tmp = []
self.onlist.append(tmp)
self.offlist = []
for n in range(self.sixe):
tmp = []
self.offlist.append(tmp)
def insertnote(self,note):
posit = note.startp % self.sixe
tmpnode = self.onlist[posit % len(self.onlist)]
tmpnode.append(note)
self.onlist[posit] = tmpnode
def insertnoteoff(self,note):
posit = note.endp % self.sixe
tmpnode = self.offlist[posit % len(self.onlist)]
tmpnode.append(note)
self.offlist[posit] = tmpnode
def putnote(self,note):
posit = note.startp % self.sixe
endposit = note.endp % self.sixe
self.insertnote(note)
self.insertnoteoff(note)
def handletick(self,posit):
posit %= self.sixe
tmpoff = self.offlist[posit]
tmpon = self.onlist[posit]
self.onlist[posit] = []
self.offlist[posit] = []
self.playticks(tmpoff,tmpon)
def playticks(self,noteoffs, noteons):
##Noteoffs first so as to avoid 'insta_kill' of a unisono note
for note in noteoffs:
MidiOut.write([[[0x90 + note.chan,note.pitch,0],pygame.midi.time()]])
for note in noteons:
MidiOut.write([[[0x90 + note.chan,note.pitch,note.dyn],pygame.midi.time()]])
## Notes with same startp will play simultaneously, as chords
## Note.endp controls the duration of the note
## startp and endp are 'ticks' - time interval controlled by
## pygame.time.wait() between calls to scheduler.handletick
class Note:
def __init__(self):
self.startp,self.endp = 0,0
self.dyn,self.chan = 65,0
self.pitch = 60
## Make a scheduler instance
s = Scheduler()
def testmidi():
globtick = 0
for k in range(121):
if (k % 3 == 0):
n = Note()
testpitch = k % 7 + (k / 7) ## just some mod-stuff to add variety to pitches
n.pitch = 34 + ((testpitch + 30) % 60)
## Note starttime and endtime
n.startp = globtick + (1 + (3 * (k % 8))) ## some variety to rhythms
n.endp = globtick + (8 + (3 * (k % 8))) ## listen for chords
## Note is appended to scheduler to a place determined by note.startp
s.putnote(n)
## Handle Note.startp's and endp's positioned in current tick
s.handletick(globtick)
globtick += 1
## Adjust this to control tempo
pygame.time.wait(20)
## Ensure that all notes are ended
for k in range(23):
s.handletick(globtick)
globtick += 1
pygame.time.wait(20)
import random
def testmidi_random():
globtick = 0
for k in range(121):
n = Note()
n.pitch = random.randint(36,72)
## Note starttime and endtime
stara = random.randint(1,12)
dura = random.randint(2, 8)
n.startp = globtick + stara
n.endp = globtick + stara + dura
n.dyn = random.randint(30,90)
s.putnote(n)
## Handle Note.startp's and endp's positioned in current tick
s.handletick(globtick)
globtick += 1
## Adjust this to control tempo
pygame.time.wait(180)
## Ensure that all notes are ended
for k in range(23):
s.handletick(globtick)
globtick += 1
pygame.time.wait(80)
## testmidi()
testmidi_random()