mirror of
https://github.com/libretro/Mesen.git
synced 2025-01-18 23:02:57 +00:00
MMC5 Audio: Square channels + PCM (write mode only, no IRQs)
This commit is contained in:
parent
5124378867
commit
36404116ea
@ -236,7 +236,7 @@ void APU::StreamState(bool saving)
|
||||
Stream(_nesModel, squareChannel0, squareChannel1, triangleChannel, noiseChannel, deltaModulationChannel, frameCounter, mixer);
|
||||
}
|
||||
|
||||
void APU::AddExpansionAudioDelta(AudioChannel channel, int8_t delta)
|
||||
void APU::AddExpansionAudioDelta(AudioChannel channel, int16_t delta)
|
||||
{
|
||||
Instance->_mixer->SetExpansionAudioType(channel);
|
||||
Instance->_mixer->AddExpansionAudioDelta(Instance->_currentCycle, delta);
|
||||
|
@ -63,5 +63,5 @@ class APU : public Snapshotable, public IMemoryHandler
|
||||
|
||||
static void StaticRun();
|
||||
|
||||
static void AddExpansionAudioDelta(AudioChannel channel, int8_t delta);
|
||||
static void AddExpansionAudioDelta(AudioChannel channel, int16_t delta);
|
||||
};
|
@ -41,7 +41,10 @@ public:
|
||||
_period = 0;
|
||||
_lastOutput = 0;
|
||||
_previousCycle = 0;
|
||||
_mixer->Reset();
|
||||
if(_mixer) {
|
||||
//_mixer is null/not needed for MMC5 square channels
|
||||
_mixer->Reset();
|
||||
}
|
||||
}
|
||||
|
||||
virtual void StreamState(bool saving)
|
||||
@ -93,7 +96,7 @@ public:
|
||||
return 0;
|
||||
}
|
||||
|
||||
void AddOutput(int8_t output)
|
||||
virtual void AddOutput(int8_t output)
|
||||
{
|
||||
if(output != _lastOutput) {
|
||||
_mixer->AddDelta(_channel, _previousCycle, output - _lastOutput);
|
||||
|
@ -438,6 +438,7 @@
|
||||
<ClInclude Include="MMC3_49.h" />
|
||||
<ClInclude Include="MMC3_52.h" />
|
||||
<ClInclude Include="MMC3_ChrRam.h" />
|
||||
<ClInclude Include="MMC5Audio.h" />
|
||||
<ClInclude Include="ModChannel.h" />
|
||||
<ClInclude Include="Namco163.h" />
|
||||
<ClInclude Include="Namco163Audio.h" />
|
||||
|
@ -610,6 +610,9 @@
|
||||
<ClInclude Include="Sunsoft4.h">
|
||||
<Filter>Nes\Mappers\Sunsoft</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="MMC5Audio.h">
|
||||
<Filter>Nes\Mappers\MMC</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="stdafx.cpp">
|
||||
|
19
Core/MMC5.h
19
Core/MMC5.h
@ -2,6 +2,7 @@
|
||||
#include "stdafx.h"
|
||||
#include "BaseMapper.h"
|
||||
#include "PPU.h"
|
||||
#include "MMC5Audio.h"
|
||||
|
||||
class MMC5 : public BaseMapper
|
||||
{
|
||||
@ -10,6 +11,8 @@ private:
|
||||
const uint8_t NtEmptyIndex = 5;
|
||||
const uint8_t NtFillModeIndex = 6;
|
||||
|
||||
MMC5Audio _audio;
|
||||
|
||||
uint8_t _prgRamProtect1;
|
||||
uint8_t _prgRamProtect2;
|
||||
|
||||
@ -166,6 +169,8 @@ private:
|
||||
if(!PPU::GetControlFlags().BackgroundEnabled && !PPU::GetControlFlags().SpritesEnabled) {
|
||||
_ppuInFrame = false;
|
||||
}
|
||||
|
||||
_audio.ProcessCpuClock();
|
||||
}
|
||||
|
||||
virtual void NotifyVRAMAddressChange(uint16_t addr)
|
||||
@ -321,11 +326,12 @@ protected:
|
||||
|
||||
ArrayInfo<uint8_t> prgBanks = { _prgBanks, 5 };
|
||||
ArrayInfo<uint16_t> chrBanks = { _chrBanks, 12 };
|
||||
SnapshotInfo audio{ &_audio };
|
||||
Stream(_prgRamProtect1, _prgRamProtect2, _fillModeTile, _fillModeColor, _verticalSplitEnabled, _verticalSplitRightSide,
|
||||
_verticalSplitDelimiterTile, _verticalSplitScroll, _verticalSplitBank, _multiplierValue1, _multiplierValue2,
|
||||
_nametableMapping, _extendedRamMode, _exAttributeLastNametableFetch, _exAttrLastFetchCounter, _exAttrSelectedChrBank,
|
||||
_prgMode, prgBanks, _chrMode, _chrUpperBits, chrBanks, _lastChrReg,
|
||||
_spriteFetch, _largeSprites, _irqCounterTarget, _irqEnabled, _previousScanline, _irqCounter, _irqPending, _ppuInFrame);
|
||||
_spriteFetch, _largeSprites, _irqCounterTarget, _irqEnabled, _previousScanline, _irqCounter, _irqPending, _ppuInFrame, audio);
|
||||
|
||||
if(!saving) {
|
||||
UpdatePrgBanks();
|
||||
@ -393,6 +399,10 @@ protected:
|
||||
SwitchChrBank(addr, value);
|
||||
} else {
|
||||
switch(addr) {
|
||||
case 0x5000: case 0x5001: case 0x5002: case 0x5003: case 0x5004: case 0x5005: case 0x5006: case 0x5007: case 0x5010: case 0x5011: case 0x5015:
|
||||
_audio.WriteRegister(addr, value);
|
||||
break;
|
||||
|
||||
case 0x5100: _prgMode = value & 0x03; UpdatePrgBanks(); break;
|
||||
case 0x5101: _chrMode = value & 0x03; UpdateChrBanks(); break;
|
||||
case 0x5102: _prgRamProtect1 = value & 0x03; UpdatePrgBanks(); break;
|
||||
@ -430,6 +440,9 @@ protected:
|
||||
uint8_t ReadRegister(uint16_t addr)
|
||||
{
|
||||
switch(addr) {
|
||||
case 0x5010: case 0x5015:
|
||||
return _audio.ReadRegister(addr);
|
||||
|
||||
case 0x5204:
|
||||
{
|
||||
uint8_t value = (_ppuInFrame ? 0x40 : 0x00) | (_irqPending ? 0x80 : 0x00);
|
||||
@ -441,6 +454,8 @@ protected:
|
||||
case 0x5205: return (_multiplierValue1*_multiplierValue2) & 0xFF;
|
||||
case 0x5206: return (_multiplierValue1*_multiplierValue2) >> 8;
|
||||
}
|
||||
return 0;
|
||||
|
||||
//Open bus
|
||||
return (addr >> 8);
|
||||
}
|
||||
};
|
||||
|
150
Core/MMC5Audio.h
Normal file
150
Core/MMC5Audio.h
Normal file
@ -0,0 +1,150 @@
|
||||
#pragma once
|
||||
#include "stdafx.h"
|
||||
#include "SquareChannel.h"
|
||||
|
||||
class MMC5Square : public SquareChannel
|
||||
{
|
||||
int8_t _currentOutput;
|
||||
|
||||
private:
|
||||
virtual void InitializeSweep()
|
||||
{
|
||||
//"$5001 has no effect. The MMC5 pulse channels will not sweep, as they have no sweep unit."
|
||||
}
|
||||
|
||||
bool IsMuted()
|
||||
{
|
||||
//"Frequency values less than 8 do not silence the MMC5 pulse channels; they can output ultrasonic frequencies."
|
||||
return false;
|
||||
}
|
||||
public:
|
||||
MMC5Square() : SquareChannel(AudioChannel::MMC5, nullptr, false)
|
||||
{
|
||||
_currentOutput = 0;
|
||||
}
|
||||
|
||||
virtual void AddOutput(int8_t output)
|
||||
{
|
||||
_currentOutput = output;
|
||||
}
|
||||
|
||||
int8_t GetOutput()
|
||||
{
|
||||
return _currentOutput;
|
||||
}
|
||||
|
||||
void Run()
|
||||
{
|
||||
SquareChannel::Run(1);
|
||||
EndFrame();
|
||||
}
|
||||
};
|
||||
|
||||
class MMC5Audio : public Snapshotable
|
||||
{
|
||||
private:
|
||||
MMC5Square _square1;
|
||||
MMC5Square _square2;
|
||||
int16_t _audioCounter;
|
||||
int16_t _lastOutput;
|
||||
|
||||
bool _pcmReadMode;
|
||||
bool _pcmIrqEnabled;
|
||||
uint8_t _pcmOutput;
|
||||
|
||||
protected:
|
||||
void StreamState(bool saving)
|
||||
{
|
||||
SnapshotInfo square1{ &_square1 };
|
||||
SnapshotInfo square2{ &_square2 };
|
||||
Stream(square1, square2, _audioCounter, _lastOutput, _pcmReadMode, _pcmIrqEnabled, _pcmOutput);
|
||||
}
|
||||
|
||||
public:
|
||||
MMC5Audio()
|
||||
{
|
||||
_audioCounter = 0;
|
||||
_lastOutput = 0;
|
||||
_pcmReadMode = false;
|
||||
_pcmIrqEnabled = false;
|
||||
_pcmOutput = 0;
|
||||
}
|
||||
|
||||
void ProcessCpuClock()
|
||||
{
|
||||
_audioCounter--;
|
||||
_square1.Run();
|
||||
_square2.Run();
|
||||
if(_audioCounter <= 0) {
|
||||
//~240hz envelope/length counter
|
||||
switch(EmulationSettings::GetNesModel()) {
|
||||
default:
|
||||
case NesModel::NTSC: _audioCounter = CPU::ClockRateNtsc / 240; break;
|
||||
case NesModel::PAL: _audioCounter = CPU::ClockRatePal / 240; break;
|
||||
case NesModel::Dendy: _audioCounter = CPU::ClockRateDendy / 240; break;
|
||||
}
|
||||
|
||||
_square1.TickLengthCounter();
|
||||
_square1.TickEnvelope();
|
||||
_square2.TickLengthCounter();
|
||||
_square2.TickEnvelope();
|
||||
}
|
||||
|
||||
int16_t summedOutput = (_square1.GetOutput() + _square2.GetOutput()) * 4 + _pcmOutput;
|
||||
APU::AddExpansionAudioDelta(AudioChannel::MMC5, summedOutput - _lastOutput);
|
||||
_lastOutput = summedOutput;
|
||||
|
||||
_square1.ReloadCounter();
|
||||
_square2.ReloadCounter();
|
||||
}
|
||||
|
||||
uint8_t ReadRegister(uint16_t addr)
|
||||
{
|
||||
switch(addr) {
|
||||
case 0x5010:
|
||||
//TODO: PCM IRQ
|
||||
return 0;
|
||||
|
||||
case 0x5015:
|
||||
uint8_t status = 0;
|
||||
status |= _square1.GetStatus() ? 0x01 : 0x00;
|
||||
status |= _square2.GetStatus() ? 0x02 : 0x00;
|
||||
return status;
|
||||
}
|
||||
|
||||
//Open bus
|
||||
return (addr >> 8);
|
||||
}
|
||||
|
||||
void WriteRegister(uint16_t addr, uint8_t value)
|
||||
{
|
||||
switch(addr) {
|
||||
case 0x5000: case 0x5001: case 0x5002: case 0x5003:
|
||||
_square1.WriteRAM(addr, value);
|
||||
break;
|
||||
|
||||
case 0x5004: case 0x5005: case 0x5006: case 0x5007:
|
||||
_square2.WriteRAM(addr, value);
|
||||
break;
|
||||
|
||||
case 0x5010:
|
||||
//TODO: Read mode & PCM IRQs are not implemented
|
||||
_pcmReadMode = (value & 0x01) == 0x01;
|
||||
_pcmIrqEnabled = (value & 0x80) == 0x80;
|
||||
break;
|
||||
|
||||
case 0x5011:
|
||||
if(!_pcmReadMode) {
|
||||
if(value != 0) {
|
||||
_pcmOutput = value;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x5015:
|
||||
_square1.SetEnabled((value & 0x01) == 0x01);
|
||||
_square2.SetEnabled((value & 0x02) == 0x02);
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
@ -33,7 +33,7 @@ void SoundMixer::StreamState(bool saving)
|
||||
UpdateRates();
|
||||
}
|
||||
|
||||
ArrayInfo<int8_t> currentOutput = { _currentOutput, MaxChannelCount };
|
||||
ArrayInfo<int16_t> currentOutput = { _currentOutput, MaxChannelCount };
|
||||
Stream(_previousOutput, currentOutput);
|
||||
}
|
||||
|
||||
@ -151,14 +151,15 @@ int16_t SoundMixer::GetOutputVolume()
|
||||
int16_t expansionOutput = 0;
|
||||
switch(_expansionAudioType) {
|
||||
case AudioChannel::FDS: expansionOutput = (int16_t)(_currentOutput[ExpansionAudioIndex] * _volumes[ExpansionAudioIndex] * 20); break;
|
||||
case AudioChannel::VRC6: expansionOutput = (int16_t)(_currentOutput[ExpansionAudioIndex] * _volumes[ExpansionAudioIndex] * 75); break;
|
||||
case AudioChannel::MMC5: expansionOutput = (int16_t)(_currentOutput[ExpansionAudioIndex] * _volumes[ExpansionAudioIndex] * 40); break;
|
||||
case AudioChannel::Namco163: expansionOutput = (int16_t)(_currentOutput[ExpansionAudioIndex] * _volumes[ExpansionAudioIndex] * 20); break;
|
||||
case AudioChannel::Sunsoft5B: expansionOutput = (int16_t)(_currentOutput[ExpansionAudioIndex] * _volumes[ExpansionAudioIndex] * 20); break;
|
||||
case AudioChannel::VRC6: expansionOutput = (int16_t)(_currentOutput[ExpansionAudioIndex] * _volumes[ExpansionAudioIndex] * 75); break;
|
||||
}
|
||||
return squareVolume + tndVolume + expansionOutput;
|
||||
}
|
||||
|
||||
void SoundMixer::AddDelta(AudioChannel channel, uint32_t time, int8_t delta)
|
||||
void SoundMixer::AddDelta(AudioChannel channel, uint32_t time, int16_t delta)
|
||||
{
|
||||
if(delta != 0) {
|
||||
_timestamps.push_back(time);
|
||||
@ -166,7 +167,7 @@ void SoundMixer::AddDelta(AudioChannel channel, uint32_t time, int8_t delta)
|
||||
}
|
||||
}
|
||||
|
||||
void SoundMixer::AddExpansionAudioDelta(uint32_t time, int8_t delta)
|
||||
void SoundMixer::AddExpansionAudioDelta(uint32_t time, int16_t delta)
|
||||
{
|
||||
if(delta != 0) {
|
||||
_timestamps.push_back(time);
|
||||
|
@ -36,8 +36,8 @@ private:
|
||||
int16_t _previousOutput = 0;
|
||||
|
||||
vector<uint32_t> _timestamps;
|
||||
int8_t _channelOutput[MaxChannelCount][CycleLength];
|
||||
int8_t _currentOutput[MaxChannelCount];
|
||||
int16_t _channelOutput[MaxChannelCount][CycleLength];
|
||||
int16_t _currentOutput[MaxChannelCount];
|
||||
|
||||
blip_t* _blipBuf;
|
||||
int16_t *_outputBuffer;
|
||||
@ -63,10 +63,10 @@ public:
|
||||
void Reset();
|
||||
|
||||
void PlayAudioBuffer(uint32_t cycle);
|
||||
void AddDelta(AudioChannel channel, uint32_t time, int8_t delta);
|
||||
void AddDelta(AudioChannel channel, uint32_t time, int16_t delta);
|
||||
|
||||
void SetExpansionAudioType(AudioChannel channel);
|
||||
void AddExpansionAudioDelta(uint32_t time, int8_t delta);
|
||||
void AddExpansionAudioDelta(uint32_t time, int16_t delta);
|
||||
|
||||
static void StartRecording(string filepath);
|
||||
static void StopRecording();
|
||||
|
@ -28,13 +28,13 @@ private:
|
||||
uint32_t _sweepTargetPeriod = 0;
|
||||
uint16_t _realPeriod = 0;
|
||||
|
||||
bool IsMuted()
|
||||
virtual bool IsMuted()
|
||||
{
|
||||
//A period of t < 8, either set explicitly or via a sweep period update, silences the corresponding pulse channel.
|
||||
return _realPeriod < 8 || (!_sweepNegate && _sweepTargetPeriod > 0x7FF);
|
||||
}
|
||||
|
||||
void InitializeSweep(uint8_t regValue)
|
||||
virtual void InitializeSweep(uint8_t regValue)
|
||||
{
|
||||
_sweepEnabled = (regValue & 0x80) == 0x80;
|
||||
_sweepNegate = (regValue & 0x08) == 0x08;
|
||||
|
1
GUI.NET/Forms/Config/frmAudioConfig.Designer.cs
generated
1
GUI.NET/Forms/Config/frmAudioConfig.Designer.cs
generated
@ -254,7 +254,6 @@
|
||||
// trkMmc5Vol
|
||||
//
|
||||
this.trkMmc5Vol.Anchor = System.Windows.Forms.AnchorStyles.Top;
|
||||
this.trkMmc5Vol.Enabled = false;
|
||||
this.trkMmc5Vol.Location = new System.Drawing.Point(81, 160);
|
||||
this.trkMmc5Vol.Margin = new System.Windows.Forms.Padding(0);
|
||||
this.trkMmc5Vol.Maximum = 100;
|
||||
|
Loading…
x
Reference in New Issue
Block a user