FDS: Fixed sound emulation (fixes Bio Miracle)
This commit is contained in:
parent
3b34a7c113
commit
5ccbe95a55
4 changed files with 96 additions and 65 deletions
|
@ -29,47 +29,47 @@ public:
|
|||
|
||||
virtual void WriteReg(uint16_t addr, uint8_t value)
|
||||
{
|
||||
switch(addr & 0x03) {
|
||||
case 0:
|
||||
switch(addr) {
|
||||
case 0x4080:
|
||||
_speed = value & 0x3F;
|
||||
_volumeIncrease = (value & 0x40) == 0x40;
|
||||
_envelopeOff = (value & 0x80) == 0x80;
|
||||
|
||||
//"Writing to this register immediately resets the clock timer that ticks the volume envelope (delaying the next tick slightly)."
|
||||
_timer = 8 * (_speed + 1) * _masterSpeed;
|
||||
ResetTimer();
|
||||
|
||||
if(_envelopeOff) {
|
||||
//Envelope is off, gain = speed
|
||||
_gain = _speed;
|
||||
}
|
||||
break;
|
||||
|
||||
case 2:
|
||||
case 0x4082:
|
||||
_frequency = (_frequency & 0x0F00) | value;
|
||||
break;
|
||||
|
||||
case 3:
|
||||
case 0x4083:
|
||||
_frequency = (_frequency & 0xFF) | ((value & 0x0F) << 8);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void TickEnvelope(bool envelopesDisabled)
|
||||
bool TickEnvelope()
|
||||
{
|
||||
if(_envelopeOff) {
|
||||
_gain = _speed;
|
||||
} else if(!envelopesDisabled && _speed > 0) {
|
||||
if(_timer > 0) {
|
||||
_timer--;
|
||||
} else {
|
||||
_timer = 8 * (_speed + 1) * _masterSpeed;
|
||||
if(!_envelopeOff && _masterSpeed > 0) {
|
||||
_timer--;
|
||||
if(_timer == 0) {
|
||||
ResetTimer();
|
||||
|
||||
if(_volumeIncrease) {
|
||||
if(_gain < 32) {
|
||||
_gain++;
|
||||
}
|
||||
} else {
|
||||
if(_gain > 0) {
|
||||
_gain--;
|
||||
}
|
||||
if(_volumeIncrease && _gain < 32) {
|
||||
_gain++;
|
||||
} else if(!_volumeIncrease && _gain > 0) {
|
||||
_gain--;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
uint8_t GetGain()
|
||||
|
@ -81,4 +81,9 @@ public:
|
|||
{
|
||||
return _frequency;
|
||||
}
|
||||
|
||||
void ResetTimer()
|
||||
{
|
||||
_timer = 8 * (_speed + 1) * _masterSpeed;
|
||||
}
|
||||
};
|
|
@ -285,7 +285,9 @@ bool FDS::IsDiskInserted()
|
|||
uint8_t FDS::ReadRegister(uint16_t addr)
|
||||
{
|
||||
uint8_t value = 0;
|
||||
if(_diskRegEnabled && addr < 0x4040 || _soundRegEnabled && addr >= 0x4040) {
|
||||
if(_soundRegEnabled && addr >= 0x4040) {
|
||||
return _audio->ReadRegister(addr);
|
||||
} else if(_diskRegEnabled && addr <= 0x4033) {
|
||||
switch(addr) {
|
||||
case 0x4030:
|
||||
value |= CPU::HasIRQSource(IRQSource::External) ? 0x01 : 0x00;
|
||||
|
@ -313,9 +315,6 @@ uint8_t FDS::ReadRegister(uint16_t addr)
|
|||
case 0x4033:
|
||||
//Always return good battery
|
||||
return 0x80 & _extConWriteReg;
|
||||
|
||||
default:
|
||||
return _audio->ReadRegister(addr);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -47,41 +47,46 @@ protected:
|
|||
|
||||
void ClockAudio() override
|
||||
{
|
||||
//"The envelopes are not ticked while the waveform is halted."
|
||||
_volume.TickEnvelope(_disableEnvelopes || _haltWaveform);
|
||||
_mod.TickEnvelope(_disableEnvelopes || _haltWaveform);
|
||||
|
||||
if(_mod.IsEnabled()) {
|
||||
if(_mod.TickModulator()) {
|
||||
//Modulator was ticked, update wave pitch
|
||||
_wavePitch = _mod.GetWavePitch(_volume.GetFrequency());
|
||||
int frequency = _volume.GetFrequency();
|
||||
if(!_haltWaveform && !_disableEnvelopes) {
|
||||
_volume.TickEnvelope();
|
||||
if(_mod.TickEnvelope()) {
|
||||
_mod.UpdateOutput(frequency);
|
||||
}
|
||||
} else {
|
||||
_wavePitch = 0;
|
||||
}
|
||||
|
||||
if(_mod.TickModulator()) {
|
||||
//Modulator was ticked, update wave pitch
|
||||
_mod.UpdateOutput(frequency);
|
||||
}
|
||||
|
||||
if(_haltWaveform) {
|
||||
//"The high bit of this register halts the waveform and resets its phase to 0. Note that if halted it will output the constant value at $4040"
|
||||
//"writes to the volume register $4080 or master volume $4089 will affect the output."
|
||||
_wavePosition = 0;
|
||||
}
|
||||
|
||||
int32_t freq = _volume.GetFrequency() + _wavePitch;
|
||||
if(freq > 0 && !_waveWriteEnabled) {
|
||||
_waveOverflowCounter += freq;
|
||||
if(_waveOverflowCounter < freq) {
|
||||
//Overflow, tick
|
||||
uint32_t level = std::min((int)_volume.GetGain(), 32) * WaveVolumeTable[_masterVolume];
|
||||
|
||||
uint8_t outputLevel = (_waveTable[_wavePosition] * level) / 1152;
|
||||
APU::AddExpansionAudioDelta(AudioChannel::FDS, outputLevel - _lastOutput);
|
||||
_lastOutput = outputLevel;
|
||||
UpdateOutput();
|
||||
} else {
|
||||
UpdateOutput();
|
||||
|
||||
_wavePosition = (_wavePosition + 1) & 0x3F;
|
||||
if(frequency + _mod.GetOutput() > 0 && !_waveWriteEnabled) {
|
||||
_waveOverflowCounter += frequency + _mod.GetOutput();
|
||||
if(_waveOverflowCounter < frequency + _mod.GetOutput()) {
|
||||
_wavePosition = (_wavePosition + 1) & 0x3F;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void UpdateOutput()
|
||||
{
|
||||
uint32_t level = std::min((int)_volume.GetGain(), 32) * WaveVolumeTable[_masterVolume];
|
||||
uint8_t outputLevel = (_waveTable[_wavePosition] * level) / 1152;
|
||||
|
||||
|
||||
if(_lastOutput != outputLevel) {
|
||||
APU::AddExpansionAudioDelta(AudioChannel::FDS, outputLevel - _lastOutput);
|
||||
_lastOutput = outputLevel;
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
uint8_t ReadRegister(uint16_t addr)
|
||||
{
|
||||
|
@ -113,6 +118,10 @@ public:
|
|||
case 0x4083:
|
||||
_disableEnvelopes = (value & 0x40) == 0x40;
|
||||
_haltWaveform = (value & 0x80) == 0x80;
|
||||
if(_disableEnvelopes) {
|
||||
_volume.ResetTimer();
|
||||
_mod.ResetTimer();
|
||||
}
|
||||
_volume.WriteReg(addr, value);
|
||||
break;
|
||||
|
||||
|
|
|
@ -14,6 +14,7 @@ private:
|
|||
uint8_t _modTable[64];
|
||||
uint8_t _modTablePosition = 0;
|
||||
uint16_t _overflowCounter = 0;
|
||||
int32_t _output = 0;
|
||||
|
||||
protected:
|
||||
void StreamState(bool saving) override
|
||||
|
@ -21,17 +22,28 @@ protected:
|
|||
BaseFdsChannel::StreamState(saving);
|
||||
|
||||
ArrayInfo<uint8_t> modTable = { _modTable, 64 };
|
||||
Stream(_counter, _modulationDisabled, _modTablePosition, _overflowCounter, modTable);
|
||||
Stream(_counter, _modulationDisabled, _modTablePosition, _overflowCounter, modTable, _output);
|
||||
}
|
||||
|
||||
public:
|
||||
virtual void WriteReg(uint16_t addr, uint8_t value) override
|
||||
{
|
||||
switch(addr & 0x03) {
|
||||
case 1: UpdateCounter(value & 0x7F); break;
|
||||
case 3: _modulationDisabled = (value & 0x80) == 0x80; break;
|
||||
switch(addr) {
|
||||
case 0x4084:
|
||||
case 0x4086:
|
||||
BaseFdsChannel::WriteReg(addr, value);
|
||||
break;
|
||||
case 0x4085:
|
||||
UpdateCounter(_counter & 0x7F);
|
||||
break;
|
||||
case 0x4087:
|
||||
BaseFdsChannel::WriteReg(addr, value);
|
||||
_modulationDisabled = (value & 0x80) == 0x80;
|
||||
if(_modulationDisabled) {
|
||||
_overflowCounter = 0;
|
||||
}
|
||||
break;
|
||||
}
|
||||
BaseFdsChannel::WriteReg(addr, value);
|
||||
}
|
||||
|
||||
void WriteModTable(uint8_t value)
|
||||
|
@ -61,22 +73,23 @@ public:
|
|||
|
||||
bool TickModulator()
|
||||
{
|
||||
_overflowCounter += _frequency;
|
||||
if(IsEnabled()) {
|
||||
_overflowCounter += _frequency;
|
||||
|
||||
if(_overflowCounter < _frequency) {
|
||||
//Overflowed, tick the modulator
|
||||
int32_t offset = _modLut[_modTable[_modTablePosition]];
|
||||
UpdateCounter(offset == ModReset ? 0 : _counter + offset);
|
||||
if(_overflowCounter < _frequency) {
|
||||
//Overflowed, tick the modulator
|
||||
int32_t offset = _modLut[_modTable[_modTablePosition]];
|
||||
UpdateCounter(offset == ModReset ? 0 : _counter + offset);
|
||||
|
||||
_modTablePosition = (_modTablePosition + 1) & 0x3F;
|
||||
_modTablePosition = (_modTablePosition + 1) & 0x3F;
|
||||
|
||||
return true;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
int32_t GetWavePitch(uint16_t volumePitch)
|
||||
void UpdateOutput(uint16_t volumePitch)
|
||||
{
|
||||
//Code from NesDev Wiki
|
||||
|
||||
|
@ -108,6 +121,11 @@ public:
|
|||
}
|
||||
|
||||
// final mod result is in temp
|
||||
return temp;
|
||||
_output = temp;
|
||||
}
|
||||
|
||||
int32_t GetOutput()
|
||||
{
|
||||
return IsEnabled() ? _output : 0;
|
||||
}
|
||||
};
|
||||
|
|
Loading…
Add table
Reference in a new issue