Files
gen1recomp/tests/engine/chip_mono_fastpath_test.lua

115 lines
3.4 KiB
Lua

-- ../pokecrystal/audio/engine.asm:1987
package.path = "./?.lua;./?/init.lua;" .. package.path
local T = require("tests.harness")
local check, eq = T.check, T.eq
love = require("tests.love_stub")
local bit = require("bit")
local ChipAsm = require("src.audio.ChipAsm")
local ChipSynth = require("src.core.ChipSynth")
local SAMPLES = 4096
local song = ChipAsm.song{
channels = {
{ hw = 1, program = {
{ notetype = { speed = 8, volume = 12, fade = 2 } },
{ octave = 4 },
{ note = "C", len = 6 },
{ note = "E", len = 6 },
{ loop = { count = 0, to = 1 } },
} },
{ hw = 2, program = {
{ notetype = { speed = 6, volume = 10, fade = 1 } },
{ octave = 3 },
{ note = "G", len = 4 },
{ note = "A", len = 8 },
{ loop = { count = 0, to = 1 } },
} },
},
}
song.generation = 2
local data = { audio = { generation = 2 } }
local function render(fastPath)
ChipSynth._setMonoFastPathForTest(fastPath)
local engine = ChipSynth.newEngine(data, song, { allowLoops = true })
local frames = {}
for index = 1, SAMPLES do
local left, right = engine:sampleStereo()
frames[index] = { left, right }
end
return frames, engine
end
ChipSynth.setStereo(false)
local general = render(false)
local fast, fastEngine = render(true)
check(fastEngine.monoMix,
"a Gen 2 engine built with SOUND off takes the mono fast path")
local mismatches, sounding = 0, 0
for index = 1, SAMPLES do
if fast[index][1] ~= general[index][1]
or fast[index][2] ~= general[index][2] then
mismatches = mismatches + 1
end
if general[index][1] ~= 0 then sounding = sounding + 1 end
end
eq(mismatches, 0, "the mono fast path is bit-identical to the general path")
check(sounding > SAMPLES / 2,
"and the comparison ran against a song that is actually sounding")
local split = 0
for index = 1, SAMPLES do
if fast[index][1] ~= fast[index][2] then split = split + 1 end
end
eq(split, 0, "every mono frame carries the same value in both speakers")
eq(fastEngine.hpfCapRight, fastEngine.hpfCapLeft,
"the fast path keeps the right-hand HPF state in step")
eq(fastEngine.lpfRight, fastEngine.lpfLeft,
"and the right-hand LPF state too")
ChipSynth._setMonoFastPathForTest(true)
local forced = ChipSynth.newEngine(data, song, { allowLoops = true })
check(forced.monoMix, "the engine starts on the fast path")
local channel = forced.channels[2]
local mask = bit.lshift(1, channel.hardware - 1)
channel.tracks = bit.lshift(mask, 4)
channel.forcePanning = true
forced:refreshMonoMix()
check(not forced.monoMix,
"a force_stereo_panning channel drops the engine off the fast path")
local function spread(engine, count)
local left, right = 0, 0
for _ = 1, count do
local l, r = engine:sampleStereo()
left, right = left + math.abs(l), right + math.abs(r)
end
return left, right
end
local forcedLeft, forcedRight = spread(forced, SAMPLES)
check(forcedLeft ~= forcedRight,
"and the general path pans that channel to one speaker")
ChipSynth.setStereo(true)
local stereo = ChipSynth.newEngine(data, song, { allowLoops = true })
check(not stereo.monoMix, "STEREO never takes the mono fast path")
stereo.channels[2].tracks = bit.lshift(1, stereo.channels[2].hardware - 1)
local stereoLeft, stereoRight = spread(stereo, SAMPLES)
check(stereoLeft ~= stereoRight,
"a right-only track mixes differently into each speaker")
ChipSynth.setStereo(false)
ChipSynth._setMonoFastPathForTest(true)
T.finish("chip mono fast path")