fceux/output/luaScripts/SoundDisplay2.lua

325 lines
14 KiB
Lua

-- feos, r57shell, 2012-2018
-- gui.box frame simulates transparency
print("Hi-hat and keys may glitch if you produce sound effects.")
print("Leftclick over the displays: channel names to hide the volumes, notes to hide the keyboard.")
print(" ")
print("And praise Gocha!")
iterator = 15
kb = {x=9, y=154, on=true}
prev_keys = input.get()
semitones = {"A", "A#", "B", "C", "C#", "D", "D#", "E", "F", "F#", "G", "G#"}
volumes = {
S1V = {0}, S1C = {},
S2V = {0}, S2C = {},
TV = {0},
NV = {0},
DPCMV = {0}
}
function Draw()
snd = sound.get()
keys = input.get()
if iterator == 1 then
kb.y = 30
else
kb.y = 154
end
-- do only at the first frame
if #volumes.S1V == 1 then
channels = {
Square1 = {x=1, y=9, vol=volumes.S1V, color=volumes.S1C, duty=0, midi=0, semitone=0, octave=0, prev_semitone=0, float = {}, floaty = {}, floatz = {}},
Square2 = {x=1+45*1, y=9, vol=volumes.S2V, color=volumes.S2C, duty=0, midi=0, semitone=0, octave=0, prev_semitone=0, float = {}, floaty = {}, floatz = {}},
Triangle = {x=1+45*2, y=9, vol=volumes.TV, midi=0, semitone=0, octave=0, prev_semitone=0, float = {}, floaty = {}, floatz = {}},
Noise = {x=1+45*3, y=9, vol=volumes.NV, midi=0, semitone=0, octave=0},
DPCM = {x=1+45*4, y=9, vol=volumes.DPCMV}
}
end
-- update the first indices for volume tables
-- shift the previous ones farther
table.insert(channels.Square1.vol, 1, snd.rp2a03.square1.volume*15)
table.insert(channels.Square2.vol, 1, snd.rp2a03.square2.volume*15)
table.insert(channels.Triangle.vol, 1, snd.rp2a03.triangle.volume*15)
table.insert(channels.Noise.vol, 1, snd.rp2a03.noise.volume*15)
table.insert(channels.DPCM.vol, 1, snd.rp2a03.dpcm.volume*15)
-- get duty and midikey for proper channels
channels.Square1.duty = snd.rp2a03.square1.duty
channels.Square2.duty = snd.rp2a03.square2.duty
channels.Square1.midi = snd.rp2a03.square1.midikey
channels.Square2.midi = snd.rp2a03.square2.midikey
channels.Triangle.midi = snd.rp2a03.triangle.midikey
channels.Noise.midi = snd.rp2a03.noise.midikey
-- guess notes
for name, chan in pairs(channels) do
if name == "Square1" or name == "Square2" or name == "Triangle" or name == "Noise" then
if chan.vol[1] > 0 then
chan.octave = math.floor((chan.midi - 12) / 12)
chan.semitone = tostring(semitones[math.floor((chan.midi - 21) % 12) + 1])
else chan.semitone = "--"; chan.octave = "-"
end
end
end
-- notes display
gui.text(kb.x+203, kb.y, "S1: "..channels.Square1.semitone..channels.Square1.octave, "#ff0000ff", "#000000ff")
gui.text(kb.x+203, kb.y+9, "S2: "..channels.Square2.semitone..channels.Square2.octave, "#aa00ccff", "#000000ff")
gui.text(kb.x+204, kb.y+18, "Tr: "..channels.Triangle.semitone..channels.Triangle.octave, "#00aaffff", "#000000ff")
gui.text(kb.x+204, kb.y+27, "Ns: "..channels.Noise.semitone..channels.Noise.octave, "#ffffffff", "#000000ff")
-----------------
-- Draw hi-hat --
-----------------
xhh1 = 227
yhh1 = 18
xhh2 = 227
yhh2 = 18
if channels.Noise.vol[1] > 0 then
if channels.Noise.octave >= 9 and channels.Noise.octave <= 12 then
colorhh = "#ffaa00"
if channels.Noise.vol[2] - channels.Noise.vol[1] < 4
and channels.Noise.vol[2] > 0
then yhh1 = 15
end
end
else colorhh = "#00000000"
end
gui.line(xhh1-1, yhh1, xhh1+28, yhh1, "#00000088")
gui.line(xhh1-1, yhh1-1, xhh1+28, yhh1-1, "#00000088")
gui.line(xhh1-1, yhh1-2, xhh1+28, yhh1-2, "#00000088")
gui.line(xhh1+3, yhh1-3, xhh1+24, yhh1-3, "#00000088")
gui.line(xhh1+8, yhh1-4, xhh1+19, yhh1-4, "#00000088")
gui.line(xhh1+11, yhh1-5, xhh1+16, yhh1-5, "#00000088")
gui.line(xhh1+12, yhh1-6, xhh1+15, yhh1-6, "#00000088")
gui.line(xhh2-1, yhh2, xhh2+28, yhh2, "#00000088")
gui.line(xhh2-1, yhh2+1, xhh2+28, yhh2+1, "#00000088")
gui.line(xhh2-1, yhh2+2, xhh2+28, yhh2+2, "#00000088")
gui.line(xhh2+3, yhh2+3, xhh2+24, yhh2+3, "#00000088")
gui.line(xhh2+8, yhh2+4, xhh2+19, yhh2+4, "#00000088")
gui.line(xhh2+11, yhh2+5, xhh2+16, yhh2+5, "#00000088")
gui.line(xhh2+12, yhh2+6, xhh2+15, yhh2+6, "#00000088")
gui.line(xhh1, yhh1-1, xhh1+27, yhh1-1, colorhh)
gui.line(xhh1+4, yhh1-2, xhh1+23, yhh1-2, colorhh)
gui.line(xhh1+9, yhh1-3, xhh1+18, yhh1-3, colorhh)
gui.line(xhh1+12, yhh1-4, xhh1+15, yhh1-4, colorhh)
gui.line(xhh1+13, yhh1-5, xhh1+14, yhh1-5, colorhh)
gui.line(xhh2, yhh2+1, xhh2+27, yhh2+1, colorhh)
gui.line(xhh2+4, yhh2+2, xhh2+23, yhh2+2, colorhh)
gui.line(xhh2+9, yhh2+3, xhh2+18, yhh2+3, colorhh)
gui.line(xhh2+12, yhh2+4, xhh2+15, yhh2+4, colorhh)
gui.line(xhh2+13, yhh2+5, xhh2+14, yhh2+5, colorhh)
--------------------
-- Keyboard stuff --
--------------------
if (kb.on) then
-- capture leftclicks
if keys.xmouse <= 256 and keys.xmouse >= 205 and keys.ymouse >= kb.y and keys.ymouse <= kb.y+27 then
if keys["leftclick"] and not prev_keys["leftclick"] then kb.on = false end
end
-- draw the kayboard
gui.box(kb.x-8, kb.y, kb.x+200, kb.y+16, "#ffffffff") -- white solid box
for a = -2, 49 do gui.box(kb.x+4*a, kb.y, kb.x+4*a, kb.y+16, "#00000000") end -- black lines
-- draw colored boxes as clean notes
for name, chan in pairs(channels) do
if name == "Square1" or name == "Square2" or name == "Triangle" then
if name == "Triangle" then color = "#00aaffff"
elseif name == "Square1" then color = "#ff0000ff"
else color = "#aa00ccff"
end
if chan.semitone == "C" then gui.box(kb.x+1 +28*(chan.octave-1), kb.y, kb.x+3 +28*(chan.octave-1), kb.y+16, color)
elseif chan.semitone == "D" then gui.box(kb.x+5 +28*(chan.octave-1), kb.y, kb.x+7 +28*(chan.octave-1), kb.y+16, color)
elseif chan.semitone == "E" then gui.box(kb.x+9 +28*(chan.octave-1), kb.y, kb.x+11+28*(chan.octave-1), kb.y+16, color)
elseif chan.semitone == "F" then gui.box(kb.x+13+28*(chan.octave-1), kb.y, kb.x+15+28*(chan.octave-1), kb.y+16, color)
elseif chan.semitone == "G" then gui.box(kb.x+17+28*(chan.octave-1), kb.y, kb.x+19+28*(chan.octave-1), kb.y+16, color)
elseif chan.semitone == "A" then gui.box(kb.x+21+28*(chan.octave-1), kb.y, kb.x+23+28*(chan.octave-1), kb.y+16, color)
elseif chan.semitone == "B" then gui.box(kb.x+25+28*(chan.octave-1), kb.y, kb.x+27+28*(chan.octave-1), kb.y+16, color)
end
end
end
-- draw accidental keys
gui.box(kb.x-3, kb.y, kb.x-5, kb.y+10, "#00000000")
for oct = 0, 6 do
gui.box(kb.x+3+28*oct, kb.y, kb.x+5+28*oct, kb.y+10, "#00000000")
gui.box(kb.x+7+28*oct, kb.y, kb.x+9+28*oct, kb.y+10, "#00000000")
gui.box(kb.x+15+28*oct, kb.y, kb.x+17+28*oct, kb.y+10, "#00000000")
gui.box(kb.x+19+28*oct, kb.y, kb.x+21+28*oct, kb.y+10, "#00000000")
gui.box(kb.x+23+28*oct, kb.y, kb.x+25+28*oct, kb.y+10, "#00000000")
end
-- draw colored boxes over accidental keys
for name, chan in pairs(channels) do
if name == "Square1" or name == "Square2" or name == "Triangle" then
if name == "Triangle" then color = "#00aaffff"
elseif name == "Square1" then color = "#ff0000ff"
else color = "#aa00ccff"
end
if chan.semitone == "C#" then gui.box(kb.x+3 +28*(chan.octave-1), kb.y, kb.x+5 +28*(chan.octave-1), kb.y+10, color)
elseif chan.semitone == "D#" then gui.box(kb.x+7 +28*(chan.octave-1), kb.y, kb.x+9 +28*(chan.octave-1), kb.y+10, color)
elseif chan.semitone == "F#" then gui.box(kb.x+15+28*(chan.octave-1), kb.y, kb.x+17+28*(chan.octave-1), kb.y+10, color)
elseif chan.semitone == "G#" then gui.box(kb.x+19+28*(chan.octave-1), kb.y, kb.x+21+28*(chan.octave-1), kb.y+10, color)
elseif chan.semitone == "A#" then gui.box(kb.x+23+28*(chan.octave-1), kb.y, kb.x+25+28*(chan.octave-1), kb.y+10, color)
end
end
end
grey = "#aaaaaaaa"
for oct = 0, 6 do
gui.line(kb.x+3+28*oct, kb.y+10, kb.x+5+28*oct, kb.y+10, grey)
gui.line(kb.x+7+28*oct, kb.y+10, kb.x+9+28*oct, kb.y+10, grey)
gui.line(kb.x+15+28*oct, kb.y+10, kb.x+17+28*oct, kb.y+10, grey)
gui.line(kb.x+19+28*oct, kb.y+10, kb.x+21+28*oct, kb.y+10, grey)
gui.line(kb.x+23+28*oct, kb.y+10, kb.x+25+28*oct, kb.y+10, grey)
end
gui.line(kb.x-3, kb.y+10, kb.x-5, kb.y+10, grey)
gui.line(kb.x-8, kb.y, kb.x+200, kb.y, "#00000088")
gui.line(kb.x-8, kb.y+16, kb.x+200, kb.y+16, "#00000088")
gui.line(kb.x-8, kb.y, kb.x-8, kb.y+16, "#00000088")
gui.line(kb.x+200, kb.y, kb.x+200, kb.y+16, "#00000088")
else
-- capture leftclicks
if keys.xmouse <= 256 and keys.xmouse >= 205 and keys.ymouse >= kb.y and keys.ymouse <= kb.y+27 then
if keys["leftclick"] and not prev_keys["leftclick"] then kb.on = true end
end
end
--------------------
-- Floating notes --
--------------------
if (kb.on) then
for name, chan in pairs(channels) do
if name == "Square1" or name == "Square2" or name == "Triangle" then
if chan.prev_semitone ~= chan.semitone then
local len_prev = #chan.float
if chan.semitone == "C" then table.insert(chan.float, 1, kb.x+1 +28*(chan.octave-1))
elseif chan.semitone == "D" then table.insert(chan.float, 1, kb.x+5 +28*(chan.octave-1))
elseif chan.semitone == "E" then table.insert(chan.float, 1, kb.x+9 +28*(chan.octave-1))
elseif chan.semitone == "F" then table.insert(chan.float, 1, kb.x+13+28*(chan.octave-1))
elseif chan.semitone == "G" then table.insert(chan.float, 1, kb.x+17+28*(chan.octave-1))
elseif chan.semitone == "A" then table.insert(chan.float, 1, kb.x+21+28*(chan.octave-1))
elseif chan.semitone == "B" then table.insert(chan.float, 1, kb.x+25+28*(chan.octave-1))
elseif chan.semitone == "C#" then table.insert(chan.float, 1, kb.x+3 +28*(chan.octave-1))
elseif chan.semitone == "D#" then table.insert(chan.float, 1, kb.x+7 +28*(chan.octave-1))
elseif chan.semitone == "F#" then table.insert(chan.float, 1, kb.x+15+28*(chan.octave-1))
elseif chan.semitone == "G#" then table.insert(chan.float, 1, kb.x+19+28*(chan.octave-1))
elseif chan.semitone == "A#" then table.insert(chan.float, 1, kb.x+23+28*(chan.octave-1))
end
if len_prev ~= #chan.float then table.insert(chan.floaty, 1, 0) table.insert(chan.floatz, 1, 0) end
elseif chan.semitone ~= '--' then
chan.floaty[1] = 0
end
if name == "Triangle" then color = "#00aaffff"
elseif name == "Square1" then color = "#ff0000ff"
else color = "#aa00ccff"
end
for i = 1, #chan.float do
local y = kb.y+chan.floaty[i]
local z = kb.y+chan.floatz[i]
chan.floaty[i] = chan.floaty[i] + 1
chan.floatz[i] = chan.floatz[i] + 1
if y+17<=z+15 then
if movie.framecount()%2 == 0 then gui.box(chan.float[i]-1, y+16, chan.float[i]+3, z+16, "#eedd2200") end
gui.box(chan.float[i], y+17, chan.float[i]+2, z+15, color)
end
end
while #chan.float ~= 0 and chan.floaty[#chan.float] > 224 do
table.remove(chan.float, #chan.float)
table.remove(chan.floaty, #chan.floaty)
table.remove(chan.floatz, #chan.floatz)
end
end
end
end
---------------------
-- Volumes display --
---------------------
for name, chan in pairs(channels) do
if name == "Square1" or name == "Square2" then
-- set color for each duty value
if chan.duty == 0 then table.insert(chan.color,1,"#aaff00ff")
elseif chan.duty == 1 then table.insert(chan.color,1,"#00ff00ff")
elseif chan.duty == 2 then table.insert(chan.color,1,"#00bb00ff")
else table.insert(chan.color,1,"#008800ff")
end
end
-- capture leftclicks
if iterator == 15 then
if keys.ymouse <= 24 and keys.ymouse >= 0 then
if keys["leftclick"] and not prev_keys["leftclick"] then iterator = 1 end
end
else
if keys.ymouse <= 24 and keys.ymouse >= 0 then
if keys["leftclick"] and not prev_keys["leftclick"] then iterator = 15 end
end
end
-- draw volumes
gui.text(chan.x, 9, name, "#ffffffff", "#000000ff")
if iterator <=14 then
-- draw just first volume values
gui.text(chan.x, chan.y+9+1, chan.vol[1])
if tonumber(chan.vol[1]) > 0 then
for j = 0, chan.vol[1]-1 do
gui.box(chan.x+13+j*2, chan.y+9, chan.x+15+j*2, chan.y+8+9, "#000000ff")
gui.line(chan.x+14+j*2, chan.y+1+9, chan.x+14+j*2, chan.y+7+9, "#00ff00ff")
if name == "Square1" or name == "Square2" then
-- color comes from duty
gui.text(chan.x+38, chan.y, chan.duty, chan.color[1], "#000000ff")
gui.line(chan.x+14+j*2, chan.y+1+9, chan.x+14+j*2, chan.y+7+9, chan.color[1])
end
end
end
else
-- draw all 15 volume values
for i = 1, #chan.vol do
gui.text(chan.x, chan.y+i*9+1, chan.vol[i])
if tonumber(chan.vol[i]) > 0 then
for j = 0, chan.vol[i]-1 do
gui.box(chan.x+13+j*2, chan.y+i*9, chan.x+15+j*2, chan.y+8+i*9, "#000000ff")
gui.line(chan.x+14+j*2, chan.y+1+i*9, chan.x+14+j*2, chan.y+7+i*9, "#00ff00ff")
if name == "Square1" or name == "Square2" then
-- color comes from duty
gui.text(chan.x+38, chan.y, chan.duty, chan.color[1], "#000000ff")
gui.line(chan.x+14+j*2, chan.y+1+i*9, chan.x+14+j*2, chan.y+7+i*9, chan.color[i])
end
end
end
end
end
-- keep the table limited
table.remove(chan.vol, 15)
-- highlight the first values
-- 30 Hz blinking, works properly if your monitor is set to 60 Hz
if chan.vol[1] > 0 and movie.framecount()%2 == 0 then
gui.box(chan.x+12, chan.y+8, chan.x+14+chan.vol[1]*2, chan.y+18, "#ffcc0000")
end
end
for name, chan in pairs(channels) do
if name == "Square1" or name == "Square2" or name == "Triangle" then
chan.prev_semitone = chan.semitone
end
end
prev_keys = keys
end
emu.registerafter(Draw);