mirror of
https://github.com/libretro/Mesen.git
synced 2024-11-23 17:19:39 +00:00
188 lines
4.4 KiB
C++
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)
|
|
{
|
|
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()
|
|
{
|
|
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;
|
|
}
|
|
}; |