2012-11-27 05:11:40 +00:00
|
|
|
|
using System;
|
2013-11-14 19:33:13 +00:00
|
|
|
|
|
2013-11-04 00:36:15 +00:00
|
|
|
|
using BizHawk.Common;
|
2012-11-27 05:11:40 +00:00
|
|
|
|
|
2014-10-03 21:04:37 +00:00
|
|
|
|
namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS
|
2012-11-27 05:11:40 +00:00
|
|
|
|
{
|
2016-02-22 23:50:11 +00:00
|
|
|
|
public sealed partial class Sid
|
2012-11-27 05:11:40 +00:00
|
|
|
|
{
|
2017-04-24 13:35:05 +00:00
|
|
|
|
/*
|
2016-02-22 23:50:11 +00:00
|
|
|
|
Commodore SID 6581/8580 core.
|
|
|
|
|
|
|
|
|
|
Many thanks to:
|
|
|
|
|
- Michael Huth for die shots of the 6569R3 chip (to get ideas how to implement)
|
|
|
|
|
http://mail.lipsia.de/~enigma/neu/6581.html
|
|
|
|
|
- Kevtris for figuring out ADSR tables
|
|
|
|
|
http://blog.kevtris.org/?p=13
|
|
|
|
|
- Mixer for a lot of useful SID info
|
|
|
|
|
http://www.sid.fi/sidwiki/doku.php?id=sid-knowledge
|
|
|
|
|
- Documentation collected by the libsidplayfp team
|
|
|
|
|
https://sourceforge.net/projects/sidplay-residfp/
|
|
|
|
|
*/
|
|
|
|
|
|
2017-04-24 13:35:05 +00:00
|
|
|
|
// ------------------------------------
|
2017-05-16 23:58:06 +00:00
|
|
|
|
public int _databus;
|
2017-04-24 13:35:05 +00:00
|
|
|
|
private int _cachedCycles;
|
|
|
|
|
private bool _disableVoice3;
|
|
|
|
|
private int _envelopeOutput0;
|
|
|
|
|
private int _envelopeOutput1;
|
|
|
|
|
private int _envelopeOutput2;
|
|
|
|
|
private readonly Envelope[] _envelopes;
|
2017-05-13 18:04:02 +00:00
|
|
|
|
private readonly Envelope _envelope0;
|
|
|
|
|
private readonly Envelope _envelope1;
|
|
|
|
|
private readonly Envelope _envelope2;
|
2017-05-13 13:51:03 +00:00
|
|
|
|
private bool[] _filterEnable;
|
2017-04-24 13:35:05 +00:00
|
|
|
|
private int _filterFrequency;
|
|
|
|
|
private int _filterResonance;
|
|
|
|
|
private bool _filterSelectBandPass;
|
|
|
|
|
private bool _filterSelectLoPass;
|
|
|
|
|
private bool _filterSelectHiPass;
|
|
|
|
|
private int _mixer;
|
2017-05-13 18:04:02 +00:00
|
|
|
|
private readonly short[] _outputBuffer;
|
2017-05-22 20:44:27 +00:00
|
|
|
|
private int[] _outputBuffer_filtered;
|
|
|
|
|
private int[] _outputBuffer_not_filtered;
|
2017-05-13 18:04:02 +00:00
|
|
|
|
private int _outputBufferIndex;
|
2017-05-22 20:44:27 +00:00
|
|
|
|
private int filter_index;
|
2017-05-25 17:42:36 +00:00
|
|
|
|
private int last_filtered_value;
|
2017-04-24 13:35:05 +00:00
|
|
|
|
private int _potCounter;
|
|
|
|
|
private int _potX;
|
|
|
|
|
private int _potY;
|
2017-05-22 20:44:27 +00:00
|
|
|
|
private int _sample;
|
2017-04-24 13:35:05 +00:00
|
|
|
|
private int _voiceOutput0;
|
|
|
|
|
private int _voiceOutput1;
|
|
|
|
|
private int _voiceOutput2;
|
|
|
|
|
private readonly Voice[] _voices;
|
2017-05-13 18:04:02 +00:00
|
|
|
|
private readonly Voice _voice0;
|
|
|
|
|
private readonly Voice _voice1;
|
|
|
|
|
private readonly Voice _voice2;
|
2017-04-24 13:35:05 +00:00
|
|
|
|
private int _volume;
|
|
|
|
|
|
|
|
|
|
public Func<int> ReadPotX;
|
2016-02-22 23:50:11 +00:00
|
|
|
|
public Func<int> ReadPotY;
|
|
|
|
|
|
2017-05-22 20:44:27 +00:00
|
|
|
|
public RealFFT fft;
|
|
|
|
|
|
2017-05-13 18:04:02 +00:00
|
|
|
|
private readonly int _cpuCyclesNum;
|
|
|
|
|
private int _sampleCyclesNum;
|
|
|
|
|
private readonly int _sampleCyclesDen;
|
|
|
|
|
private readonly int _sampleRate;
|
2016-02-22 23:50:11 +00:00
|
|
|
|
|
2017-04-24 13:35:05 +00:00
|
|
|
|
public Sid(int[][] newWaveformTable, int sampleRate, int cyclesNum, int cyclesDen)
|
|
|
|
|
{
|
|
|
|
|
_sampleRate = sampleRate;
|
|
|
|
|
_cpuCyclesNum = cyclesNum;
|
|
|
|
|
_sampleCyclesDen = cyclesDen * sampleRate;
|
2016-02-22 23:50:11 +00:00
|
|
|
|
|
2017-04-24 13:35:05 +00:00
|
|
|
|
_envelopes = new Envelope[3];
|
2016-02-22 23:50:11 +00:00
|
|
|
|
for (var i = 0; i < 3; i++)
|
|
|
|
|
_envelopes[i] = new Envelope();
|
2017-04-24 13:35:05 +00:00
|
|
|
|
_envelope0 = _envelopes[0];
|
|
|
|
|
_envelope1 = _envelopes[1];
|
|
|
|
|
_envelope2 = _envelopes[2];
|
2016-02-22 23:50:11 +00:00
|
|
|
|
|
2017-04-24 13:35:05 +00:00
|
|
|
|
_voices = new Voice[3];
|
2016-02-22 23:50:11 +00:00
|
|
|
|
for (var i = 0; i < 3; i++)
|
|
|
|
|
_voices[i] = new Voice(newWaveformTable);
|
2017-04-24 13:35:05 +00:00
|
|
|
|
_voice0 = _voices[0];
|
|
|
|
|
_voice1 = _voices[1];
|
|
|
|
|
_voice2 = _voices[2];
|
2016-02-22 23:50:11 +00:00
|
|
|
|
|
2017-04-24 13:35:05 +00:00
|
|
|
|
_filterEnable = new bool[3];
|
2016-02-22 23:50:11 +00:00
|
|
|
|
for (var i = 0; i < 3; i++)
|
|
|
|
|
_filterEnable[i] = false;
|
|
|
|
|
|
2017-04-24 13:35:05 +00:00
|
|
|
|
_outputBuffer = new short[sampleRate];
|
2017-05-22 20:44:27 +00:00
|
|
|
|
_outputBuffer_filtered = new int[sampleRate];
|
|
|
|
|
_outputBuffer_not_filtered = new int[sampleRate];
|
2012-12-10 22:51:02 +00:00
|
|
|
|
}
|
|
|
|
|
|
2012-11-27 05:11:40 +00:00
|
|
|
|
// ------------------------------------
|
|
|
|
|
|
|
|
|
|
public void HardReset()
|
|
|
|
|
{
|
2016-02-22 23:50:11 +00:00
|
|
|
|
for (var i = 0; i < 3; i++)
|
2012-11-30 06:41:02 +00:00
|
|
|
|
{
|
2016-02-22 23:50:11 +00:00
|
|
|
|
_envelopes[i].HardReset();
|
|
|
|
|
_voices[i].HardReset();
|
2012-11-30 06:41:02 +00:00
|
|
|
|
}
|
2016-02-22 23:50:11 +00:00
|
|
|
|
_potCounter = 0;
|
|
|
|
|
_potX = 0;
|
|
|
|
|
_potY = 0;
|
2012-11-27 05:11:40 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ------------------------------------
|
|
|
|
|
|
2016-02-22 23:50:11 +00:00
|
|
|
|
public void ExecutePhase()
|
2013-11-14 19:33:13 +00:00
|
|
|
|
{
|
2016-02-22 23:50:11 +00:00
|
|
|
|
_cachedCycles++;
|
2013-11-14 19:33:13 +00:00
|
|
|
|
|
|
|
|
|
// potentiometer values refresh every 512 cycles
|
2016-02-22 23:50:11 +00:00
|
|
|
|
if (_potCounter == 0)
|
2013-11-14 19:33:13 +00:00
|
|
|
|
{
|
2016-02-22 23:50:11 +00:00
|
|
|
|
_potCounter = 512;
|
|
|
|
|
_potX = ReadPotX();
|
|
|
|
|
_potY = ReadPotY();
|
2013-11-14 19:33:13 +00:00
|
|
|
|
}
|
2017-05-30 17:09:46 +00:00
|
|
|
|
|
2016-02-22 23:50:11 +00:00
|
|
|
|
_potCounter--;
|
2013-11-14 19:33:13 +00:00
|
|
|
|
}
|
|
|
|
|
|
2017-05-30 17:09:46 +00:00
|
|
|
|
public void Flush(bool flushFilter)
|
2012-11-27 05:11:40 +00:00
|
|
|
|
{
|
2017-05-24 18:45:07 +00:00
|
|
|
|
|
2017-04-24 13:35:05 +00:00
|
|
|
|
while (_cachedCycles > 0)
|
|
|
|
|
{
|
|
|
|
|
_cachedCycles--;
|
2012-11-27 05:11:40 +00:00
|
|
|
|
|
2016-02-22 23:50:11 +00:00
|
|
|
|
// process voices and envelopes
|
|
|
|
|
_voice0.ExecutePhase2();
|
|
|
|
|
_voice1.ExecutePhase2();
|
|
|
|
|
_voice2.ExecutePhase2();
|
|
|
|
|
_envelope0.ExecutePhase2();
|
|
|
|
|
_envelope1.ExecutePhase2();
|
|
|
|
|
_envelope2.ExecutePhase2();
|
|
|
|
|
|
2017-04-24 13:35:05 +00:00
|
|
|
|
_voice0.Synchronize(_voice1, _voice2);
|
|
|
|
|
_voice1.Synchronize(_voice2, _voice0);
|
|
|
|
|
_voice2.Synchronize(_voice0, _voice1);
|
2016-02-22 23:50:11 +00:00
|
|
|
|
|
2017-04-24 13:35:05 +00:00
|
|
|
|
// get output
|
|
|
|
|
_voiceOutput0 = _voice0.Output(_voice2);
|
2016-02-22 23:50:11 +00:00
|
|
|
|
_voiceOutput1 = _voice1.Output(_voice0);
|
|
|
|
|
_voiceOutput2 = _voice2.Output(_voice1);
|
|
|
|
|
_envelopeOutput0 = _envelope0.Level;
|
|
|
|
|
_envelopeOutput1 = _envelope1.Level;
|
|
|
|
|
_envelopeOutput2 = _envelope2.Level;
|
|
|
|
|
|
2017-05-22 20:44:27 +00:00
|
|
|
|
int temp_v0 = (_voiceOutput0 * _envelopeOutput0);
|
|
|
|
|
int temp_v1 = (_voiceOutput1 * _envelopeOutput1);
|
|
|
|
|
int temp_v2 = (_voiceOutput2 * _envelopeOutput2);
|
|
|
|
|
|
2017-05-29 01:28:03 +00:00
|
|
|
|
int temp_filtered = 0;
|
2017-05-22 20:44:27 +00:00
|
|
|
|
int temp_not_filtered = 0;
|
|
|
|
|
|
|
|
|
|
//note that voice 3 disable is relevent only if it is not going to the filter
|
|
|
|
|
// see block diargam http://archive.6502.org/datasheets/mos_6581_sid.pdf
|
|
|
|
|
if (!_filterEnable[2] && _disableVoice3)
|
|
|
|
|
temp_v2 = 0;
|
|
|
|
|
|
|
|
|
|
// break sound into filtered and non-filtered output
|
|
|
|
|
// we need to process the filtered parts in bulk, so let's do it here
|
|
|
|
|
if (_filterEnable[0])
|
|
|
|
|
temp_filtered += temp_v0;
|
|
|
|
|
else
|
|
|
|
|
temp_not_filtered += temp_v0;
|
|
|
|
|
|
|
|
|
|
if (_filterEnable[1])
|
|
|
|
|
temp_filtered += temp_v1;
|
|
|
|
|
else
|
|
|
|
|
temp_not_filtered += temp_v1;
|
|
|
|
|
|
|
|
|
|
if (_filterEnable[2])
|
|
|
|
|
temp_filtered += temp_v2;
|
|
|
|
|
else
|
|
|
|
|
temp_not_filtered += temp_v2;
|
|
|
|
|
|
2017-04-24 13:35:05 +00:00
|
|
|
|
_sampleCyclesNum += _sampleCyclesDen;
|
|
|
|
|
if (_sampleCyclesNum >= _cpuCyclesNum)
|
|
|
|
|
{
|
|
|
|
|
_sampleCyclesNum -= _cpuCyclesNum;
|
|
|
|
|
|
|
|
|
|
if (_outputBufferIndex < _sampleRate)
|
|
|
|
|
{
|
2017-05-22 20:44:27 +00:00
|
|
|
|
_outputBuffer_not_filtered[_outputBufferIndex] = temp_not_filtered;
|
2017-05-29 01:28:03 +00:00
|
|
|
|
_outputBuffer_filtered[_outputBufferIndex] = temp_filtered;
|
2017-05-22 20:44:27 +00:00
|
|
|
|
_outputBufferIndex++;
|
2017-04-24 13:35:05 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2017-05-22 20:44:27 +00:00
|
|
|
|
//here we need to apply filtering to the samples and add them back to the buffer
|
2017-05-29 01:28:03 +00:00
|
|
|
|
|
2017-05-30 17:09:46 +00:00
|
|
|
|
if (flushFilter)
|
2017-05-29 01:28:03 +00:00
|
|
|
|
{
|
|
|
|
|
if (_filterEnable[0] | _filterEnable[1] | _filterEnable[2])
|
2017-05-25 17:42:36 +00:00
|
|
|
|
{
|
2017-05-29 01:28:03 +00:00
|
|
|
|
if ((_outputBufferIndex - filter_index) >= 16)
|
2017-05-25 17:42:36 +00:00
|
|
|
|
{
|
2017-05-29 01:28:03 +00:00
|
|
|
|
filter_operator();
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
// the length is too short for the FFT to reliably act on the output
|
|
|
|
|
// instead, clamp it to the previous output.
|
|
|
|
|
for (int i = filter_index; i < _outputBufferIndex; i++)
|
|
|
|
|
{
|
|
|
|
|
_outputBuffer_filtered[i] = last_filtered_value;
|
|
|
|
|
}
|
2017-05-25 17:42:36 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
2017-05-22 20:44:27 +00:00
|
|
|
|
|
2017-05-29 01:28:03 +00:00
|
|
|
|
filter_index = _outputBufferIndex;
|
|
|
|
|
last_filtered_value = _outputBuffer_filtered[_outputBufferIndex - 1];
|
2017-05-22 20:44:27 +00:00
|
|
|
|
}
|
|
|
|
|
|
2017-05-29 01:28:03 +00:00
|
|
|
|
// if the filter is off, keep updating the filter index to the most recent Flush
|
|
|
|
|
if (!(_filterEnable[0] | _filterEnable[1] | _filterEnable[2]))
|
|
|
|
|
{
|
|
|
|
|
filter_index = _outputBufferIndex;
|
|
|
|
|
}
|
2017-04-24 13:35:05 +00:00
|
|
|
|
}
|
|
|
|
|
|
2017-05-29 01:28:03 +00:00
|
|
|
|
|
2017-05-22 20:44:27 +00:00
|
|
|
|
public void filter_operator()
|
|
|
|
|
{
|
2017-05-25 17:42:36 +00:00
|
|
|
|
double loc_filterFrequency = (double)(_filterFrequency << 2) + 500;
|
2017-05-24 18:45:07 +00:00
|
|
|
|
|
2017-05-22 20:44:27 +00:00
|
|
|
|
double attenuation;
|
|
|
|
|
|
2017-05-29 01:28:03 +00:00
|
|
|
|
int nsamp = _outputBufferIndex - filter_index;
|
2017-05-22 20:44:27 +00:00
|
|
|
|
|
|
|
|
|
// pass the list of filtered samples to the FFT
|
2017-05-24 18:45:07 +00:00
|
|
|
|
// but needs to be a power of 2, so find the next highest power of 2 and re-sample
|
|
|
|
|
int nsamp_2 = 2;
|
|
|
|
|
bool test = true;
|
|
|
|
|
while(test)
|
2017-05-22 20:44:27 +00:00
|
|
|
|
{
|
2017-05-24 18:45:07 +00:00
|
|
|
|
nsamp_2 *= 2;
|
|
|
|
|
if (nsamp_2>nsamp)
|
|
|
|
|
{
|
|
|
|
|
test = false;
|
|
|
|
|
}
|
2017-05-22 20:44:27 +00:00
|
|
|
|
}
|
2017-05-24 18:45:07 +00:00
|
|
|
|
|
|
|
|
|
fft = new RealFFT(nsamp_2);
|
|
|
|
|
|
|
|
|
|
double[] temp_buffer = new double[nsamp_2];
|
|
|
|
|
|
|
|
|
|
// linearly interpolate the original sample set into the new denser sample set
|
|
|
|
|
for (double i = 0; i < nsamp_2; i++)
|
2017-05-22 20:44:27 +00:00
|
|
|
|
{
|
2017-05-29 01:28:03 +00:00
|
|
|
|
temp_buffer[(int)i] = _outputBuffer_filtered[(int)Math.Floor((i / (nsamp_2-1) * (nsamp - 1))) + filter_index];
|
2017-05-22 20:44:27 +00:00
|
|
|
|
}
|
2017-05-29 01:28:03 +00:00
|
|
|
|
|
2017-05-25 17:42:36 +00:00
|
|
|
|
/*
|
2017-05-29 01:28:03 +00:00
|
|
|
|
for (int i = 0; i< nsamp; i++)
|
2017-05-25 17:42:36 +00:00
|
|
|
|
{
|
2017-05-29 01:28:03 +00:00
|
|
|
|
Console.Write(_outputBuffer_filtered[(int)i + filter_index]);
|
2017-05-25 17:42:36 +00:00
|
|
|
|
Console.Write(" ");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Console.WriteLine(" ");
|
|
|
|
|
Console.WriteLine("After");
|
|
|
|
|
*/
|
2017-05-22 20:44:27 +00:00
|
|
|
|
|
2017-05-24 18:45:07 +00:00
|
|
|
|
// now we have everything we need to perform the FFT
|
|
|
|
|
fft.ComputeForward(temp_buffer);
|
2017-05-25 17:42:36 +00:00
|
|
|
|
|
2017-05-24 18:45:07 +00:00
|
|
|
|
// for each element in the frequency list, attenuate it according to the specs
|
|
|
|
|
for (int i = 0; i < nsamp_2; i++)
|
2017-05-22 20:44:27 +00:00
|
|
|
|
{
|
2017-05-29 01:28:03 +00:00
|
|
|
|
double freq = (i + 1) * ((double)(880*50)/nsamp);
|
|
|
|
|
|
|
|
|
|
// add resonance effect
|
|
|
|
|
// let's assume that frequencies near the peak are doubled in strength at max resonance
|
|
|
|
|
if ((1.2 > freq / loc_filterFrequency) && (freq / loc_filterFrequency > 0.8 ))
|
|
|
|
|
{
|
|
|
|
|
temp_buffer[i] = temp_buffer[i] * (1 + (double)_filterResonance/15);
|
|
|
|
|
}
|
2017-05-22 20:44:27 +00:00
|
|
|
|
|
2017-05-24 18:45:07 +00:00
|
|
|
|
// low pass filter
|
|
|
|
|
if (_filterSelectLoPass && freq > loc_filterFrequency)
|
|
|
|
|
{
|
|
|
|
|
//attenuated at 12db per octave
|
|
|
|
|
attenuation = Math.Log(freq / loc_filterFrequency, 2);
|
|
|
|
|
attenuation = 12 * attenuation;
|
|
|
|
|
temp_buffer[i] = temp_buffer[i] * Math.Pow(2, -attenuation / 10);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// High pass filter
|
|
|
|
|
if (_filterSelectHiPass && freq < _filterFrequency)
|
|
|
|
|
{
|
|
|
|
|
//attenuated at 12db per octave
|
|
|
|
|
attenuation = Math.Log(freq / _filterFrequency, 2);
|
|
|
|
|
attenuation = 12 * attenuation;
|
|
|
|
|
temp_buffer[i] = temp_buffer[i] * Math.Pow(2, -attenuation / 10);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Band pass filter
|
|
|
|
|
if (_filterSelectBandPass)
|
|
|
|
|
{
|
|
|
|
|
//attenuated at 6db per octave
|
|
|
|
|
attenuation = Math.Log(freq / _filterFrequency, 2);
|
2017-05-25 17:42:36 +00:00
|
|
|
|
attenuation = 6 * attenuation;
|
|
|
|
|
temp_buffer[i] = temp_buffer[i] * Math.Pow(2, -Math.Abs(attenuation) / 10);
|
2017-05-24 18:45:07 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
2017-05-22 20:44:27 +00:00
|
|
|
|
// now transform back into time space and reassemble the attenuated frequency components
|
2017-05-24 18:45:07 +00:00
|
|
|
|
fft.ComputeReverse(temp_buffer);
|
|
|
|
|
|
2017-05-29 01:28:03 +00:00
|
|
|
|
int temp = nsamp - 1;
|
2017-05-24 18:45:07 +00:00
|
|
|
|
//re-sample back down to the original number of samples
|
2017-05-29 01:28:03 +00:00
|
|
|
|
for (double i = 0; i < nsamp; i++)
|
2017-05-24 18:45:07 +00:00
|
|
|
|
{
|
2017-05-29 01:28:03 +00:00
|
|
|
|
_outputBuffer_filtered[(int)i + filter_index] = (int)(temp_buffer[(int)Math.Ceiling((i / (nsamp - 1) * (nsamp_2 - 1)))]/(nsamp_2/2));
|
|
|
|
|
|
|
|
|
|
if (_outputBuffer_filtered[(int)i + filter_index] < 0)
|
2017-05-24 18:45:07 +00:00
|
|
|
|
{
|
2017-05-29 01:28:03 +00:00
|
|
|
|
_outputBuffer_filtered[(int)i + filter_index] = 0;
|
2017-05-24 18:45:07 +00:00
|
|
|
|
}
|
2017-05-25 17:42:36 +00:00
|
|
|
|
|
2017-05-29 01:28:03 +00:00
|
|
|
|
// the FFT is only an approximate model and fails at low sample rates
|
|
|
|
|
// what we want to do is limit how much the output samples can deviate from previous output
|
|
|
|
|
// thus smoothing out the FT samples
|
|
|
|
|
|
|
|
|
|
if (i<16)
|
|
|
|
|
_outputBuffer_filtered[(int)i + filter_index] = (int)((last_filtered_value * Math.Pow(15 - i,1) + _outputBuffer_filtered[(int)i + filter_index] * Math.Pow(i,1))/ Math.Pow(15,1));
|
|
|
|
|
|
|
|
|
|
//Console.Write(_outputBuffer_filtered[(int)i + filter_index]);
|
2017-05-25 17:42:36 +00:00
|
|
|
|
//Console.Write(" ");
|
2017-05-24 18:45:07 +00:00
|
|
|
|
}
|
2017-05-29 01:28:03 +00:00
|
|
|
|
|
2017-05-25 17:42:36 +00:00
|
|
|
|
//Console.WriteLine(" ");
|
|
|
|
|
//Console.WriteLine("Before");
|
2017-05-22 20:44:27 +00:00
|
|
|
|
|
|
|
|
|
}
|
2017-04-24 13:35:05 +00:00
|
|
|
|
// ----------------------------------
|
|
|
|
|
|
|
|
|
|
public void SyncState(Serializer ser)
|
2012-12-03 08:38:12 +00:00
|
|
|
|
{
|
2017-05-16 23:58:06 +00:00
|
|
|
|
ser.Sync("_databus", ref _databus);
|
2017-05-13 13:51:03 +00:00
|
|
|
|
ser.Sync("_cachedCycles", ref _cachedCycles);
|
|
|
|
|
ser.Sync("_disableVoice3", ref _disableVoice3);
|
|
|
|
|
ser.Sync("_envelopeOutput0", ref _envelopeOutput0);
|
|
|
|
|
ser.Sync("_envelopeOutput1", ref _envelopeOutput1);
|
|
|
|
|
ser.Sync("_envelopeOutput2", ref _envelopeOutput2);
|
|
|
|
|
|
2017-05-13 14:00:28 +00:00
|
|
|
|
for (int i = 0; i < _envelopes.Length; i++)
|
|
|
|
|
{
|
|
|
|
|
ser.BeginSection("Envelope" + i);
|
|
|
|
|
_envelopes[i].SyncState(ser);
|
|
|
|
|
ser.EndSection();
|
|
|
|
|
}
|
2017-05-13 13:51:03 +00:00
|
|
|
|
|
|
|
|
|
ser.Sync("_filterEnable", ref _filterEnable, useNull: false);
|
|
|
|
|
ser.Sync("_filterFrequency", ref _filterFrequency);
|
|
|
|
|
ser.Sync("_filterResonance", ref _filterResonance);
|
|
|
|
|
ser.Sync("_filterSelectBandPass", ref _filterSelectBandPass);
|
|
|
|
|
ser.Sync("_filterSelectLoPass", ref _filterSelectLoPass);
|
|
|
|
|
ser.Sync("_filterSelectHiPass", ref _filterSelectHiPass);
|
|
|
|
|
ser.Sync("_mixer", ref _mixer);
|
|
|
|
|
ser.Sync("_potCounter", ref _potCounter);
|
|
|
|
|
ser.Sync("_potX", ref _potX);
|
|
|
|
|
ser.Sync("_potY", ref _potY);
|
|
|
|
|
ser.Sync("_sample", ref _sample);
|
|
|
|
|
ser.Sync("_voiceOutput0", ref _voiceOutput0);
|
|
|
|
|
ser.Sync("_voiceOutput1", ref _voiceOutput1);
|
|
|
|
|
ser.Sync("_voiceOutput2", ref _voiceOutput2);
|
|
|
|
|
|
2017-05-13 14:00:28 +00:00
|
|
|
|
for (int i = 0; i < _voices.Length; i++)
|
|
|
|
|
{
|
|
|
|
|
ser.BeginSection("Voice" + i);
|
|
|
|
|
_voices[i].SyncState(ser);
|
|
|
|
|
ser.EndSection();
|
|
|
|
|
}
|
|
|
|
|
|
2017-05-13 13:51:03 +00:00
|
|
|
|
ser.Sync("_volume", ref _volume);
|
2012-12-03 08:38:12 +00:00
|
|
|
|
}
|
2012-11-27 05:11:40 +00:00
|
|
|
|
}
|
|
|
|
|
}
|