diff --git a/BizHawk.Emulation/Sound/YM2612.cs b/BizHawk.Emulation/Sound/YM2612.cs index 0d8a3ccb2c..08a5bc02c3 100644 --- a/BizHawk.Emulation/Sound/YM2612.cs +++ b/BizHawk.Emulation/Sound/YM2612.cs @@ -189,7 +189,7 @@ namespace BizHawk.Emulation.Sound switch (register & 0xF0) { case 0x20: WriteLowBlock(register, value); break; - case 0x30: Write_MUL_DT1(chan, oper, value); break; + case 0x30: Write_MUL_DT(chan, oper, value); break; case 0x40: Write_TL(chan, oper, value); break; case 0x50: Write_AR_KS(chan, oper, value); break; case 0x60: Write_DR_AM(chan, oper, value); break; @@ -208,7 +208,7 @@ namespace BizHawk.Emulation.Sound switch (register & 0xF0) { - case 0x30: Write_MUL_DT1(chan, oper, value); break; + case 0x30: Write_MUL_DT(chan, oper, value); break; case 0x40: Write_TL(chan, oper, value); break; case 0x50: Write_AR_KS(chan, oper, value); break; case 0x60: Write_DR_AM(chan, oper, value); break; @@ -353,12 +353,12 @@ namespace BizHawk.Emulation.Sound channel.LeftOutput = (value & 0x80) != 0; } - void Write_MUL_DT1(int chan, int op, byte value) + void Write_MUL_DT(int chan, int op, byte value) { if (chan < 0) return; var oper = Channels[chan].Operators[op]; oper.MUL_Multiple = value & 15; - oper.DT1_Detune = (value >> 4) & 7; + oper.DT_Detune = (value >> 4) & 7; } public void Write_TL(int chan, int op, byte value) @@ -519,7 +519,7 @@ namespace BizHawk.Emulation.Sound } // ==================================================================================== - // Envelope Generator + // Support Tables // ==================================================================================== #region tables @@ -616,8 +616,48 @@ namespace BizHawk.Emulation.Sound 0x000, 0x020, 0x040, 0x060, 0x080, 0x0A0, 0x0C0, 0x0E0, 0x100, 0x120, 0x140, 0x160, 0x180, 0x1A0, 0x1C0, 0x3FF }; + + static readonly int[] detuneTable = + { + 0, 0, 1, 2, // Key-Code 0 + 0, 0, 1, 2, // Key-Code 1 + 0, 0, 1, 2, // Key-Code 2 + 0, 0, 1, 2, // Key-Code 3 + 0, 1, 2, 2, // Key-Code 4 + 0, 1, 2, 3, // Key-Code 5 + 0, 1, 2, 3, // Key-Code 6 + 0, 1, 2, 3, // Key-Code 7 + 0, 1, 2, 4, // Key-Code 8 + 0, 1, 3, 4, // Key-Code 9 + 0, 1, 3, 4, // Key-Code 10 + 0, 1, 3, 5, // Key-Code 11 + 0, 2, 4, 5, // Key-Code 12 + 0, 2, 4, 6, // Key-Code 13 + 0, 2, 4, 6, // Key-Code 14 + 0, 2, 5, 7, // Key-Code 15 + 0, 2, 5, 8, // Key-Code 16 + 0, 3, 6, 8, // Key-Code 17 + 0, 3, 6, 9, // Key-Code 18 + 0, 3, 7, 10, // Key-Code 19 + 0, 4, 8, 11, // Key-Code 20 + 0, 4, 8, 12, // Key-Code 21 + 0, 4, 9, 13, // Key-Code 22 + 0, 5, 10, 14, // Key-Code 23 + 0, 5, 11, 16, // Key-Code 24 + 0, 6, 12, 17, // Key-Code 25 + 0, 6, 13, 19, // Key-Code 26 + 0, 7, 14, 20, // Key-Code 27 + 0, 8, 16, 22, // Key-Code 28 + 0, 8, 16, 22, // Key-Code 29 + 0, 8, 16, 22, // Key-Code 30 + 0, 8, 16, 22 // Key-Code 31 + }; #endregion + // ==================================================================================== + // Envelope Generator + // ==================================================================================== + int egDivisorCounter; // This provides the /3 divisor to run the envelope generator once for every 3 FM sample output ticks. int egCycleCounter; // This provides a rolling counter of the envelope generator update ticks. (/3 divisor already applied) @@ -755,7 +795,12 @@ namespace BizHawk.Emulation.Sound default: phaseIncrement <<= op.Block - 1; break; } - // TODO: Detune + // Apply Detune + int detuneAdjustment = detuneTable[(op.KeyCode * 4) + (op.DT_Detune & 3)]; + if ((op.DT_Detune & 4) != 0) + detuneAdjustment = -detuneAdjustment; + phaseIncrement += detuneAdjustment; + phaseIncrement &= 0x1FFFF; // mask to 17-bits, which is the current size of the register at this point in the calculation. This allows proper detune overflow. // Apply MUL switch (op.MUL_Multiple) @@ -920,7 +965,7 @@ namespace BizHawk.Emulation.Sound public int KS_KeyScale; // 2 bits public int SSG_EG; // 4 bits - public int DT1_Detune; // 3 bits + public int DT_Detune; // 3 bits public int MUL_Multiple; // 4 bits public bool AM_AmplitudeModulation; // 1 bit @@ -944,7 +989,7 @@ namespace BizHawk.Emulation.Sound set { egAttenuation = value; - if (egAttenuation < 0) egAttenuation = 0; + if (egAttenuation < 0) egAttenuation = 0; if (egAttenuation > 1023) egAttenuation = 1023; } } @@ -984,13 +1029,65 @@ namespace BizHawk.Emulation.Sound GetSamplesNative(nativeSamples); // downsample from native output rate to 44100. - // TODO this is not good resampling code. I'm not rightly sure it's even correct as linear interpolation goes. + //CrappyNaiveResampler(nativeSamples, samples); + MaybeBetterDownsampler(nativeSamples, samples); + } + + static void CrappyNaiveResampler(short[] input, short[] output) + { + // this is not good resampling code. + int numStereoSamples = output.Length / 2; + int offset = 0; for (int i = 0; i < numStereoSamples; i++) { int nativeOffset = ((i * ntscOutputRate) / 44100) * 2; - samples[offset++] += nativeSamples[nativeOffset++]; // left - samples[offset++] += nativeSamples[nativeOffset]; // right + output[offset++] += input[nativeOffset++]; // left + output[offset++] += input[nativeOffset]; // right + } + } + + static double Fraction(double value) + { + return value - Math.Floor(value); + } + + static void MaybeBetterDownsampler(short[] input, short[] output) + { + // This is still not a good resampler. But it's better than the other one. Unsure how much difference it makes. + // The difference with this one is that all source samples will be sampled and weighted, none skipped over. + double nativeSamplesPerOutputSample = (double) input.Length / (double) output.Length; + int outputStereoSamples = output.Length / 2; + int inputStereoSamples = input.Length / 2; + + int offset = 0; + for (int i = 0; i < outputStereoSamples; i++) + { + + double startSample = nativeSamplesPerOutputSample * i; + double endSample = nativeSamplesPerOutputSample * (i+1); + + int iStartSample = (int) Math.Floor(startSample); + int iEndSample = (int) Math.Floor(endSample); + double leftSample = 0; + double rightSample = 0; + for (int j = iStartSample; j <= iEndSample; j++) + { + if (j == inputStereoSamples) + break; + + double weight = 1.0; + + if (j == iStartSample) + weight = 1.0 - Fraction(startSample); + else if (j == iEndSample) + weight = Fraction(endSample); + + leftSample += ((double) input[(j * 2) + 0] * weight); + rightSample += ((double) input[(j * 2) + 1] * weight); + } + output[offset++] = (short) leftSample; + output[offset++] = (short) rightSample; } } diff --git a/BizHawk.MultiClient/BizHawk.MultiClient.csproj b/BizHawk.MultiClient/BizHawk.MultiClient.csproj index 112a6db3a0..b8b8ca6ed0 100644 --- a/BizHawk.MultiClient/BizHawk.MultiClient.csproj +++ b/BizHawk.MultiClient/BizHawk.MultiClient.csproj @@ -836,6 +836,7 @@ + diff --git a/BizHawk.MultiClient/MainForm.cs b/BizHawk.MultiClient/MainForm.cs index c15906cc76..bbc85961a1 100644 --- a/BizHawk.MultiClient/MainForm.cs +++ b/BizHawk.MultiClient/MainForm.cs @@ -1404,7 +1404,7 @@ namespace BizHawk.MultiClient UpdateStatusSlots(); UpdateDumpIcon(); - LastState = Global.Emulator.SaveStateBinary(); + CaptureRewindState(); return true; } @@ -2400,9 +2400,9 @@ namespace BizHawk.MultiClient private void Render() { var video = Global.Emulator.VideoProvider; - if (video.BufferHeight != lastHeight || video.BufferWidth != lastWidth) + if (video.BufferHeight != lastHeight || video.VirtualWidth != lastWidth) { - lastWidth = video.BufferWidth; + lastWidth = video.VirtualWidth; lastHeight = video.BufferHeight; FrameBufferResized(); } @@ -2420,14 +2420,18 @@ namespace BizHawk.MultiClient int borderHeight = Size.Height - renderTarget.Size.Height; // start at target zoom and work way down until we find acceptable zoom - for (; zoom >= 1; zoom--) + int videoWidth = video.BufferWidth; // just so its initialized + int videoHeight = video.BufferHeight; + for (; zoom >= 1; zoom--) { - if ((((video.BufferWidth * zoom) + borderWidth) < area.Width) && (((video.BufferHeight * zoom) + borderHeight) < area.Height)) + videoWidth = 320 * zoom; + videoHeight = video.BufferHeight * zoom; + if ((videoWidth + borderWidth < area.Width) && (videoHeight + borderHeight < area.Height)) break; } // Change size - Size = new Size((video.BufferWidth * zoom) + borderWidth, (video.BufferHeight * zoom + borderHeight)); + Size = new Size(videoWidth + borderWidth, videoHeight + borderHeight); PerformLayout(); Global.RenderPanel.Resized = true;