Mesen/Core/FdsAudio.h
2016-01-30 14:57:50 -05:00

151 lines
3.3 KiB
C++

#pragma once
#include "stdafx.h"
#include "Snapshotable.h"
#include "EmulationSettings.h"
#include "APU.h"
#include "BaseFdsChannel.h"
#include "ModChannel.h"
#include <algorithm>
class FdsAudio : public Snapshotable
{
private:
const uint32_t WaveVolumeTable[4] = { 36, 24, 17, 14 };
//Register values
uint8_t _waveTable[64];
bool _waveWriteEnabled = false;
BaseFdsChannel _volume;
ModChannel _mod;
bool _disableEnvelopes = false;
bool _haltWaveform = false;
uint8_t _masterVolume = 0;
//Internal values
uint16_t _waveOverflowCounter = 0;
int32_t _wavePitch = 0;
uint8_t _wavePosition = 0;
uint8_t _lastOutput = 0;
protected:
void StreamState(bool saving)
{
Stream(&_volume);
Stream(&_mod);
StreamArray<uint8_t>(_waveTable, 64);
Stream<bool>(_waveWriteEnabled);
Stream<bool>(_disableEnvelopes);
Stream<bool>(_haltWaveform);
Stream<uint8_t>(_masterVolume);
//Internal values
Stream<uint16_t>(_waveOverflowCounter);
Stream<int32_t>(_wavePitch);
Stream<uint8_t>(_wavePosition);
Stream<uint8_t>(_lastOutput);
}
public:
void Clock()
{
//"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());
}
} else {
_wavePitch = 0;
}
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;
_wavePosition = (_wavePosition + 1) & 0x3F;
}
}
}
uint8_t ReadRegister(uint16_t addr)
{
if(addr <= 0x407F) {
return 0x40 | _waveTable[addr & 0x3F];
} else {
switch(addr) {
case 0x4090: return 0x40 | _volume.GetGain();
case 0x4092: return 0x40 | _mod.GetGain();
}
}
//Open bus
return (addr >> 8);
}
void WriteRegister(uint16_t addr, uint8_t value)
{
if(addr <= 0x407F) {
if(_waveWriteEnabled) {
_waveTable[addr & 0x3F] = value & 0x3F;
}
} else {
switch(addr) {
case 0x4080:
case 0x4082:
_volume.WriteReg(addr, value);
break;
case 0x4083:
_disableEnvelopes = (value & 0x40) == 0x40;
_haltWaveform = (value & 0x80) == 0x80;
_volume.WriteReg(addr, value);
break;
case 0x4084:
case 0x4085:
case 0x4086:
case 0x4087:
_mod.WriteReg(addr, value);
break;
case 0x4088:
_mod.WriteModTable(value);
break;
case 0x4089:
_masterVolume = value & 0x03;
_waveWriteEnabled = (value & 0x80) == 0x80;
break;
case 0x408A:
_volume.SetMasterEnvelopeSpeed(value);
_mod.SetMasterEnvelopeSpeed(value);
break;
}
}
}
};