mirror of
https://github.com/Phantop/LADXHD.git
synced 2024-11-01 04:14:22 +00:00
776 lines
27 KiB
C#
776 lines
27 KiB
C#
|
using System;
|
|||
|
using Microsoft.Xna.Framework.Audio;
|
|||
|
|
|||
|
namespace GbsPlayer
|
|||
|
{
|
|||
|
public class Sound
|
|||
|
{
|
|||
|
public CDynamicEffectInstance _soundOutput;
|
|||
|
|
|||
|
public bool WasStopped;
|
|||
|
private int _endBufferCount;
|
|||
|
private int _bufferCount;
|
|||
|
|
|||
|
private const int OutputRate = 44100;
|
|||
|
//private const int OutputRate = 48000;
|
|||
|
|
|||
|
private byte[] _soundBuffer = new byte[(OutputRate / 100) * 2]; // 100 buffers per second
|
|||
|
private int _bufferIndex;
|
|||
|
|
|||
|
// FF10-FF3F
|
|||
|
private byte[] _soundRegister = new byte[0x30];
|
|||
|
|
|||
|
// frame sequencer
|
|||
|
private int _frameSequencerTimer;
|
|||
|
private byte _frameSequencerCounter;
|
|||
|
|
|||
|
// Square 1
|
|||
|
private byte _modeOneNumberOfSweepShifts;
|
|||
|
private bool _modeOneSweepSubtraction;
|
|||
|
private byte _modeOneSweepTime;
|
|||
|
private byte _modeOneSweepCounter;
|
|||
|
|
|||
|
private float _modeOneWavePatternDutyPercentage;
|
|||
|
|
|||
|
private int _square1LengthLoad;
|
|||
|
private byte _square1LengthCounter;
|
|||
|
|
|||
|
// envelope
|
|||
|
private byte _square1StartVolume;
|
|||
|
private bool _square1EnvelopeAddMode;
|
|||
|
private byte _square1EnvelopePeriod;
|
|||
|
private int _square1EnvelopeCounter;
|
|||
|
private int _square1Volume;
|
|||
|
|
|||
|
private short _square1Frequency;
|
|||
|
private double _modeOneFreqCounter;
|
|||
|
private double _modeOneFreqTime;
|
|||
|
private bool _modeOneFreqDuty;
|
|||
|
private byte _square1WaveCounter;
|
|||
|
|
|||
|
private bool _modeOneCounterEnable;
|
|||
|
|
|||
|
private bool _square1Running;
|
|||
|
private int _waveOne = 1;
|
|||
|
|
|||
|
|
|||
|
// Square 2
|
|||
|
private float _modeTwoWavePatternDutyPercentage;
|
|||
|
|
|||
|
private byte _square2StartVolume;
|
|||
|
private bool _square2EnvelopeAddMode;
|
|||
|
private byte _square2EnvelopePeriod;
|
|||
|
private int _square2EnvelopeCounter;
|
|||
|
private int _square2Volume;
|
|||
|
|
|||
|
private byte _square2LengthCounter;
|
|||
|
private int _modeTwoSoundLength;
|
|||
|
|
|||
|
private short _square2Frequency;
|
|||
|
private double _modeTwoFreqTime;
|
|||
|
private double _modeTwoFreqCounter;
|
|||
|
private byte _square2WaveCounter;
|
|||
|
|
|||
|
private bool _modeTwoCounterEnable;
|
|||
|
|
|||
|
private bool _square2Running;
|
|||
|
private int _waveTwo = 1;
|
|||
|
|
|||
|
|
|||
|
// Wave
|
|||
|
private bool _waveDACpower;
|
|||
|
|
|||
|
private byte _waveLengthCounter;
|
|||
|
private int _modeThreeSoundLength;
|
|||
|
|
|||
|
private byte _waveVolumeCode;
|
|||
|
|
|||
|
private short _modeThreeFrequency;
|
|||
|
|
|||
|
private double _modeThreeFreqCounter;
|
|||
|
private double _modeThreeFreqTime;
|
|||
|
|
|||
|
private bool _waveTrigger;
|
|||
|
private bool _waveLengthEnable;
|
|||
|
|
|||
|
private byte[] _waveNibbles = new byte[32];
|
|||
|
private int _waveIndex;
|
|||
|
|
|||
|
private short _waveThree;
|
|||
|
|
|||
|
// Noise
|
|||
|
private byte _noiseLengthLoad;
|
|||
|
private byte _noiseVolume;
|
|||
|
private byte _noiseInitVolume;
|
|||
|
private bool _noiseEnvelopeAddMode;
|
|||
|
private byte _noiseEnvelopePeriod;
|
|||
|
|
|||
|
private byte _noiseClockShift;
|
|||
|
private bool _noiseWidthMode;
|
|||
|
private byte _noiseDivisorCode;
|
|||
|
|
|||
|
private bool _noiseTrigger;
|
|||
|
private bool _noiseLengthEnable;
|
|||
|
|
|||
|
private short _noiseLfsr;
|
|||
|
|
|||
|
private double _noiseTimeSteps;
|
|||
|
|
|||
|
private int _noiseEnvelopeCounter;
|
|||
|
|
|||
|
private byte _noiseLengthCounter;
|
|||
|
|
|||
|
// FF24
|
|||
|
private byte _soundOutputLevel;
|
|||
|
// FF25
|
|||
|
private byte _channelLeftRightControl;
|
|||
|
// FF26
|
|||
|
private byte _soundOnOff;
|
|||
|
|
|||
|
private double _highPassFilterCapacitor;
|
|||
|
|
|||
|
public int DebugCounter;
|
|||
|
|
|||
|
public Sound()
|
|||
|
{
|
|||
|
_soundOutput = new CDynamicEffectInstance(OutputRate);
|
|||
|
}
|
|||
|
|
|||
|
public void Init()
|
|||
|
{
|
|||
|
_frameSequencerTimer = 0;
|
|||
|
_frameSequencerCounter = 0;
|
|||
|
|
|||
|
_square1LengthCounter = 0;
|
|||
|
_square2LengthCounter = 0;
|
|||
|
_waveLengthCounter = 0;
|
|||
|
_noiseLengthCounter = 0;
|
|||
|
|
|||
|
_modeOneFreqCounter = 0;
|
|||
|
_modeTwoFreqCounter = 0;
|
|||
|
_modeThreeFreqCounter = 0;
|
|||
|
|
|||
|
_square1EnvelopeCounter = 0;
|
|||
|
_square2EnvelopeCounter = 0;
|
|||
|
_noiseEnvelopeCounter = 0;
|
|||
|
|
|||
|
_highPassFilterCapacitor = 0;
|
|||
|
|
|||
|
_bufferIndex = 0;
|
|||
|
WasStopped = false;
|
|||
|
}
|
|||
|
|
|||
|
public bool IsPlaying()
|
|||
|
{
|
|||
|
return _soundOutput.State == SoundState.Playing;
|
|||
|
}
|
|||
|
|
|||
|
public bool FinishedPlaying()
|
|||
|
{
|
|||
|
return _soundOutput.GetPendingBufferCount() == 0;
|
|||
|
}
|
|||
|
|
|||
|
public void SetStopTime(float length)
|
|||
|
{
|
|||
|
_endBufferCount = (int)(length * OutputRate);
|
|||
|
}
|
|||
|
|
|||
|
public void Play()
|
|||
|
{
|
|||
|
_soundOutput.Play();
|
|||
|
}
|
|||
|
|
|||
|
public void Pause()
|
|||
|
{
|
|||
|
_soundOutput.Pause();
|
|||
|
}
|
|||
|
|
|||
|
public void Resume()
|
|||
|
{
|
|||
|
_soundOutput.Resume();
|
|||
|
}
|
|||
|
|
|||
|
public void Stop()
|
|||
|
{
|
|||
|
_bufferCount = 0;
|
|||
|
_bufferIndex = 0;
|
|||
|
_soundOutput.Stop();
|
|||
|
}
|
|||
|
|
|||
|
public void SetVolume(float volume)
|
|||
|
{
|
|||
|
_soundOutput.SetVolume(volume);
|
|||
|
}
|
|||
|
|
|||
|
public void AddCurrentBuffer()
|
|||
|
{
|
|||
|
_soundOutput.SubmitBuffer(_soundBuffer, 0, _bufferIndex);
|
|||
|
_bufferIndex = 0;
|
|||
|
}
|
|||
|
|
|||
|
public byte this[int index]
|
|||
|
{
|
|||
|
get
|
|||
|
{
|
|||
|
Console.WriteLine("Get Index 0x{0:X}", index);
|
|||
|
|
|||
|
// Square 1
|
|||
|
if (index == 0xFF10)
|
|||
|
return _soundRegister[index - 0xFF10];
|
|||
|
if (index == 0xFF11)
|
|||
|
return (byte)(_soundRegister[index - 0xFF10] & 0xC0);
|
|||
|
if (index == 0xFF12)
|
|||
|
return _soundRegister[index - 0xFF10];
|
|||
|
if (index == 0xFF13)
|
|||
|
return 0;
|
|||
|
if (index == 0xFF14)
|
|||
|
return _soundRegister[index - 0xFF10];
|
|||
|
|
|||
|
// Square 2
|
|||
|
if (index == 0xFF15) // not used
|
|||
|
return 0;
|
|||
|
if (index == 0xFF16)
|
|||
|
return (byte)(_soundRegister[index - 0xFF10] & 0xC0);
|
|||
|
if (index == 0xFF17)
|
|||
|
return _soundRegister[index - 0xFF10];
|
|||
|
if (index == 0xFF18)
|
|||
|
return 0;
|
|||
|
if (index == 0xFF19)
|
|||
|
return _soundRegister[index - 0xFF10];
|
|||
|
|
|||
|
// Wave
|
|||
|
if (index == 0xFF1A)
|
|||
|
return (byte)(_waveDACpower ? 0x80 : 0x00);
|
|||
|
if (index == 0xFF1B)
|
|||
|
return 0;
|
|||
|
if (index == 0xFF1C)
|
|||
|
return (byte)(_waveVolumeCode << 5);
|
|||
|
if (index == 0xFF1D)
|
|||
|
return 0;
|
|||
|
if (index == 0xFF1E)
|
|||
|
return (byte)(_waveLengthEnable ? 0x40 : 0x00);
|
|||
|
|
|||
|
// Noise
|
|||
|
if (0xFF1F <= index && index <= 0xFF23)
|
|||
|
{
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
// Stuff
|
|||
|
if (index == 0xFF24)
|
|||
|
return _soundOutputLevel;
|
|||
|
if (index == 0xFF25)
|
|||
|
return _channelLeftRightControl;
|
|||
|
if (index == 0xFF26)
|
|||
|
return _soundOnOff;
|
|||
|
|
|||
|
return _soundRegister[index - 0xFF10];
|
|||
|
}
|
|||
|
set
|
|||
|
{
|
|||
|
// Square 1
|
|||
|
if (index == 0xFF10)
|
|||
|
{
|
|||
|
_soundRegister[index - 0xFF10] = (byte)(value & 0x7F);
|
|||
|
|
|||
|
_modeOneNumberOfSweepShifts = (byte)(value & 0x07);
|
|||
|
_modeOneSweepSubtraction = (value & 0x08) == 0x08;
|
|||
|
_modeOneSweepTime = (byte)((value & 0x70) >> 4);
|
|||
|
|
|||
|
_modeOneSweepCounter = 0;
|
|||
|
}
|
|||
|
else if (index == 0xFF11)
|
|||
|
{
|
|||
|
_soundRegister[index - 0xFF10] = (byte)(value & 0xC7);
|
|||
|
|
|||
|
// Sound Length = (64-t1) * (1/256) sec
|
|||
|
_square1LengthLoad = 64 - (byte)(value & 0x3F); // was 0x07 before?
|
|||
|
|
|||
|
// 12.5, 25, 50, 75
|
|||
|
var waveDuty = (byte)(value >> 6);
|
|||
|
_modeOneWavePatternDutyPercentage = waveDuty == 0 ? 1 : (waveDuty * 2);
|
|||
|
}
|
|||
|
else if (index == 0xFF12)
|
|||
|
{
|
|||
|
_soundRegister[index - 0xFF10] = value;
|
|||
|
|
|||
|
_square1StartVolume = (byte)(value >> 4);
|
|||
|
_square1EnvelopeAddMode = (value & 0x08) == 0x08;
|
|||
|
_square1EnvelopePeriod = (byte)(value & 0x07);
|
|||
|
}
|
|||
|
else if (index == 0xFF13)
|
|||
|
{
|
|||
|
_soundRegister[index - 0xFF10] = value;
|
|||
|
|
|||
|
_square1Frequency = (short)((_square1Frequency & 0x700) + value); // this was 0xF00 before
|
|||
|
UpdateSquare1FrequencyTime();
|
|||
|
}
|
|||
|
else if (index == 0xFF14)
|
|||
|
{
|
|||
|
_soundRegister[index - 0xFF10] = (byte)(value & 0x40);
|
|||
|
|
|||
|
_square1Frequency = (short)(((value & 0x07) << 8) | (_square1Frequency & 0xFF));
|
|||
|
UpdateSquare1FrequencyTime();
|
|||
|
|
|||
|
_modeOneCounterEnable = (value & 0x40) == 0x40;
|
|||
|
|
|||
|
if ((value & 0x80) == 0x80)
|
|||
|
{
|
|||
|
_square1Running = true;
|
|||
|
|
|||
|
_soundOnOff |= 0x01;
|
|||
|
|
|||
|
_square1LengthCounter = 0;
|
|||
|
|
|||
|
// set to initial value
|
|||
|
_square1Volume = _square1StartVolume;
|
|||
|
// set the envelope counter to the period
|
|||
|
_square1EnvelopeCounter = _square1EnvelopePeriod;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
// Square 2
|
|||
|
else if (index == 0xFF15) { } // not used
|
|||
|
else if (index == 0xFF16)
|
|||
|
{
|
|||
|
_soundRegister[index - 0xFF10] = (byte)(value & 0xC7);
|
|||
|
|
|||
|
// Sound Length = (64-t1) * (1/256) sec
|
|||
|
_modeTwoSoundLength = 64 - (byte)(value & 0x3F);
|
|||
|
|
|||
|
// 12.5, 25, 50, 75
|
|||
|
var waveDuty = (byte)(value >> 6);
|
|||
|
_modeTwoWavePatternDutyPercentage = waveDuty == 0 ? 1 : (waveDuty * 2);
|
|||
|
}
|
|||
|
else if (index == 0xFF17)
|
|||
|
{
|
|||
|
_soundRegister[index - 0xFF10] = value;
|
|||
|
|
|||
|
_square2StartVolume = (byte)(value >> 4);
|
|||
|
_square2EnvelopeAddMode = (value & 0x08) == 0x08;
|
|||
|
_square2EnvelopePeriod = (byte)(value & 0x07);
|
|||
|
}
|
|||
|
else if (index == 0xFF18)
|
|||
|
{
|
|||
|
_soundRegister[index - 0xFF10] = value;
|
|||
|
|
|||
|
_square2Frequency = (short)((_square2Frequency & 0x700) + value); // this was 0xF00 before
|
|||
|
UpdateSquare2FrequencyTime();
|
|||
|
}
|
|||
|
else if (index == 0xFF19)
|
|||
|
{
|
|||
|
_soundRegister[index - 0xFF10] = (byte)(value & 0x40);
|
|||
|
|
|||
|
_square2Frequency = (short)(((value & 0x07) << 8) + (_square2Frequency & 0xFF));
|
|||
|
UpdateSquare2FrequencyTime();
|
|||
|
|
|||
|
_modeTwoCounterEnable = (value & 0x40) == 0x40;
|
|||
|
|
|||
|
if ((value & 0x80) == 0x80)
|
|||
|
{
|
|||
|
_square2Running = true;
|
|||
|
|
|||
|
_soundOnOff |= 0x02;
|
|||
|
|
|||
|
_square2LengthCounter = 0;
|
|||
|
|
|||
|
// set to initial value
|
|||
|
_square2Volume = _square2StartVolume;
|
|||
|
// set the envelope counter to the period
|
|||
|
_square2EnvelopeCounter = _square2EnvelopePeriod;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
// Wave
|
|||
|
else if (index == 0xFF1A)
|
|||
|
{
|
|||
|
_waveDACpower = (value & 0x80) == 0x80;
|
|||
|
// When the sound OFF flag(bit 7 of NR30) is reset to "0", cancellation of the OFF mode must be performed by setting the sound OFF flag to a "1".This is performed by Sound 3.
|
|||
|
}
|
|||
|
else if (index == 0xFF1B)
|
|||
|
{
|
|||
|
// (256-t1) * (1/256) sec
|
|||
|
_modeThreeSoundLength = 256 - value;
|
|||
|
}
|
|||
|
else if (index == 0xFF1C)
|
|||
|
{
|
|||
|
_waveVolumeCode = (byte)((value >> 5) & 0x03);
|
|||
|
|
|||
|
if (_waveVolumeCode == 0x00)
|
|||
|
{
|
|||
|
_modeThreeFreqCounter = 0;
|
|||
|
}
|
|||
|
}
|
|||
|
else if (index == 0xFF1D)
|
|||
|
{
|
|||
|
_modeThreeFrequency = (short)((_modeThreeFrequency & 0x700) + value);
|
|||
|
UpdateWaveFrequencyTime();
|
|||
|
|
|||
|
_modeThreeFreqCounter = 0;
|
|||
|
}
|
|||
|
else if (index == 0xFF1E)
|
|||
|
{
|
|||
|
_modeThreeFrequency = (short)((_modeThreeFrequency & 0xFF) + ((value & 0x07) << 8));
|
|||
|
UpdateWaveFrequencyTime();
|
|||
|
|
|||
|
_waveTrigger = (value & 0x80) == 0x80;
|
|||
|
_waveLengthEnable = (value & 0x40) == 0x40;
|
|||
|
|
|||
|
// start sound 3 again
|
|||
|
if (_waveDACpower && _waveTrigger)
|
|||
|
{
|
|||
|
_soundOnOff |= 0x04;
|
|||
|
|
|||
|
_waveLengthCounter = 0;
|
|||
|
_modeThreeFreqCounter = 0;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
// Noise
|
|||
|
else if (index == 0xFF1F) { } // not used
|
|||
|
else if (index == 0xFF20)
|
|||
|
{
|
|||
|
_noiseLengthLoad = (byte)(64 - (value & 0x3F));
|
|||
|
}
|
|||
|
else if (index == 0xFF21)
|
|||
|
{
|
|||
|
_noiseInitVolume = (byte)(value >> 4);
|
|||
|
_noiseEnvelopeAddMode = (value & 0x08) == 0x08;
|
|||
|
_noiseEnvelopePeriod = (byte)(value & 0x07);
|
|||
|
}
|
|||
|
else if (index == 0xFF22)
|
|||
|
{
|
|||
|
_noiseClockShift = (byte)(value >> 4);
|
|||
|
_noiseWidthMode = (value & 0x08) == 0x08;
|
|||
|
_noiseDivisorCode = (byte)(value & 0x07);
|
|||
|
}
|
|||
|
else if (index == 0xFF23)
|
|||
|
{
|
|||
|
_noiseTrigger = (value & 0x80) == 0x80;
|
|||
|
_noiseLengthEnable = (value & 0x40) == 0x40;
|
|||
|
|
|||
|
// turn channel on/off
|
|||
|
if (_noiseTrigger)
|
|||
|
{
|
|||
|
_soundOnOff |= 0x08;
|
|||
|
|
|||
|
_noiseLengthCounter = 0;
|
|||
|
|
|||
|
_noiseVolume = _noiseInitVolume;
|
|||
|
|
|||
|
_noiseEnvelopeCounter = _noiseEnvelopePeriod;
|
|||
|
|
|||
|
if (_noiseWidthMode)
|
|||
|
_noiseLfsr = 0x7F;
|
|||
|
else
|
|||
|
_noiseLfsr = 0x7FFF;
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
_soundOnOff &= 0xF7;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
else if (index == 0xFF24)
|
|||
|
{
|
|||
|
_soundOutputLevel = value;
|
|||
|
if (value != 0xFF)
|
|||
|
Console.WriteLine("Volume not supported {0:X}", value);
|
|||
|
}
|
|||
|
else if (index == 0xFF25)
|
|||
|
_channelLeftRightControl = value;
|
|||
|
// NR52
|
|||
|
else if (index == 0xFF26)
|
|||
|
_soundOnOff = value;
|
|||
|
|
|||
|
// wave data
|
|||
|
else if (0xFF30 <= index && index <= 0xFF3F)
|
|||
|
{
|
|||
|
_soundRegister[index - 0xFF10] = value;
|
|||
|
|
|||
|
_waveNibbles[(index - 0xFF30) * 2] = (byte)(value >> 4);
|
|||
|
_waveNibbles[(index - 0xFF30) * 2 + 1] = (byte)(value & 0x0F);
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
private void UpdateSquare1FrequencyTime()
|
|||
|
{
|
|||
|
_modeOneFreqTime = OutputRate / (4194304.0 / (4 * (2048 - _square1Frequency)));
|
|||
|
}
|
|||
|
|
|||
|
private void UpdateSquare2FrequencyTime()
|
|||
|
{
|
|||
|
_modeTwoFreqTime = OutputRate / (4194304.0 / (4 * (2048 - _square2Frequency)));
|
|||
|
}
|
|||
|
|
|||
|
private void UpdateWaveFrequencyTime()
|
|||
|
{
|
|||
|
_modeThreeFreqTime = OutputRate / (4194304.0 / (2 * (2048 - _modeThreeFrequency)));
|
|||
|
}
|
|||
|
|
|||
|
// gets called 44100 (outputRate) times a second
|
|||
|
public void UpdateBuffer()
|
|||
|
{
|
|||
|
DebugCounter++;
|
|||
|
_frameSequencerTimer++;
|
|||
|
|
|||
|
// 44100/512 = 86
|
|||
|
if (_frameSequencerTimer >= 86)
|
|||
|
{
|
|||
|
_frameSequencerTimer = 0;
|
|||
|
|
|||
|
// 256Hz Length Ctr Clock
|
|||
|
if (_frameSequencerCounter % 2 == 0)
|
|||
|
{
|
|||
|
_square1LengthCounter++;
|
|||
|
_square2LengthCounter++;
|
|||
|
_waveLengthCounter++;
|
|||
|
_noiseLengthCounter++;
|
|||
|
|
|||
|
// deactivate channel 1
|
|||
|
if (_square1LengthCounter >= _square1LengthLoad && _modeOneCounterEnable)
|
|||
|
_soundOnOff &= 0xFE;
|
|||
|
|
|||
|
// deactivate channel 2
|
|||
|
if (_square2LengthCounter >= _modeTwoSoundLength && _modeTwoCounterEnable)
|
|||
|
_soundOnOff &= 0xFD;
|
|||
|
|
|||
|
// deactivate channel 3
|
|||
|
if (_waveLengthCounter >= _modeThreeSoundLength && _waveLengthEnable)
|
|||
|
{
|
|||
|
_waveTrigger = false;
|
|||
|
_soundOnOff &= 0xFB;
|
|||
|
}
|
|||
|
|
|||
|
if (_noiseLengthCounter >= _noiseLengthLoad && _noiseLengthEnable)
|
|||
|
{
|
|||
|
_soundOnOff &= 0xF7;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
// 64Hz Volume Envelope Clock
|
|||
|
if ((_frameSequencerCounter + 1) % 8 == 0)
|
|||
|
{
|
|||
|
// step channel 1 up/down
|
|||
|
_square1EnvelopeCounter--;
|
|||
|
if (_square1EnvelopePeriod != 0 && _square1EnvelopeCounter <= 0 &&
|
|||
|
(!_square1EnvelopeAddMode && _square1Volume > 0 || _square1EnvelopeAddMode && _square1Volume < 15))
|
|||
|
{
|
|||
|
_square1EnvelopeCounter = _square1EnvelopePeriod;
|
|||
|
_square1Volume += _square1EnvelopeAddMode ? 1 : -1;
|
|||
|
}
|
|||
|
|
|||
|
// step channel 2 up/down
|
|||
|
_square2EnvelopeCounter--;
|
|||
|
if (_square2EnvelopePeriod != 0 && _square2EnvelopeCounter <= 0 &&
|
|||
|
(!_square2EnvelopeAddMode && _square2Volume > 0 || _square2EnvelopeAddMode && _square2Volume < 15))
|
|||
|
{
|
|||
|
_square2EnvelopeCounter = _square2EnvelopePeriod;
|
|||
|
_square2Volume += _square2EnvelopeAddMode ? 1 : -1;
|
|||
|
}
|
|||
|
|
|||
|
// step channel 4
|
|||
|
_noiseEnvelopeCounter--;
|
|||
|
if (_noiseEnvelopePeriod != 0 && _noiseEnvelopeCounter <= 0 &&
|
|||
|
(!_noiseEnvelopeAddMode && _noiseVolume > 0 || _noiseEnvelopeAddMode && _noiseVolume < 15))
|
|||
|
{
|
|||
|
_noiseEnvelopeCounter = _noiseEnvelopePeriod;
|
|||
|
_noiseVolume += (byte)(_noiseEnvelopeAddMode ? 1 : -1);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
// 128Hz Sweep Clock
|
|||
|
if ((_frameSequencerCounter + 2) % 4 == 0)
|
|||
|
{
|
|||
|
_modeOneSweepCounter++;
|
|||
|
|
|||
|
// sweep
|
|||
|
if (_modeOneSweepTime != 0 && _modeOneNumberOfSweepShifts != 0 && _modeOneSweepCounter >= _modeOneSweepTime)
|
|||
|
{
|
|||
|
_modeOneSweepCounter = 0;
|
|||
|
var newFreq = (short)(_square1Frequency + (_modeOneSweepSubtraction ? -1 : 1) * (_square1Frequency >> _modeOneNumberOfSweepShifts));
|
|||
|
|
|||
|
// newFreq > 11bits
|
|||
|
if ((newFreq & 0x7FF) != newFreq)
|
|||
|
{
|
|||
|
// deactivate channel
|
|||
|
_soundOnOff &= 0xFE;
|
|||
|
}
|
|||
|
else if (newFreq <= 0)
|
|||
|
{
|
|||
|
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
_square1Frequency = newFreq;
|
|||
|
}
|
|||
|
|
|||
|
_soundRegister[0xFF13 - 0xFF10] = (byte)(_square1Frequency & 0xFF);
|
|||
|
_soundRegister[0xFF14 - 0xFF10] = (byte)(_soundRegister[0xFF14 - 0xFF10] & 0xF8 + (_square1Frequency >> 8));
|
|||
|
|
|||
|
UpdateSquare1FrequencyTime();
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
_frameSequencerCounter++;
|
|||
|
if (_frameSequencerCounter >= 8)
|
|||
|
_frameSequencerCounter = 0;
|
|||
|
}
|
|||
|
|
|||
|
// Square 1
|
|||
|
short channel0 = 0;
|
|||
|
{
|
|||
|
if (_square1Running)
|
|||
|
{
|
|||
|
_modeOneFreqCounter++;
|
|||
|
|
|||
|
if (_modeOneFreqCounter >= _modeOneFreqTime)
|
|||
|
{
|
|||
|
_modeOneFreqCounter -= _modeOneFreqTime;
|
|||
|
|
|||
|
// update wave
|
|||
|
_waveOne = _square1WaveCounter < _modeOneWavePatternDutyPercentage ? 1 : 0;
|
|||
|
_square1WaveCounter = (byte)((_square1WaveCounter + 1) % 8);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
// value between 0-15
|
|||
|
channel0 = (short)(_waveOne * _square1Volume);
|
|||
|
|
|||
|
// Sound 1 ON Flag
|
|||
|
if ((_soundOnOff & 0x01) == 0x00)
|
|||
|
channel0 = 0;
|
|||
|
}
|
|||
|
|
|||
|
// Square 2
|
|||
|
short channel1 = 0;
|
|||
|
{
|
|||
|
if (_square2Running)
|
|||
|
{
|
|||
|
_modeTwoFreqCounter++;
|
|||
|
|
|||
|
if (_modeTwoFreqCounter >= _modeTwoFreqTime)
|
|||
|
{
|
|||
|
_modeTwoFreqCounter -= _modeTwoFreqTime;
|
|||
|
|
|||
|
// update wave
|
|||
|
_waveTwo = _square2WaveCounter < _modeTwoWavePatternDutyPercentage ? 1 : 0;
|
|||
|
_square2WaveCounter = (byte)((_square2WaveCounter + 1) % 8);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
channel1 = (short)(_waveTwo * _square2Volume);
|
|||
|
|
|||
|
// Sound 2 ON Flag
|
|||
|
if ((_soundOnOff & 0x02) == 0x00)
|
|||
|
channel1 = 0;
|
|||
|
}
|
|||
|
|
|||
|
// Wave
|
|||
|
double channel2 = 0;
|
|||
|
{
|
|||
|
_modeThreeFreqCounter++;
|
|||
|
if (_modeThreeFreqCounter >= _modeThreeFreqTime)
|
|||
|
{
|
|||
|
_modeThreeFreqCounter -= _modeThreeFreqTime;
|
|||
|
_waveIndex = (_waveIndex + 1) % 32;
|
|||
|
|
|||
|
// 32 x 4 bit samples
|
|||
|
_waveThree = (byte)(_waveNibbles[_waveIndex] >> (_waveVolumeCode - 1));
|
|||
|
}
|
|||
|
|
|||
|
channel2 = _waveThree;
|
|||
|
|
|||
|
// Sound 3 ON Flag
|
|||
|
if ((_soundOnOff & 0x04) == 0x00 || _waveVolumeCode == 0x00 || !_waveDACpower)
|
|||
|
channel2 = 0;
|
|||
|
}
|
|||
|
|
|||
|
// Noise Channel
|
|||
|
double channel3 = 0;
|
|||
|
{
|
|||
|
channel3 = 0;
|
|||
|
if ((_soundOnOff & 0x08) != 0x00)
|
|||
|
{
|
|||
|
channel3 = (double)((~_noiseLfsr & 0x01) * _noiseVolume);
|
|||
|
|
|||
|
var s = _noiseClockShift;
|
|||
|
var r = _noiseDivisorCode == 0 ? 0.5 : _noiseDivisorCode;
|
|||
|
var divisor = (262144 / r / Math.Pow(2, s));
|
|||
|
var freq = 4194304 / divisor;
|
|||
|
|
|||
|
var stepSize = 4194304 / OutputRate;
|
|||
|
_noiseTimeSteps += stepSize;
|
|||
|
|
|||
|
var stepCount = (int)(_noiseTimeSteps / freq);
|
|||
|
if (stepCount > 1)
|
|||
|
channel3 /= (double)stepCount;
|
|||
|
|
|||
|
while (_noiseTimeSteps >= freq)
|
|||
|
{
|
|||
|
_noiseTimeSteps -= freq;
|
|||
|
|
|||
|
var lsb = (short)((_noiseLfsr & 0x01) ^ ((_noiseLfsr >> 1) & 0x01));
|
|||
|
|
|||
|
_noiseLfsr >>= 1;
|
|||
|
|
|||
|
_noiseLfsr |= (short)(lsb << 14);
|
|||
|
|
|||
|
if (_noiseWidthMode)
|
|||
|
_noiseLfsr |= (short)(lsb << 6);
|
|||
|
|
|||
|
if (_noiseTimeSteps > freq)
|
|||
|
channel3 += (double)((~_noiseLfsr & 0x01) * _noiseVolume) / (double)stepCount;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
//channel0 = 0;
|
|||
|
//channel1 = 0;
|
|||
|
//channel2 = 0;
|
|||
|
//channel3 = 0;
|
|||
|
|
|||
|
double mixerOutput = ((channel0 / 15.0) +
|
|||
|
(channel1 / 15.0) +
|
|||
|
(channel2 / 15.0) +
|
|||
|
(channel3 / 15.0)) / 4;
|
|||
|
|
|||
|
if ((_soundOnOff & 0x80) != 0x80)
|
|||
|
mixerOutput = 0;
|
|||
|
|
|||
|
var output = HighPass(mixerOutput, (_soundOnOff & 0x0F) != 0x00);
|
|||
|
var byteOutput = (short)(output * short.MaxValue);
|
|||
|
|
|||
|
_soundBuffer[_bufferIndex++] = (byte)(byteOutput & 0xFF);
|
|||
|
_soundBuffer[_bufferIndex++] = (byte)(byteOutput >> 8);
|
|||
|
|
|||
|
// this can happen when _bufferIndex get reset while in the process of setting the _soundBuffer
|
|||
|
if (_bufferIndex % 2 != 0)
|
|||
|
_bufferIndex = 0;
|
|||
|
|
|||
|
_bufferCount++;
|
|||
|
if (_endBufferCount > 0 && _bufferCount > _endBufferCount)
|
|||
|
WasStopped = true;
|
|||
|
|
|||
|
if (_bufferIndex >= _soundBuffer.Length || (WasStopped && _bufferIndex > 0))
|
|||
|
AddCurrentBuffer();
|
|||
|
}
|
|||
|
|
|||
|
private double HighPass(double input, bool dacsEnabled)
|
|||
|
{
|
|||
|
if (!dacsEnabled)
|
|||
|
return 0.0;
|
|||
|
|
|||
|
var output = input - _highPassFilterCapacitor;
|
|||
|
|
|||
|
// 0,999958 ^ (4194304/rate)
|
|||
|
// 0,999958 ^ (4194304/44100hz) = 0,996013308910
|
|||
|
// 0,999958 ^ (4194304/48000hz) = 0,996336633487
|
|||
|
_highPassFilterCapacitor = input - output * 0.996013308910; // for 44100hz
|
|||
|
|
|||
|
return output;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|