conductor=klass() function conductor:init(track,time) self.track=track self.time=time end function conductor:perform( performers, pitchblocks, notation ) local n_performers=#performers local n_pitchblocks=#pitchblocks.chunks assert(#notation==n_performers,"need "..n_performers.." parts for "..n_performers.." performers") for i=1,n_performers do assert(#notation[i]==n_pitchblocks,"performers should have "..n_pitchblocks.." for "..n_pitchblocks.." pitchblocks") local function resolve_pitch(t,ix) local block = pitchblocks.chunks[t+1] assert_range(ix,0,#block,"pitch ix") return block[ix+1] end local running_note=nil local function touch_running_note( t,pitch,force_new,verbs ) if force_new or (running_note and running_note.pitch != pitch) or (not running_note and pitch!=nil) then if running_note then performers[i].time=self.time+running_note.t performers[i]:play(t-running_note.t,running_note.pitch,running_note.verbs) running_note=nil end if pitch then running_note={ t=t, pitch=pitch, verbs=verbs } end end end for t=0,n_pitchblocks-1 do local note_char=notation[i][t+1] local pitch local force_start_new local verbs="" if note_char >= "a" and note_char <= "z" then ix = ord(note_char) - ord("a") pitch=resolve_pitch(t,ix) elseif note_char >= "A" and note_char <= "Z" then ix = ord(note_char) - ord("A") pitch=resolve_pitch(t,ix) force_start_new = true elseif note_char == "." then pitch=nil elseif note_char == "@" then pitch,verbs=0,"kick" elseif note_char == "!" then pitch,verbs=0,"snare" elseif note_char == "'" then pitch,verbs=0,"hihat" else assert(false,"wtf? "..note_char) end touch_running_note(t,pitch,force_start_new,verbs) end touch_running_note(n_pitchblocks,nil) performers[i].time=self.time end end