mirror of
https://github.com/libretro/Mesen.git
synced 2024-11-23 17:19:39 +00:00
VRC6: Implemented all 3 audio channels
This commit is contained in:
parent
ac938995b6
commit
76600d31a9
@ -548,6 +548,8 @@
|
||||
<ClInclude Include="VRC2_4.h" />
|
||||
<ClInclude Include="VRC3.h" />
|
||||
<ClInclude Include="VRC6.h" />
|
||||
<ClInclude Include="Vrc6Pulse.h" />
|
||||
<ClInclude Include="Vrc6Saw.h" />
|
||||
<ClInclude Include="VRC7.h" />
|
||||
<ClInclude Include="VrcIrq.h" />
|
||||
<ClInclude Include="VsControlManager.h" />
|
||||
|
@ -68,6 +68,9 @@
|
||||
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
|
||||
<Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
|
||||
</Filter>
|
||||
<Filter Include="Nes\Mappers\VRC">
|
||||
<UniqueIdentifier>{0de6642c-1ea6-4872-b21f-2c3009eee9ad}</UniqueIdentifier>
|
||||
</Filter>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="IAudioDevice.h">
|
||||
@ -97,9 +100,6 @@
|
||||
<ClInclude Include="IMessageManager.h">
|
||||
<Filter>Nes\Interfaces</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="VRC2_4.h">
|
||||
<Filter>Nes\Mappers</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="NetMessage.h">
|
||||
<Filter>NetPlay\Messages</Filter>
|
||||
</ClInclude>
|
||||
@ -283,24 +283,12 @@
|
||||
<ClInclude Include="NtdecTc112.h">
|
||||
<Filter>Nes\Mappers</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="VRC3.h">
|
||||
<Filter>Nes\Mappers</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Rambo1.h">
|
||||
<Filter>Nes\Mappers</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="BaseMapper.h">
|
||||
<Filter>Nes\Mappers</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="VRC6.h">
|
||||
<Filter>Nes\Mappers</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="VrcIrq.h">
|
||||
<Filter>Nes\Mappers</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="VRC7.h">
|
||||
<Filter>Nes\Mappers</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="FdsLoader.h">
|
||||
<Filter>Nes\RomLoader</Filter>
|
||||
</ClInclude>
|
||||
@ -580,15 +568,36 @@
|
||||
<ClInclude Include="stdafx.h">
|
||||
<Filter>Misc</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="VRC1.h">
|
||||
<Filter>Nes\Mappers</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="TxSRom.h">
|
||||
<Filter>Nes\Mappers</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="WaveRecorder.h">
|
||||
<Filter>Misc</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="VrcIrq.h">
|
||||
<Filter>Nes\Mappers\VRC</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="VRC1.h">
|
||||
<Filter>Nes\Mappers\VRC</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="VRC2_4.h">
|
||||
<Filter>Nes\Mappers\VRC</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="VRC3.h">
|
||||
<Filter>Nes\Mappers\VRC</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="VRC6.h">
|
||||
<Filter>Nes\Mappers\VRC</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="VRC7.h">
|
||||
<Filter>Nes\Mappers\VRC</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Vrc6Pulse.h">
|
||||
<Filter>Nes\Mappers\VRC</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Vrc6Saw.h">
|
||||
<Filter>Nes\Mappers\VRC</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="stdafx.cpp">
|
||||
|
@ -151,6 +151,7 @@ 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;
|
||||
}
|
||||
return squareVolume + tndVolume + expansionOutput;
|
||||
}
|
||||
|
52
Core/VRC6.h
52
Core/VRC6.h
@ -2,6 +2,9 @@
|
||||
#include "stdafx.h"
|
||||
#include "BaseMapper.h"
|
||||
#include "VrcIrq.h"
|
||||
#include "Vrc6Pulse.h"
|
||||
#include "Vrc6Saw.h"
|
||||
|
||||
enum class VRCVariant;
|
||||
|
||||
//incomplete - missing audio and more
|
||||
@ -9,9 +12,16 @@ class VRC6 : public BaseMapper
|
||||
{
|
||||
private:
|
||||
VrcIrq _irq;
|
||||
Vrc6Pulse _pulse1;
|
||||
Vrc6Pulse _pulse2;
|
||||
Vrc6Saw _saw;
|
||||
|
||||
VRCVariant _model;
|
||||
uint8_t _bankingMode;
|
||||
uint8_t _chrRegisters[8];
|
||||
int32_t _lastOutput;
|
||||
|
||||
bool _haltAudio;
|
||||
|
||||
void UpdatePrgRamAccess()
|
||||
{
|
||||
@ -25,7 +35,9 @@ protected:
|
||||
void InitMapper()
|
||||
{
|
||||
_irq.Reset();
|
||||
_lastOutput = 0;
|
||||
_bankingMode = 0;
|
||||
_haltAudio = false;
|
||||
memset(_chrRegisters, 0, sizeof(_chrRegisters));
|
||||
SelectPRGPage(3, -1);
|
||||
}
|
||||
@ -33,9 +45,13 @@ protected:
|
||||
virtual void StreamState(bool saving)
|
||||
{
|
||||
BaseMapper::StreamState(saving);
|
||||
Stream(_irq);
|
||||
ArrayInfo<uint8_t> chrRegisters = { _chrRegisters, 8 };
|
||||
Stream(_bankingMode, chrRegisters);
|
||||
Stream(_bankingMode, chrRegisters, _lastOutput, _haltAudio);
|
||||
|
||||
Stream(&_irq);
|
||||
Stream(&_pulse1);
|
||||
Stream(&_pulse2);
|
||||
Stream(&_saw);
|
||||
|
||||
if(!saving) {
|
||||
UpdatePrgRamAccess();
|
||||
@ -45,6 +61,15 @@ protected:
|
||||
void ProcessCpuClock()
|
||||
{
|
||||
_irq.ProcessCpuClock();
|
||||
if(!_haltAudio) {
|
||||
_pulse1.Clock();
|
||||
_pulse2.Clock();
|
||||
_saw.Clock();
|
||||
}
|
||||
|
||||
int32_t outputLevel = _pulse1.GetVolume() + _pulse2.GetVolume() + _saw.GetVolume();
|
||||
APU::AddExpansionAudioDelta(AudioChannel::VRC6, outputLevel - _lastOutput);
|
||||
_lastOutput = outputLevel;
|
||||
}
|
||||
|
||||
void UpdatePpuBanking()
|
||||
@ -100,11 +125,32 @@ protected:
|
||||
SelectPrgPage2x(0, (value & 0x0F) << 1);
|
||||
break;
|
||||
|
||||
case 0x9000: case 0x9001: case 0x9002:
|
||||
_pulse1.WriteReg(addr, value);
|
||||
break;
|
||||
|
||||
case 0x9003: {
|
||||
_haltAudio = (value & 0x01) == 0x01;
|
||||
uint8_t frequencyShift = (value & 0x04) == 0x04 ? 8 : ((value & 0x02) == 0x02 ? 4 : 0);
|
||||
_pulse1.SetFrequencyShift(frequencyShift);
|
||||
_pulse2.SetFrequencyShift(frequencyShift);
|
||||
_saw.SetFrequencyShift(frequencyShift);
|
||||
break;
|
||||
}
|
||||
|
||||
case 0xA000: case 0xA001: case 0xA002:
|
||||
_pulse2.WriteReg(addr, value);
|
||||
break;
|
||||
|
||||
case 0xB000: case 0xB001: case 0xB002:
|
||||
_saw.WriteReg(addr, value);
|
||||
break;
|
||||
|
||||
case 0xB003:
|
||||
_bankingMode = value;
|
||||
UpdatePpuBanking();
|
||||
break;
|
||||
|
||||
|
||||
case 0xC000: case 0xC001: case 0xC002: case 0xC003:
|
||||
SelectPRGPage(2, value & 0x1F);
|
||||
break;
|
||||
|
73
Core/Vrc6Pulse.h
Normal file
73
Core/Vrc6Pulse.h
Normal file
@ -0,0 +1,73 @@
|
||||
#pragma once
|
||||
#include "stdafx.h"
|
||||
#include "Snapshotable.h"
|
||||
|
||||
class Vrc6Pulse: public Snapshotable
|
||||
{
|
||||
private:
|
||||
uint8_t _volume = 0;
|
||||
uint8_t _dutyCycle = 0;
|
||||
bool _ignoreDuty = false;
|
||||
uint16_t _frequency = 1;
|
||||
bool _enabled = false;
|
||||
|
||||
int32_t _timer = 1;
|
||||
uint8_t _step = 0;
|
||||
uint8_t _frequencyShift = 0;
|
||||
|
||||
void StreamState(bool saving)
|
||||
{
|
||||
Stream(_volume, _dutyCycle, _ignoreDuty, _frequency, _enabled, _timer, _step, _frequencyShift);
|
||||
}
|
||||
|
||||
public:
|
||||
void WriteReg(uint16_t addr, uint8_t value)
|
||||
{
|
||||
switch(addr & 0x03) {
|
||||
case 0:
|
||||
_volume = value & 0x0F;
|
||||
_dutyCycle = (value & 0x70) >> 4;
|
||||
_ignoreDuty = (value & 0x80) == 0x80;
|
||||
break;
|
||||
|
||||
case 1:
|
||||
_frequency = (_frequency & 0x0F00) | value;
|
||||
break;
|
||||
|
||||
case 2:
|
||||
_frequency = (_frequency & 0xFF) | ((value & 0x0F) << 8);
|
||||
_enabled = (value & 0x80) == 0x80;
|
||||
if(!_enabled) {
|
||||
_step = 0;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void SetFrequencyShift(uint8_t shift)
|
||||
{
|
||||
_frequencyShift = shift;
|
||||
}
|
||||
|
||||
void Clock()
|
||||
{
|
||||
if(_enabled) {
|
||||
_timer--;
|
||||
if(_timer == 0) {
|
||||
_step = (_step + 1) & 0x0F;
|
||||
_timer = (_frequency >> _frequencyShift) + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t GetVolume()
|
||||
{
|
||||
if(!_enabled) {
|
||||
return 0;
|
||||
} else if(_ignoreDuty) {
|
||||
return _volume;
|
||||
} else {
|
||||
return _step <= _dutyCycle ? _volume : 0;
|
||||
}
|
||||
}
|
||||
};
|
79
Core/Vrc6Saw.h
Normal file
79
Core/Vrc6Saw.h
Normal file
@ -0,0 +1,79 @@
|
||||
#pragma once
|
||||
#include "stdafx.h"
|
||||
#include "Snapshotable.h"
|
||||
|
||||
class Vrc6Saw : public Snapshotable
|
||||
{
|
||||
private:
|
||||
uint8_t _accumulatorRate = 0;
|
||||
uint8_t _accumulator = 0;
|
||||
uint16_t _frequency = 1;
|
||||
bool _enabled = false;
|
||||
|
||||
int32_t _timer = 1;
|
||||
uint8_t _step = 0;
|
||||
uint8_t _frequencyShift = 0;
|
||||
|
||||
void StreamState(bool saving)
|
||||
{
|
||||
Stream(_accumulatorRate, _accumulator, _frequency, _enabled, _timer, _step, _frequencyShift);
|
||||
}
|
||||
|
||||
public:
|
||||
void WriteReg(uint16_t addr, uint8_t value)
|
||||
{
|
||||
switch(addr & 0x03) {
|
||||
case 0:
|
||||
_accumulatorRate = value & 0x3F;
|
||||
break;
|
||||
|
||||
case 1:
|
||||
_frequency = (_frequency & 0x0F00) | value;
|
||||
break;
|
||||
|
||||
case 2:
|
||||
_frequency = (_frequency & 0xFF) | ((value & 0x0F) << 8);
|
||||
_enabled = (value & 0x80) == 0x80;
|
||||
if(!_enabled) {
|
||||
//If E is clear, the accumulator is forced to zero until E is again set.
|
||||
_accumulator = 0;
|
||||
|
||||
//"The phase of the saw generator can be mostly reset by clearing and immediately setting E. Clearing E does not reset the frequency divider, however."
|
||||
_step = 0;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void SetFrequencyShift(uint8_t shift)
|
||||
{
|
||||
_frequencyShift = shift;
|
||||
}
|
||||
|
||||
void Clock()
|
||||
{
|
||||
if(_enabled) {
|
||||
_timer--;
|
||||
if(_timer == 0) {
|
||||
_step = (_step + 1) % 14;
|
||||
_timer = (_frequency >> _frequencyShift) + 1;
|
||||
|
||||
if(_step == 0) {
|
||||
_accumulator = 0;
|
||||
} else if((_step & 0x01) == 0x00) {
|
||||
_accumulator += _accumulatorRate;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t GetVolume()
|
||||
{
|
||||
if(!_enabled) {
|
||||
return 0;
|
||||
} else {
|
||||
//"The high 5 bits of the accumulator are then output (provided the channel is enabled by having the E bit set)."
|
||||
return _accumulator >> 3;
|
||||
}
|
||||
}
|
||||
};
|
@ -2,7 +2,7 @@
|
||||
#include "Snapshotable.h"
|
||||
#include "CPU.h"
|
||||
|
||||
class VrcIrq : Snapshotable
|
||||
class VrcIrq : public Snapshotable
|
||||
{
|
||||
private:
|
||||
uint8_t _irqReloadValue;
|
||||
|
@ -62,11 +62,11 @@ namespace Mesen.GUI.Config
|
||||
InteropEmu.SetChannelVolume(AudioChannel.Noise, ConvertVolume(audioInfo.NoiseVolume));
|
||||
InteropEmu.SetChannelVolume(AudioChannel.DMC, ConvertVolume(audioInfo.DmcVolume));
|
||||
InteropEmu.SetChannelVolume(AudioChannel.FDS, ConvertVolume(audioInfo.FdsVolume));
|
||||
InteropEmu.SetChannelVolume(AudioChannel.MMC5, ConvertVolume(audioInfo.FdsVolume));
|
||||
InteropEmu.SetChannelVolume(AudioChannel.VRC6, ConvertVolume(audioInfo.FdsVolume));
|
||||
InteropEmu.SetChannelVolume(AudioChannel.VRC7, ConvertVolume(audioInfo.FdsVolume));
|
||||
InteropEmu.SetChannelVolume(AudioChannel.Namco163, ConvertVolume(audioInfo.FdsVolume));
|
||||
InteropEmu.SetChannelVolume(AudioChannel.Sunsoft5B, ConvertVolume(audioInfo.FdsVolume));
|
||||
InteropEmu.SetChannelVolume(AudioChannel.MMC5, ConvertVolume(audioInfo.Mmc5Volume));
|
||||
InteropEmu.SetChannelVolume(AudioChannel.VRC6, ConvertVolume(audioInfo.Vrc6Volume));
|
||||
InteropEmu.SetChannelVolume(AudioChannel.VRC7, ConvertVolume(audioInfo.Vrc7Volume));
|
||||
InteropEmu.SetChannelVolume(AudioChannel.Namco163, ConvertVolume(audioInfo.Namco163Volume));
|
||||
InteropEmu.SetChannelVolume(AudioChannel.Sunsoft5B, ConvertVolume(audioInfo.Sunsoft5bVolume));
|
||||
InteropEmu.SetSampleRate(audioInfo.SampleRate);
|
||||
|
||||
InteropEmu.SetFlag(EmulationFlags.MuteSoundInBackground, audioInfo.MuteSoundInBackground);
|
||||
|
1
GUI.NET/Forms/Config/frmAudioConfig.Designer.cs
generated
1
GUI.NET/Forms/Config/frmAudioConfig.Designer.cs
generated
@ -269,7 +269,6 @@
|
||||
// trkVrc6Vol
|
||||
//
|
||||
this.trkVrc6Vol.Anchor = System.Windows.Forms.AnchorStyles.Top;
|
||||
this.trkVrc6Vol.Enabled = false;
|
||||
this.trkVrc6Vol.Location = new System.Drawing.Point(156, 160);
|
||||
this.trkVrc6Vol.Margin = new System.Windows.Forms.Padding(0);
|
||||
this.trkVrc6Vol.Maximum = 100;
|
||||
|
Loading…
Reference in New Issue
Block a user