Mesen/Core/Namco163Audio.h
2016-12-17 23:14:47 -05:00

188 lines
4.4 KiB
C++

#pragma once
#include "stdafx.h"
#include "Snapshotable.h"
#include "APU.h"
#include "BaseExpansionAudio.h"
class Namco163Audio : public BaseExpansionAudio
{
private:
uint8_t _internalRam[0x80];
int16_t _channelOutput[8];
uint8_t _ramPosition;
bool _autoIncrement;
uint8_t _updateCounter;
int8_t _currentChannel;
int16_t _lastOutput;
bool _disableSound;
enum SoundReg
{
FrequencyLow = 0x00,
PhaseLow = 0x01,
FrequencyMid = 0x02,
PhaseMid = 0x03,
FrequencyHigh = 0x04,
WaveLength = 0x04,
PhaseHigh = 0x05,
WaveAddress = 0x06,
Volume = 0x07
};
uint32_t GetFrequency(int channel)
{
uint8_t baseAddr = 0x40 + channel * 0x08;
return ((_internalRam[baseAddr + SoundReg::FrequencyHigh] & 0x03) << 16) | (_internalRam[baseAddr + SoundReg::FrequencyMid] << 8) | _internalRam[baseAddr + SoundReg::FrequencyLow];
}
uint32_t GetPhase(int channel)
{
uint8_t baseAddr = 0x40 + channel * 0x08;
return (_internalRam[baseAddr + SoundReg::PhaseHigh] << 16) | (_internalRam[baseAddr + SoundReg::PhaseMid] << 8) | _internalRam[baseAddr + SoundReg::PhaseLow];
}
void SetPhase(int channel, uint32_t phase)
{
uint8_t baseAddr = 0x40 + channel * 0x08;
_internalRam[baseAddr + SoundReg::PhaseHigh] = (phase >> 16) & 0xFF;
_internalRam[baseAddr + SoundReg::PhaseMid] = (phase >> 8) & 0xFF;
_internalRam[baseAddr + SoundReg::PhaseLow] = phase & 0xFF;
}
uint8_t GetWaveAddress(int channel)
{
uint8_t baseAddr = 0x40 + channel * 0x08;
return _internalRam[baseAddr + SoundReg::WaveAddress];
}
uint8_t GetWaveLength(int channel)
{
uint8_t baseAddr = 0x40 + channel * 0x08;
return 256 - (_internalRam[baseAddr + SoundReg::WaveLength] & 0xFC);
}
uint8_t GetVolume(int channel)
{
uint8_t baseAddr = 0x40 + channel * 0x08;
return _internalRam[baseAddr + SoundReg::Volume] & 0x0F;
}
uint8_t GetNumberOfChannels()
{
return (_internalRam[0x7F] >> 4) & 0x07;
}
void UpdateChannel(int channel)
{
uint32_t phase = GetPhase(channel);
uint32_t freq = GetFrequency(channel);
uint8_t length = GetWaveLength(channel);
uint8_t offset = GetWaveAddress(channel);
uint8_t volume = GetVolume(channel);
if(length == 0) {
phase = 0;
} else {
phase = (phase + freq) % (length << 16);
}
uint8_t samplePosition = ((phase >> 16) + offset) & 0xFF;
int8_t sample;
if((samplePosition & 0x01)) {
sample = _internalRam[samplePosition / 2] >> 4;
} else {
sample = _internalRam[samplePosition / 2] & 0x0F;
}
_channelOutput[channel] = (sample - 8) * volume;
UpdateOutputLevel();
SetPhase(channel, phase);
}
void UpdateOutputLevel()
{
int16_t summedOutput = 0;
for(int i = 7, min = 7 - GetNumberOfChannels(); i >= min; i--) {
summedOutput += _channelOutput[i];
}
summedOutput /= GetNumberOfChannels();
APU::AddExpansionAudioDelta(AudioChannel::Namco163, summedOutput - _lastOutput);
_lastOutput = summedOutput;
}
protected:
void StreamState(bool saving) override
{
BaseExpansionAudio::StreamState(saving);
ArrayInfo<uint8_t> internalRam{ _internalRam, 0x80 };
ArrayInfo<int16_t> channelOutput{ _channelOutput, 8 };
Stream(internalRam, channelOutput, _ramPosition, _autoIncrement, _updateCounter, _currentChannel, _lastOutput);
}
void ClockAudio() override
{
if(!_disableSound && GetNumberOfChannels()) {
_updateCounter++;
if(_updateCounter == 15) {
UpdateChannel(_currentChannel);
_updateCounter = 0;
_currentChannel--;
if(_currentChannel < 7 - GetNumberOfChannels()) {
_currentChannel = 7;
}
}
}
}
public:
Namco163Audio()
{
memset(_internalRam, 0, sizeof(_internalRam));
memset(_channelOutput, 0, sizeof(_channelOutput));
_ramPosition = 0;
_autoIncrement = false;
_updateCounter = 0;
_currentChannel = 7;
_lastOutput = 0;
_disableSound = false;
}
void WriteRegister(uint16_t addr, uint8_t value)
{
switch(addr & 0xF800) {
case 0x4800:
_internalRam[_ramPosition] = value;
if(_autoIncrement) {
_ramPosition = (_ramPosition + 1) & 0x7F;
}
break;
case 0xE000:
_disableSound = (value & 0x40) == 0x40;
break;
case 0xF800:
_ramPosition = value & 0x7F;
_autoIncrement = (value & 0x80) == 0x80;
break;
}
}
uint8_t ReadRegister(uint16_t addr)
{
uint8_t value = 0;
switch(addr & 0xF800) {
case 0x4800: {
value = _internalRam[_ramPosition];
if(_autoIncrement) {
_ramPosition = (_ramPosition + 1) & 0x7F;
}
break;
}
}
return value;
}
};