-- Scrolling Pitch Display for FCEUX -- 2014-07-19 -- Robert Hart (rnhart.net) SCREEN_LEFT = 0 SCREEN_RIGHT = 255 MIDI_LEFT = 18 MIDI_RIGHT = 102 MIDI_RANGE = (MIDI_RIGHT + 1) - MIDI_LEFT SCREEN_RANGE = (SCREEN_RIGHT + 1) - SCREEN_LEFT BACKGROUND_COLOR = {0,0,0,70} SCROLL_BOTTOM = 225 SCROLL_SIZE = 216 KEYBOARD_SIZE = 5 KEYBOARD_TOP = SCROLL_BOTTOM KEYBOARD_BOTTOM = SCROLL_BOTTOM + KEYBOARD_SIZE KEYBOARD_FILLS = ({[0]="white","black","white","black","white","white","black","white","black","white","black","white"}) -- FCEUX 2.2.2 pitch fixes SHORT_FIX_DOWN = 12 * ( math.log(93/2) / math.log(2) ) DPCM_FIX_DOWN = 12 * ( math.log(11) / math.log(2) ) function round(x) return math.floor( x + .5 ) end function Scale(x) temp = math.floor ( (x - MIDI_LEFT) * SCREEN_RANGE / MIDI_RANGE + .5 ) if (temp < SCREEN_LEFT) then temp = SCREEN_LEFT elseif (temp > SCREEN_RIGHT) then temp = SCREEN_RIGHT end return temp end WIDTH = {0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2} COLOR = {[0]="P39", [1]="P15", [2]="P2C", [3]="P15", ["triangle"]="P12", ["noise-short"]="P30", ["noise-long"]="P10", ["dpcm"]="P1A", ["dpcm-melodic"]="P2A" } function Reset() history = {} previous_frame = emu.framecount() - 1 -- Set up DPCM visualization function hash = rom.gethash("md5") if (hash == "d4782e6bbf337f075174345fe4848983") then -- My demo DELTAS_PERIOD = 64 DPCM_KEY_DOWN = 12 * (math.log(DELTAS_PERIOD)/math.log(2)) DPCM_KEY_DOWN = DPCM_KEY_DOWN + DPCM_FIX_DOWN DPCM_Function = function() if(snd.dpcm.volume > 0) then if (snd.dpcm.dmcaddress == 0xC000) then dpcm_key = Scale(snd.dpcm.midikey - DPCM_KEY_DOWN) dpcm_color = COLOR["dpcm-melodic"] dpcm_width = WIDTH[8] else dpcm_key = Scale(MIDI_LEFT + 1 + snd.dpcm.regs.frequency) dpcm_color = COLOR["dpcm"] dpcm_width = WIDTH[8] end return {dpcm_key, dpcm_color, dpcm_width} else return nil end end elseif (hash == "e02c7c510e0e37b1f2711103fff9e58a") then -- Ikinari Musician DELTAS_PERIOD = 64 DPCM_KEY_DOWN = 12 * (math.log(DELTAS_PERIOD)/math.log(2)) DPCM_KEY_DOWN = DPCM_KEY_DOWN + DPCM_FIX_DOWN DPCM_Function = function() if(snd.dpcm.volume > 0) then if (snd.dpcm.dmcaddress == 0xFF00) then dpcm_key = Scale(MIDI_LEFT + 1 + snd.dpcm.regs.frequency) dpcm_color = COLOR["dpcm"] dpcm_width = WIDTH[round((snd.dpcm.dmcseed/127)*14)+1] elseif (snd.dpcm.dmcaddress == 0xFF80) then dpcm_key = Scale(snd.dpcm.midikey - DPCM_KEY_DOWN) dpcm_color = COLOR["dpcm-melodic"] dpcm_width = WIDTH[8] end return {dpcm_key, dpcm_color, dpcm_width} else return nil end end else -- Default DPCM previous_seed = sound.get().rp2a03.dpcm.dmcseed DPCM_Function = function() if(snd.dpcm.volume > 0) then previous_seed = snd.dpcm.dmcseed return {Scale(MIDI_LEFT + 1 + snd.dpcm.regs.frequency), COLOR["dpcm"], WIDTH[8]} elseif ( snd.dpcm.dmcseed ~= previous_seed) then seed_change = math.abs(previous_seed - snd.dpcm.dmcseed) previous_seed = snd.dpcm.dmcseed return {Scale(MIDI_LEFT + 17), COLOR["dpcm"], (math.floor(((seed_change-1)/127)*3))} else return nil end end end end function Draw() if(emu.framecount() - previous_frame ~= 1) then Reset() end previous_frame = emu.framecount() gui.box(0,0,255,239,BACKGROUND_COLOR) snd = sound.get().rp2a03 if(snd.square1.volume > 0) then c1 = {Scale(snd.square1.midikey),COLOR[snd.square1.duty], WIDTH[math.floor(15*snd.square1.volume)]} else c1 = nil end if(snd.square2.volume > 0) then c2 = {Scale(snd.square2.midikey),COLOR[snd.square2.duty], WIDTH[math.floor(15*snd.square2.volume)]} else c2 = nil end if((snd.triangle.volume > 0) and (snd.triangle.regs.frequency > 1)) then c3 = {Scale(snd.triangle.midikey),COLOR["triangle"], WIDTH[8]} else c3 = nil end if(snd.noise.volume > 0) then if(snd.noise.short) then noise_key = Scale(snd.noise.midikey - SHORT_FIX_DOWN) noise_color = COLOR["noise-short"] else noise_key = Scale(MIDI_LEFT + 16 - snd.noise.regs.frequency) noise_color = COLOR["noise-long"] end c4 = {noise_key, noise_color, WIDTH[math.floor(15*snd.noise.volume)]} else c4 = nil end c5 = DPCM_Function() table.insert(history, 1, {c1,c2,c3,c4,c5}) table.remove(history, SCROLL_SIZE) -- draw keyboard for i = MIDI_LEFT,MIDI_RIGHT do fill = KEYBOARD_FILLS[i%12] left = Scale(i - .5) right = Scale(i + .5) gui.box(left,KEYBOARD_TOP,right,KEYBOARD_BOTTOM,fill,"gray") end -- draw notes for i = 1, #history do for j = 1, 5 do if(type(history[i][j]) ~= "nil") then x = history[i][j][1] y = SCROLL_BOTTOM - i c = history[i][j][2] w = history[i][j][3] gui.box(x-w-1,y-1,x+w+1,y,"black") gui.line(x-w,y,x+w,y,c) end end end end Reset() emu.registerafter(Draw) emu.print( " blue = triangle gray = noise, long*") emu.print( " cyan = pulse, 1/2 white = noise, short") emu.print( " red = pulse, 1/4 dk gn = dpcm, non-periodic*") emu.print( " yellow = pulse, 1/8 green = dpcm, periodic") emu.print( "") emu.print( "* percussive frequencies shown as 16 lowest pitches")