mirror of
https://github.com/libretro/MesenCE.git
synced 2026-11-10 11:11:32 -10:00
-Super Mario Land 2 - Pops in menu are fixed by immediately updating APU output after a write -Perfect Dark - Low voice volume is fixed by having the correct output when the channels are disabled (but DAC is still enabled) -Daiku no Gen - Low voice volume is fixed by keeping square channel output to digital 0 (= analog 1) until its first tick after being enabled (the game does not let the channel tick at all while the voice sample is playing)
88 lines
2.5 KiB
C++
88 lines
2.5 KiB
C++
#pragma once
|
|
#include "pch.h"
|
|
|
|
class GbEnvelope
|
|
{
|
|
public:
|
|
template<typename T, typename U>
|
|
static void ClockEnvelope(T& state, U& channel)
|
|
{
|
|
uint8_t timer = state.EnvTimer;
|
|
if(state.EnvTimer == 0 || --state.EnvTimer == 0) {
|
|
if(state.EnvPeriod > 0 && !state.EnvStopped) {
|
|
if(state.EnvRaiseVolume && state.Volume < 0x0F) {
|
|
state.Volume++;
|
|
} else if(!state.EnvRaiseVolume && state.Volume > 0) {
|
|
state.Volume--;
|
|
} else {
|
|
state.EnvStopped = true;
|
|
}
|
|
|
|
//Clocking envelope should update output immediately (based on div_trigger_volume/channel_4_volume_div tests)
|
|
channel.UpdateOutput();
|
|
|
|
state.EnvTimer = state.EnvPeriod;
|
|
if(timer == 0) {
|
|
//When the timer was already 0 (because period was 0), it looks like the next
|
|
//clock occurs earlier than expected.
|
|
//This fixes the last test result in channel_1_nrx2_glitch (but may be incorrect)
|
|
state.EnvTimer--;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
template<typename T, typename U>
|
|
static void WriteRegister(T& state, uint8_t value, U& channel)
|
|
{
|
|
bool raiseVolume = (value & 0x08) != 0;
|
|
uint8_t period = value & 0x07;
|
|
|
|
if((value & 0xF8) == 0) {
|
|
state.Enabled = false;
|
|
state.Output = 0;
|
|
} else {
|
|
//This implementation of the Zombie mode behavior differs from the description
|
|
//found here: https://gbdev.gg8.se/wiki/articles/Gameboy_sound_hardware
|
|
//Instead, it's based on the behavior of the channel_1_nrx2_glitch test and
|
|
//and SameBoy's implementation of the glitch
|
|
bool preventIncrement = false;
|
|
if(raiseVolume != state.EnvRaiseVolume) {
|
|
if(raiseVolume) {
|
|
if(!state.EnvStopped && state.EnvPeriod == 0) {
|
|
state.Volume ^= 0x0F;
|
|
} else {
|
|
state.Volume = 14 - state.Volume;
|
|
}
|
|
preventIncrement = true;
|
|
} else {
|
|
//"If the mode was changed (add to subtract or subtract to add), volume is set to 16 - volume."
|
|
state.Volume = 16 - state.Volume;
|
|
}
|
|
|
|
//"Only the low 4 bits of volume are kept"
|
|
state.Volume &= 0xF;
|
|
}
|
|
|
|
if(!state.EnvStopped && !preventIncrement) {
|
|
if(state.EnvPeriod == 0 && (period || raiseVolume)) {
|
|
if(raiseVolume) {
|
|
//"If the old envelope period was zero and the envelope is still doing automatic updates, volume is incremented by 1"
|
|
state.Volume++;
|
|
} else {
|
|
state.Volume--;
|
|
}
|
|
|
|
//"Only the low 4 bits of volume are kept"
|
|
state.Volume &= 0xF;
|
|
}
|
|
}
|
|
}
|
|
|
|
state.EnvPeriod = period;
|
|
state.EnvRaiseVolume = raiseVolume;
|
|
state.EnvVolume = (value & 0xF0) >> 4;
|
|
|
|
channel.UpdateOutput();
|
|
}
|
|
}; |