From 8bb3873836e426ed170e15d88e99ab3bb33e431b Mon Sep 17 00:00:00 2001 From: Barry Harris <44396066+barry65536@users.noreply.github.com> Date: Wed, 16 May 2012 19:47:20 +0000 Subject: [PATCH] Add FM Interpolation support to burn_ym2612.cpp --- src/burn/snd/burn_ym2612.cpp | 169 ++++++++++++++++++++++++++++++++++- 1 file changed, 167 insertions(+), 2 deletions(-) diff --git a/src/burn/snd/burn_ym2612.cpp b/src/burn/snd/burn_ym2612.cpp index 547486889..fc6ff3a2f 100644 --- a/src/burn/snd/burn_ym2612.cpp +++ b/src/burn/snd/burn_ym2612.cpp @@ -15,6 +15,7 @@ static INT16* pYM2612Buffer[2 * MAX_YM2612]; static INT32 nYM2612Position; +static UINT32 nSampleSize; static INT32 nFractionalPosition; static INT32 nNumChips = 0; @@ -69,6 +70,156 @@ static void YM2612Render(INT32 nSegmentLength) // ---------------------------------------------------------------------------- // Update the sound buffer + +#define INTERPOLATE_ADD_SOUND_LEFT(route, buffer) \ + if ((YM2612RouteDirs[route] & BURN_SND_ROUTE_LEFT) == BURN_SND_ROUTE_LEFT) { \ + nLeftSample[0] += (INT32)(pYM2612Buffer[buffer][(nFractionalPosition >> 16) - 3] * YM2612Volumes[route]); \ + nLeftSample[1] += (INT32)(pYM2612Buffer[buffer][(nFractionalPosition >> 16) - 2] * YM2612Volumes[route]); \ + nLeftSample[2] += (INT32)(pYM2612Buffer[buffer][(nFractionalPosition >> 16) - 1] * YM2612Volumes[route]); \ + nLeftSample[3] += (INT32)(pYM2612Buffer[buffer][(nFractionalPosition >> 16) - 0] * YM2612Volumes[route]); \ + } + +#define INTERPOLATE_ADD_SOUND_RIGHT(route, buffer) \ + if ((YM2612RouteDirs[route] & BURN_SND_ROUTE_RIGHT) == BURN_SND_ROUTE_RIGHT) { \ + nRightSample[0] += (INT32)(pYM2612Buffer[buffer][(nFractionalPosition >> 16) - 3] * YM2612Volumes[route]); \ + nRightSample[1] += (INT32)(pYM2612Buffer[buffer][(nFractionalPosition >> 16) - 2] * YM2612Volumes[route]); \ + nRightSample[2] += (INT32)(pYM2612Buffer[buffer][(nFractionalPosition >> 16) - 1] * YM2612Volumes[route]); \ + nRightSample[3] += (INT32)(pYM2612Buffer[buffer][(nFractionalPosition >> 16) - 0] * YM2612Volumes[route]); \ + } + +static void YM2612UpdateResample(INT16* pSoundBuf, INT32 nSegmentEnd) +{ +#if defined FBA_DEBUG + if (!DebugSnd_YM2612Initted) bprintf(PRINT_ERROR, _T("YM2612UpdateResample called without init\n")); +#endif + + INT32 nSegmentLength = nSegmentEnd; + INT32 nSamplesNeeded = nSegmentEnd * nBurnYM2612SoundRate / nBurnSoundRate + 1; + + if (nSamplesNeeded < nYM2612Position) { + nSamplesNeeded = nYM2612Position; + } + + if (nSegmentLength > nBurnSoundLen) { + nSegmentLength = nBurnSoundLen; + } + nSegmentLength <<= 1; + + YM2612Render(nSamplesNeeded); + + pYM2612Buffer[0] = pBuffer + 0 * 4096 + 4; + pYM2612Buffer[1] = pBuffer + 1 * 4096 + 4; + if (nNumChips > 1) { + pYM2612Buffer[2] = pBuffer + 2 * 4096 + 4; + pYM2612Buffer[3] = pBuffer + 3 * 4096 + 4; + } + + for (INT32 i = (nFractionalPosition & 0xFFFF0000) >> 15; i < nSegmentLength; i += 2, nFractionalPosition += nSampleSize) { + INT32 nLeftSample[4] = {0, 0, 0, 0}; + INT32 nRightSample[4] = {0, 0, 0, 0}; + INT32 nTotalLeftSample, nTotalRightSample; + + /*if ((YM2612RouteDirs[BURN_SND_YM2612_YM2612_ROUTE_1] & BURN_SND_ROUTE_LEFT) == BURN_SND_ROUTE_LEFT) { + nLeftSample[0] += (INT32)(pYM2612Buffer[0][(nFractionalPosition >> 16) - 3] * YM2612Volumes[BURN_SND_YM2612_YM2612_ROUTE_1]); + nLeftSample[1] += (INT32)(pYM2612Buffer[0][(nFractionalPosition >> 16) - 2] * YM2612Volumes[BURN_SND_YM2612_YM2612_ROUTE_1]); + nLeftSample[2] += (INT32)(pYM2612Buffer[0][(nFractionalPosition >> 16) - 1] * YM2612Volumes[BURN_SND_YM2612_YM2612_ROUTE_1]); + nLeftSample[3] += (INT32)(pYM2612Buffer[0][(nFractionalPosition >> 16) - 0] * YM2612Volumes[BURN_SND_YM2612_YM2612_ROUTE_1]); + } + if ((YM2612RouteDirs[BURN_SND_YM2612_YM2612_ROUTE_1] & BURN_SND_ROUTE_RIGHT) == BURN_SND_ROUTE_RIGHT) { + nRightSample[0] += (INT32)(pYM2612Buffer[0][(nFractionalPosition >> 16) - 3] * YM2612Volumes[BURN_SND_YM2612_YM2612_ROUTE_1]); + nRightSample[1] += (INT32)(pYM2612Buffer[0][(nFractionalPosition >> 16) - 2] * YM2612Volumes[BURN_SND_YM2612_YM2612_ROUTE_1]); + nRightSample[2] += (INT32)(pYM2612Buffer[0][(nFractionalPosition >> 16) - 1] * YM2612Volumes[BURN_SND_YM2612_YM2612_ROUTE_1]); + nRightSample[3] += (INT32)(pYM2612Buffer[0][(nFractionalPosition >> 16) - 0] * YM2612Volumes[BURN_SND_YM2612_YM2612_ROUTE_1]); + } + + if ((YM2612RouteDirs[BURN_SND_YM2612_YM2612_ROUTE_2] & BURN_SND_ROUTE_LEFT) == BURN_SND_ROUTE_LEFT) { + nLeftSample[0] += (INT32)(pYM2612Buffer[1][(nFractionalPosition >> 16) - 3] * YM2612Volumes[BURN_SND_YM2612_YM2612_ROUTE_2]); + nLeftSample[1] += (INT32)(pYM2612Buffer[1][(nFractionalPosition >> 16) - 2] * YM2612Volumes[BURN_SND_YM2612_YM2612_ROUTE_2]); + nLeftSample[2] += (INT32)(pYM2612Buffer[1][(nFractionalPosition >> 16) - 1] * YM2612Volumes[BURN_SND_YM2612_YM2612_ROUTE_2]); + nLeftSample[3] += (INT32)(pYM2612Buffer[1][(nFractionalPosition >> 16) - 0] * YM2612Volumes[BURN_SND_YM2612_YM2612_ROUTE_2]); + } + if ((YM2612RouteDirs[BURN_SND_YM2612_YM2612_ROUTE_2] & BURN_SND_ROUTE_RIGHT) == BURN_SND_ROUTE_RIGHT) { + nRightSample[0] += (INT32)(pYM2612Buffer[1][(nFractionalPosition >> 16) - 3] * YM2612Volumes[BURN_SND_YM2612_YM2612_ROUTE_2]); + nRightSample[1] += (INT32)(pYM2612Buffer[1][(nFractionalPosition >> 16) - 2] * YM2612Volumes[BURN_SND_YM2612_YM2612_ROUTE_2]); + nRightSample[2] += (INT32)(pYM2612Buffer[1][(nFractionalPosition >> 16) - 1] * YM2612Volumes[BURN_SND_YM2612_YM2612_ROUTE_2]); + nRightSample[3] += (INT32)(pYM2612Buffer[1][(nFractionalPosition >> 16) - 0] * YM2612Volumes[BURN_SND_YM2612_YM2612_ROUTE_2]); + } + + if (nNumChips > 1) { + if ((YM2612RouteDirs[2 + BURN_SND_YM2612_YM2612_ROUTE_1] & BURN_SND_ROUTE_LEFT) == BURN_SND_ROUTE_LEFT) { + nLeftSample[0] += (INT32)(pYM2612Buffer[2][(nFractionalPosition >> 16) - 3] * YM2612Volumes[2 + BURN_SND_YM2612_YM2612_ROUTE_1]); + nLeftSample[1] += (INT32)(pYM2612Buffer[2][(nFractionalPosition >> 16) - 2] * YM2612Volumes[2 + BURN_SND_YM2612_YM2612_ROUTE_1]); + nLeftSample[2] += (INT32)(pYM2612Buffer[2][(nFractionalPosition >> 16) - 1] * YM2612Volumes[2 + BURN_SND_YM2612_YM2612_ROUTE_1]); + nLeftSample[3] += (INT32)(pYM2612Buffer[2][(nFractionalPosition >> 16) - 0] * YM2612Volumes[2 + BURN_SND_YM2612_YM2612_ROUTE_1]); + } + if ((YM2612RouteDirs[2 + BURN_SND_YM2612_YM2612_ROUTE_1] & BURN_SND_ROUTE_RIGHT) == BURN_SND_ROUTE_RIGHT) { + nRightSample[0] += (INT32)(pYM2612Buffer[2][(nFractionalPosition >> 16) - 3] * YM2612Volumes[2 + BURN_SND_YM2612_YM2612_ROUTE_1]); + nRightSample[1] += (INT32)(pYM2612Buffer[2][(nFractionalPosition >> 16) - 2] * YM2612Volumes[2 + BURN_SND_YM2612_YM2612_ROUTE_1]); + nRightSample[2] += (INT32)(pYM2612Buffer[2][(nFractionalPosition >> 16) - 1] * YM2612Volumes[2 + BURN_SND_YM2612_YM2612_ROUTE_1]); + nRightSample[3] += (INT32)(pYM2612Buffer[2][(nFractionalPosition >> 16) - 0] * YM2612Volumes[2 + BURN_SND_YM2612_YM2612_ROUTE_1]); + } + + if ((YM2612RouteDirs[2 + BURN_SND_YM2612_YM2612_ROUTE_2] & BURN_SND_ROUTE_LEFT) == BURN_SND_ROUTE_LEFT) { + nLeftSample[0] += (INT32)(pYM2612Buffer[3][(nFractionalPosition >> 16) - 3] * YM2612Volumes[2 + BURN_SND_YM2612_YM2612_ROUTE_2]); + nLeftSample[1] += (INT32)(pYM2612Buffer[3][(nFractionalPosition >> 16) - 2] * YM2612Volumes[2 + BURN_SND_YM2612_YM2612_ROUTE_2]); + nLeftSample[2] += (INT32)(pYM2612Buffer[3][(nFractionalPosition >> 16) - 1] * YM2612Volumes[2 + BURN_SND_YM2612_YM2612_ROUTE_2]); + nLeftSample[3] += (INT32)(pYM2612Buffer[3][(nFractionalPosition >> 16) - 0] * YM2612Volumes[2 + BURN_SND_YM2612_YM2612_ROUTE_2]); + } + if ((YM2612RouteDirs[2 + BURN_SND_YM2612_YM2612_ROUTE_2] & BURN_SND_ROUTE_RIGHT) == BURN_SND_ROUTE_RIGHT) { + nRightSample[0] += (INT32)(pYM2612Buffer[3][(nFractionalPosition >> 16) - 3] * YM2612Volumes[2 + BURN_SND_YM2612_YM2612_ROUTE_2]); + nRightSample[1] += (INT32)(pYM2612Buffer[3][(nFractionalPosition >> 16) - 2] * YM2612Volumes[2 + BURN_SND_YM2612_YM2612_ROUTE_2]); + nRightSample[2] += (INT32)(pYM2612Buffer[3][(nFractionalPosition >> 16) - 1] * YM2612Volumes[2 + BURN_SND_YM2612_YM2612_ROUTE_2]); + nRightSample[3] += (INT32)(pYM2612Buffer[3][(nFractionalPosition >> 16) - 0] * YM2612Volumes[2 + BURN_SND_YM2612_YM2612_ROUTE_2]); + } + }*/ + + INTERPOLATE_ADD_SOUND_LEFT (BURN_SND_YM2612_YM2612_ROUTE_1, 0) + INTERPOLATE_ADD_SOUND_RIGHT (BURN_SND_YM2612_YM2612_ROUTE_1, 0) + INTERPOLATE_ADD_SOUND_LEFT (BURN_SND_YM2612_YM2612_ROUTE_2, 1) + INTERPOLATE_ADD_SOUND_RIGHT (BURN_SND_YM2612_YM2612_ROUTE_2, 1) + + if (nNumChips > 1) { + INTERPOLATE_ADD_SOUND_LEFT (2 + BURN_SND_YM2612_YM2612_ROUTE_1, 2) + INTERPOLATE_ADD_SOUND_RIGHT (2 + BURN_SND_YM2612_YM2612_ROUTE_1, 2) + INTERPOLATE_ADD_SOUND_LEFT (2 + BURN_SND_YM2612_YM2612_ROUTE_2, 3) + INTERPOLATE_ADD_SOUND_RIGHT (2 + BURN_SND_YM2612_YM2612_ROUTE_2, 3) + } + + nTotalLeftSample = INTERPOLATE4PS_16BIT((nFractionalPosition >> 4) & 0x0fff, nLeftSample[0], nLeftSample[1], nLeftSample[2], nLeftSample[3]); + nTotalRightSample = INTERPOLATE4PS_16BIT((nFractionalPosition >> 4) & 0x0fff, nRightSample[0], nRightSample[1], nRightSample[2], nRightSample[3]); + + nTotalLeftSample = BURN_SND_CLIP(nTotalLeftSample); + nTotalRightSample = BURN_SND_CLIP(nTotalRightSample); + + if (bYM2612AddSignal) { + pSoundBuf[i + 0] += nTotalLeftSample; + pSoundBuf[i + 1] += nTotalRightSample; + } else { + pSoundBuf[i + 0] = nTotalLeftSample; + pSoundBuf[i + 1] = nTotalRightSample; + } + } + + if (nSegmentEnd >= nBurnSoundLen) { + INT32 nExtraSamples = nSamplesNeeded - (nFractionalPosition >> 16); + + for (INT32 i = -4; i < nExtraSamples; i++) { + pYM2612Buffer[0][i] = pYM2612Buffer[0][(nFractionalPosition >> 16) + i]; + pYM2612Buffer[1][i] = pYM2612Buffer[1][(nFractionalPosition >> 16) + i]; + if (nNumChips > 1) { + pYM2612Buffer[2][i] = pYM2612Buffer[2][(nFractionalPosition >> 16) + i]; + pYM2612Buffer[3][i] = pYM2612Buffer[3][(nFractionalPosition >> 16) + i]; + } + } + + nFractionalPosition &= 0xFFFF; + + nYM2612Position = nExtraSamples; + + dTime += 100.0 / nBurnFPS; + } +} + static void YM2612UpdateNormal(INT16* pSoundBuf, INT32 nSegmentEnd) { #if defined FBA_DEBUG @@ -230,8 +381,22 @@ INT32 BurnYM2612Init(INT32 num, INT32 nClockFrequency, FM_IRQHANDLER IRQCallback BurnYM2612StreamCallback = StreamCallback; - nBurnYM2612SoundRate = nBurnSoundRate; - BurnYM2612Update = YM2612UpdateNormal; + if (nFMInterpolation == 3) { + // Set YM2612 core samplerate to match the hardware + nBurnYM2612SoundRate = nClockFrequency / 144; + // Bring YM2612 core samplerate within usable range + while (nBurnYM2612SoundRate > nBurnSoundRate * 3) { + nBurnYM2612SoundRate >>= 1; + } + + BurnYM2612Update = YM2612UpdateResample; + + nSampleSize = (UINT32)nBurnYM2612SoundRate * (1 << 16) / nBurnSoundRate; + } else { + nBurnYM2612SoundRate = nBurnSoundRate; + + BurnYM2612Update = YM2612UpdateNormal; + } YM2612Init(num, nClockFrequency, nBurnYM2612SoundRate, &BurnOPNTimerCallback, IRQCallback);