track=klass() function track:init() self.frames={} self.ix_to_frame={} self.next_frame_start=0 end function track:add(len) for i=0,len-1 do self.ix_to_frame[self.next_frame_start+i]={#self.frames,i} end add(self.frames,{ pattern:new({len=len}), pattern:new({len=len}), pattern:new({len=len}), pattern:new({len=len}), }) self.next_frame_start+=len end function track:pattern(channel,offset) offset = offset or -1 channel &= 0xffff offset &= 0xffff assert_range(channel,0,4,"channel") local n_frames_long=#self.frames assert_range(offset,-n_frames_long,n_frames_long,"offset") if offset<0 then offset+=n_frames_long end return self.frames[offset+1][channel+1] end function track:plot(channel,offset,ndata) assert_range(channel,0,4,"channel") assert_range(offset,0,self.next_frame_start,"offset") local tup=self.ix_to_frame[offset] assert(tup) -- should be unable to fail local frame,offset=unpack(tup) self.frames[frame+1][channel+1]:plot(offset,ndata) end function track:build( free_patterns, frame_a, frame_z ) local n_frames_long = #self.frames if (not frame_z) frame_z = frame_a + n_frames_long assert(frame_z-frame_a == n_frames_long, "wrong number of frames (must be ".. frame_a.." to "..frame_a+n_frames_long..")") -- dump patterns and frames local mapped_patterns={} local function map_to_real_pattern(pat) if (pat:silent()) return 0 | (1<<6) local key = pat:key() mapped_patterns[key] = mapped_patterns[key] or {} local t = mapped_patterns[key] for other in all(t) do if (pat:eq(other)) return other.map_ix end assert(#free_patterns>0, "out of free patterns") pat:map_to(deli(free_patterns,1)) add(t,pat) return pat.map_ix end local fmaddr=0x3100+(frame_a)*4 for frame=1,n_frames_long do for i=0,3 do poke(fmaddr+i,map_to_real_pattern(self.frames[frame][i+1])) end fmaddr+=4 end end