njz3: Fix sound volume with new quadrophonic audio code

This commit is contained in:
Ian Curtis 2022-06-19 16:15:54 +00:00
parent e3bc56c8f1
commit a742a73772
8 changed files with 98 additions and 70 deletions

View file

@ -1123,6 +1123,7 @@
<hardware>
<platform>Sega Model 3</platform>
<stepping>1.5</stepping>
<audio>QuadFrontRear</audio>
<inputs>
<input type="common" />
<input type="analog_gun1" />
@ -1197,6 +1198,7 @@
<hardware>
<platform>Sega Model 3</platform>
<stepping>1.5</stepping>
<audio>QuadFrontRear</audio>
<inputs>
<input type="common" />
<input type="analog_gun1" />

View file

@ -147,9 +147,7 @@ int CDSBResampler::UpSampleAndMix(INT16 *outL, INT16 *outR, INT16 *inL, INT16 *i
// Obtain program volume settings and convert to 24.8 fixed point (0-200 -> 0x00-0x200)
musicVol = m_config["MusicVolume"].ValueAs<int>();
soundVol = m_config["SoundVolume"].ValueAs<int>();
musicVol = (INT32) ((float) 0x100 * (float) musicVol / 100.0f);
soundVol = (INT32) ((float) 0x100 * (float) soundVol / 100.0f);
// Scale volume from 0x00-0xFF -> 0x00-0x100 (24.8 fixed point)
v[0] = (INT16) ((float) 0x100 * (float) volumeL / 255.0f);
@ -167,8 +165,8 @@ int CDSBResampler::UpSampleAndMix(INT16 *outL, INT16 *outR, INT16 *inL, INT16 *i
rightSample = (rightSample*v[0]*musicVol) >> 16;
// Apply sound volume setting
leftSoundSample = (outL[outIdx]*soundVol) >> 8;
rightSoundSample = (outR[outIdx]*soundVol) >> 8;
leftSoundSample = outL[outIdx];
rightSoundSample = outR[outIdx];
// Mix and output
outL[outIdx] = MixAndClip(leftSoundSample, leftSample);
@ -185,7 +183,6 @@ int CDSBResampler::UpSampleAndMix(INT16 *outL, INT16 *outR, INT16 *inL, INT16 *i
pFrac += (1<<8);
nFrac -= (1<<8);
inIdx++; // advance samples (for upsampling only; downsampling may advance by more than one -- add delta every loop iteration)
}
}
@ -435,7 +432,7 @@ void CDSB1::RunFrame(INT16 *audioL, INT16 *audioR)
// DSB code applies SCSP volume, too, so we must still mix
memset(mpegL, 0, (32000/60+2)*sizeof(INT16));
memset(mpegR, 0, (32000/60+2)*sizeof(INT16));
retainedSamples = Resampler.UpSampleAndMix(audioL, audioR, mpegL, mpegR, 0, 0, 44100/60, 32000/60+2, 44100, 32000);
retainedSamples = Resampler.UpSampleAndMix(audioL, audioR, mpegL, mpegR, 0, 0, NUM_SAMPLES_PER_FRAME, 32000/60+2, 44100, 32000);
return;
}
@ -457,7 +454,7 @@ void CDSB1::RunFrame(INT16 *audioL, INT16 *audioR)
// Decode MPEG for this frame
MpegDec::DecodeAudio(&mpegL[retainedSamples], &mpegR[retainedSamples], 32000 / 60 - retainedSamples + 2);
retainedSamples = Resampler.UpSampleAndMix(audioL, audioR, mpegL, mpegR, v, v, 44100/60, 32000/60+2, 44100, 32000);
retainedSamples = Resampler.UpSampleAndMix(audioL, audioR, mpegL, mpegR, v, v, NUM_SAMPLES_PER_FRAME, 32000/60+2, 44100, 32000);
}
void CDSB1::Reset(void)
@ -1001,7 +998,7 @@ void CDSB2::RunFrame(INT16 *audioL, INT16 *audioR)
// DSB code applies SCSP volume, too, so we must still mix
memset(mpegL, 0, (32000 / 60 + 2) * sizeof(INT16));
memset(mpegR, 0, (32000 / 60 + 2) * sizeof(INT16));
retainedSamples = Resampler.UpSampleAndMix(audioL, audioR, mpegL, mpegR, volume[0], volume[1], 44100 / 60, 32000 / 60 + 2, 44100, 32000);
retainedSamples = Resampler.UpSampleAndMix(audioL, audioR, mpegL, mpegR, volume[0], volume[1], NUM_SAMPLES_PER_FRAME, 32000 / 60 + 2, 44100, 32000);
return;
}
@ -1072,7 +1069,7 @@ void CDSB2::RunFrame(INT16 *audioL, INT16 *audioR)
break;
}
retainedSamples = Resampler.UpSampleAndMix(audioL, audioR, leftChannelSource, rightChannelSource, volL, volR, 44100 / 60, 32000 / 60 + 2, 44100, 32000);
retainedSamples = Resampler.UpSampleAndMix(audioL, audioR, leftChannelSource, rightChannelSource, volL, volR, NUM_SAMPLES_PER_FRAME, 32000 / 60 + 2, 44100, 32000);
}
void CDSB2::Reset(void)

View file

@ -41,6 +41,8 @@
#define FIFO_STACK_SIZE 0x100
#define FIFO_STACK_SIZE_MASK (FIFO_STACK_SIZE - 1)
#define NUM_SAMPLES_PER_FRAME (44100/60)
/******************************************************************************
Resampling

View file

@ -67,6 +67,18 @@ static FILE *soundFP;
#endif
// Offsets of memory regions within sound board's pool
#define OFFSET_RAM1 0 // 1 MB SCSP1 RAM
#define OFFSET_RAM2 0x100000 // 1 MB SCSP2 RAM
#define LENGTH_CHANNEL_BUFFER (sizeof(INT16)*NUM_SAMPLES_PER_FRAME) // 1470 bytes (16 bits x 44.1 KHz x 1/60th second)
#define OFFSET_AUDIO_FRONTLEFT 0x200000 // 1470 bytes (16 bits, 44.1 KHz, 1/60th second) left audio channel
#define OFFSET_AUDIO_FRONTRIGHT (OFFSET_AUDIO_FRONTLEFT + LENGTH_CHANNEL_BUFFER) // 1470 bytes right audio channel
#define OFFSET_AUDIO_REARLEFT (OFFSET_AUDIO_FRONTRIGHT + LENGTH_CHANNEL_BUFFER) // 1470 bytes (16 bits, 44.1 KHz, 1/60th second) left audio channel
#define OFFSET_AUDIO_REARRIGHT (OFFSET_AUDIO_REARLEFT + LENGTH_CHANNEL_BUFFER) // 1470 bytes right audio channel
#define MEMORY_POOL_SIZE (0x100000 + 0x100000 + 4*LENGTH_CHANNEL_BUFFER)
/******************************************************************************
68K Address Space Handlers
******************************************************************************/
@ -350,13 +362,25 @@ bool CSoundBoard::RunFrame(void)
}
else
{
memset(audioFL, 0, 44100/60*sizeof(INT16));
memset(audioFR, 0, 44100/60*sizeof(INT16));
memset(audioRL, 0, 44100/60*sizeof(INT16));
memset(audioRR, 0, 44100/60*sizeof(INT16));
memset(audioFL, 0, LENGTH_CHANNEL_BUFFER);
memset(audioFR, 0, LENGTH_CHANNEL_BUFFER);
memset(audioRL, 0, LENGTH_CHANNEL_BUFFER);
memset(audioRR, 0, LENGTH_CHANNEL_BUFFER);
}
// Run DSB and mix with existing audio
// Compute sound volume as
INT32 soundVol = m_config["SoundVolume"].ValueAs<int>();
soundVol = (INT32)((float)0x100 * (float)soundVol / 100.0f);
// Apply sound volume setting to SCSP channels only
for (int i = 0; i < NUM_SAMPLES_PER_FRAME; i++) {
audioFL[i] = (audioFL[i]*soundVol) >> 8;
audioFR[i] = (audioFR[i]*soundVol) >> 8;
audioRL[i] = (audioRL[i]*soundVol) >> 8;
audioRR[i] = (audioRR[i]*soundVol) >> 8;
}
// Run DSB and mix with existing audio, apply music volume
if (NULL != DSB) {
// Will need to mix with proper front, rear channels or both (game specific)
bool mixDSBWithFront = true; // Everything to front channels for now
@ -368,12 +392,12 @@ bool CSoundBoard::RunFrame(void)
}
// Output the audio buffers
bool bufferFull = OutputAudio(44100/60, audioFL, audioFR, audioRL, audioRR, m_config["FlipStereo"].ValueAs<bool>());
bool bufferFull = OutputAudio(NUM_SAMPLES_PER_FRAME, audioFL, audioFR, audioRL, audioRR, m_config["FlipStereo"].ValueAs<bool>());
#ifdef SUPERMODEL_LOG_AUDIO
// Output to binary file
INT16 s;
for (int i = 0; i < 44100/60; i++)
for (int i = 0; i < NUM_SAMPLES_PER_FRAME; i++)
{
s = audioL[i];
fwrite(&s, sizeof(INT16), 1, soundFP); // left channel
@ -452,16 +476,6 @@ void CSoundBoard::AttachDSB(CDSB *DSBPtr)
DebugLog("Sound Board connected to DSB\n");
}
// Offsets of memory regions within sound board's pool
#define OFFSET_RAM1 0 // 1 MB SCSP1 RAM
#define OFFSET_RAM2 0x100000 // 1 MB SCSP2 RAM
#define LENGTH_CHANNEL_BUFFER 0x5BE
#define OFFSET_AUDIO_FRONTLEFT 0x200000 // 1470 bytes (16 bits, 44.1 KHz, 1/60th second) left audio channel
#define OFFSET_AUDIO_FRONTRIGHT (OFFSET_AUDIO_FRONTLEFT + LENGTH_CHANNEL_BUFFER) // 1470 bytes right audio channel
#define OFFSET_AUDIO_REARLEFT (OFFSET_AUDIO_FRONTRIGHT + LENGTH_CHANNEL_BUFFER) // 1470 bytes (16 bits, 44.1 KHz, 1/60th second) left audio channel
#define OFFSET_AUDIO_REARRIGHT (OFFSET_AUDIO_REARLEFT + LENGTH_CHANNEL_BUFFER) // 1470 bytes right audio channel
#define MEMORY_POOL_SIZE (0x100000 + 0x100000 + 4*LENGTH_CHANNEL_BUFFER)
bool CSoundBoard::Init(const UINT8 *soundROMPtr, const UINT8 *sampleROMPtr)
{
@ -495,7 +509,7 @@ bool CSoundBoard::Init(const UINT8 *soundROMPtr, const UINT8 *sampleROMPtr)
M68KGetContext(&M68K);
// Initialize SCSPs
SCSP_SetBuffers(audioFL, audioFR, audioRL, audioRR, 44100/60);
SCSP_SetBuffers(audioFL, audioFR, audioRL, audioRR, NUM_SAMPLES_PER_FRAME);
SCSP_SetCB(SCSP68KRunCallback, SCSP68KIRQCallback);
if (OKAY != SCSP_Init(m_config, 2))
return FAIL;

View file

@ -154,6 +154,18 @@ static INT16 AddAndClampINT16(INT32 x, INT32 y)
return (INT16)sum;
}
static INT16 MixINT16(INT32 x, INT32 y)
{
INT32 sum = (x + y)>>1;
if (sum > INT16_MAX) {
sum = INT16_MAX;
}
if (sum < INT16_MIN) {
sum = INT16_MIN;
}
return (INT16)sum;
}
static void PlayCallback(void* data, Uint8* stream, int len)
{
//printf("PlayCallback(%d) [writePos = %u, writeWrapped = %s, playPos = %u, audioBufferSize = %u]\n",
@ -265,9 +277,9 @@ static void MixChannels(unsigned numSamples, INT16* leftFrontBuffer, INT16* righ
if (nbHostAudioChannels == 1) {
for (unsigned i = 0; i < numSamples; i++) {
INT16 monovalue = AddAndClampINT16(
(INT32)(leftFrontBuffer[i] * balanceFactorFrontLeft) + (INT32)(rightFrontBuffer[i] * balanceFactorFrontRight),
(INT32)(leftRearBuffer[i] * balanceFactorRearLeft) + (INT32)(rightRearBuffer[i] * balanceFactorRearRight));
INT16 monovalue = MixINT16(
MixINT16((INT32)(leftFrontBuffer[i] * balanceFactorFrontLeft), (INT32)(rightFrontBuffer[i] * balanceFactorFrontRight)),
MixINT16((INT32)(leftRearBuffer[i] * balanceFactorRearLeft), (INT32)(rightRearBuffer[i] * balanceFactorRearRight)));
*p++ = monovalue;
}
} else {
@ -283,8 +295,8 @@ static void MixChannels(unsigned numSamples, INT16* leftFrontBuffer, INT16* righ
// Now order channels according to audio type
if (nbHostAudioChannels == 2) {
for (unsigned i = 0; i < numSamples; i++) {
INT16 leftvalue = AddAndClampINT16((INT32)(leftFrontBuffer[i] * balanceFactorFrontLeft), (INT32)(leftRearBuffer[i] * balanceFactorRearLeft));
INT16 rightvalue = AddAndClampINT16((INT32)(rightFrontBuffer[i]*balanceFactorFrontRight), (INT32)(rightRearBuffer[i]*balanceFactorRearRight));
INT16 leftvalue = MixINT16((INT32)(leftFrontBuffer[i] * balanceFactorFrontLeft), (INT32)(leftRearBuffer[i] * balanceFactorRearLeft));
INT16 rightvalue = MixINT16((INT32)(rightFrontBuffer[i]*balanceFactorFrontRight), (INT32)(rightRearBuffer[i]*balanceFactorRearRight));
if (flipStereo) // swap left and right channels
{
*p++ = rightvalue;
@ -304,7 +316,7 @@ static void MixChannels(unsigned numSamples, INT16* leftFrontBuffer, INT16* righ
// Check game audio type
switch (AudioType) {
case Game::MONO: {
INT16 monovalue = AddAndClampINT16(AddAndClampINT16(frontLeftValue, frontRightValue), AddAndClampINT16(rearLeftValue, rearRightValue));
INT16 monovalue = MixINT16(MixINT16(frontLeftValue, frontRightValue), MixINT16(rearLeftValue, rearRightValue));
*p++ = monovalue;
*p++ = monovalue;
*p++ = monovalue;
@ -313,8 +325,8 @@ static void MixChannels(unsigned numSamples, INT16* leftFrontBuffer, INT16* righ
case Game::STEREO_LR:
case Game::STEREO_RL: {
INT16 leftvalue = AddAndClampINT16(frontLeftValue, frontRightValue);
INT16 rightvalue = AddAndClampINT16(rearLeftValue, rearRightValue);
INT16 leftvalue = MixINT16(frontLeftValue, frontRightValue);
INT16 rightvalue = MixINT16(rearLeftValue, rearRightValue);
if (flipStereo) // swap left and right channels
{
*p++ = rightvalue;
@ -366,10 +378,10 @@ static void MixChannels(unsigned numSamples, INT16* leftFrontBuffer, INT16* righ
case Game::QUAD_1_LR_2_FR_MIX:
// Split mix: one goes to left/right, other front/rear (mono)
// =>Remix all!
INT16 newfrontLeftValue = AddAndClampINT16(frontLeftValue, rearLeftValue);
INT16 newfrontRightValue = AddAndClampINT16(frontLeftValue, rearRightValue);
INT16 newrearLeftValue = AddAndClampINT16(frontRightValue, rearLeftValue);
INT16 newrearRightValue = AddAndClampINT16(frontRightValue, rearRightValue);
INT16 newfrontLeftValue = MixINT16(frontLeftValue, rearLeftValue);
INT16 newfrontRightValue = MixINT16(frontLeftValue, rearRightValue);
INT16 newrearLeftValue = MixINT16(frontRightValue, rearLeftValue);
INT16 newrearRightValue = MixINT16(frontRightValue, rearRightValue);
if (flipStereo) // swap left and right channels
{

View file

@ -859,11 +859,13 @@ static void SuperSleepUntil(uint64_t target)
}
// Spin until requested time
uint64_t now;
volatile uint64_t now;
int32_t remain;
do
{
now = SDL_GetPerformanceCounter();
} while (now < target);
remain = int32_t((target - now));
} while (remain>0);
}
@ -1251,8 +1253,8 @@ int Supermodel(const Game &game, ROMSet *rom_set, IEmulator *Model3, CInputs *In
// Refresh rate (frame limiting)
if (paused || s_runtime_config["Throttle"].ValueAs<bool>())
{
SuperSleepUntil(nextTime);
nextTime = SDL_GetPerformanceCounter() + perfCountPerFrame;
SuperSleepUntil(nextTime);
nextTime = SDL_GetPerformanceCounter() + perfCountPerFrame;
}
// Measure frame rate
@ -1729,8 +1731,7 @@ static ParsedCommandLine ParseCommandLine(int argc, char **argv)
{
std::vector<std::string> parts = Util::Format(arg).Split('=');
if (parts.size() != 2)
{
ErrorLog("'-res' requires both a width and height (e.g., '-res=496,384').");
{ErrorLog("'-res' requires both a width and height (e.g., '-res=496,384').");
cmd_line.error = true;
}
else

View file

@ -639,15 +639,15 @@ bool SCSP_Init(const Util::Config::Node &config, int n)
for(int i=0;i<0x400;++i)
{
float fcent=(double) 1200.0*log2((double)(((double) 1024.0+(double)i)/(double)1024.0));
double fcent=(double) 1200.0*log2((double)(((double) 1024.0+(double)i)/(double)1024.0));
//float fcent=1.0+(float) i/1024.0;
fcent=(double) 44100.0*pow(2.0,fcent/1200.0);
FNS_Table[i]=(float) (1<<SHIFT) *fcent;
FNS_Table[i]=(UINT32)((float) (1<<SHIFT) *fcent);
//FNS_Table[i]=(i>>(10-SHIFT))|(1<<SHIFT);
}
for (int i = 0; i < 0x400; ++i) {
float envDB = ((float)(3 * (i - 0x3ff))) / 32.0;
float envDB = ((float)(3 * (i - 0x3ff))) / 32.0f;
float scale = (float)(1 << SHIFT);
EG_TABLE[i] = (INT32)(pow(10.0, envDB / 20.0)*scale);
}
@ -681,28 +681,28 @@ bool SCSP_Init(const Util::Config::Node &config, int n)
float TL=1.0;
float SegaDB=0;
if(iTL&0x01) SegaDB-=0.4;
if(iTL&0x02) SegaDB-=0.8;
if(iTL&0x04) SegaDB-=1.5;
if(iTL&0x08) SegaDB-=3;
if(iTL&0x10) SegaDB-=6;
if(iTL&0x20) SegaDB-=12;
if(iTL&0x40) SegaDB-=24;
if(iTL&0x80) SegaDB-=48;
if(iTL&0x01) SegaDB-=0.4f;
if(iTL&0x02) SegaDB-=0.8f;
if(iTL&0x04) SegaDB-=1.5f;
if(iTL&0x08) SegaDB-=3.0f;
if(iTL&0x10) SegaDB-=6.0f;
if(iTL&0x20) SegaDB-=12.0f;
if(iTL&0x40) SegaDB-=24.0f;
if(iTL&0x80) SegaDB-=48.0f;
TL=pow(10.0,SegaDB/20.0);
TL=powf(10.0f,SegaDB/20.0f);
float PAN=1.0;
SegaDB=0;
if(iPAN&0x1) SegaDB-=3;
if(iPAN&0x2) SegaDB-=6;
if(iPAN&0x4) SegaDB-=12;
if(iPAN&0x8) SegaDB-=24;
if(iPAN&0x1) SegaDB-=3.0f;
if(iPAN&0x2) SegaDB-=6.0f;
if(iPAN&0x4) SegaDB-=12.0f;
if(iPAN&0x8) SegaDB-=24.0f;
if(iPAN==0xf) PAN=0.0;
else if(iPAN==0x1f) PAN=0.0;
else PAN=pow(10.0,SegaDB/20.0);
else PAN=powf(10.0f,SegaDB/20.0f);
float LPAN,RPAN;
@ -719,7 +719,7 @@ bool SCSP_Init(const Util::Config::Node &config, int n)
float SDL=1.0;
if(iSDL)
SDL=pow(10.0,(SDLT[iSDL])/20.0);
SDL=powf(10.0f,(SDLT[iSDL])/20.0f);
else
SDL=0.0;
@ -1545,7 +1545,7 @@ void SCSP_CpuRunScanline()
void SCSP_DoMasterSamples(int nsamples)
{
int slice = 12000000 / (SoundClock*nsamples); // 68K cycles/sample
int slice = (int)(12000000 / (SoundClock*nsamples)); // 68K cycles/sample
static int lastdiff = 0;
/*

View file

@ -49,10 +49,10 @@ struct _LFO
static int PLFO_TRI[256], PLFO_SQR[256], PLFO_SAW[256], PLFO_NOI[256];
static int ALFO_TRI[256], ALFO_SQR[256], ALFO_SAW[256], ALFO_NOI[256];
static float LFOFreq[32] = { 0.17,0.19,0.23,0.27,0.34,0.39,0.45,0.55,0.68,0.78,0.92,1.10,1.39,1.60,1.87,2.27,
2.87,3.31,3.92,4.79,6.15,7.18,8.60,10.8,14.4,17.2,21.5,28.7,43.1,57.4,86.1,172.3 };
static float ASCALE[8] = { 0.0,0.4,0.8,1.5,3.0,6.0,12.0,24.0 };
static float PSCALE[8] = { 0.0,7.0,13.5,27.0,55.0,112.0,230.0,494 };
static float LFOFreq[32] = { 0.17f, 0.19f, 0.23f, 0.27f, 0.34f, 0.39f, 0.45f, 0.55f, 0.68f, 0.78f, 0.92f, 1.10f, 1.39f, 1.60f, 1.87f, 2.27f,
2.87f, 3.31f, 3.92f, 4.79f, 6.15f, 7.18f, 8.60f, 10.8f, 14.4f, 17.2f, 21.5f, 28.7f, 43.1f, 57.4f, 86.1f, 172.3f };
static float ASCALE[8] = { 0.0f, 0.4f, 0.8f, 1.5f, 3.0f, 6.0f, 12.0f, 24.0f };
static float PSCALE[8] = { 0.0f, 7.0f, 13.5f, 27.0f, 55.0f, 112.0f, 230.0f, 494.0f };
static int PSCALES[8][256];
static int ASCALES[8][256];
@ -151,7 +151,7 @@ signed int inline ALFO_Step(struct _LFO *LFO)
void LFO_ComputeStep(struct _LFO *LFO, UINT32 LFOF, UINT32 LFOWS, UINT32 LFOS, int ALFO)
{
float step = (float)LFOFreq[LFOF] * 256.0 / (float)44100.0;
float step = (float)LFOFreq[LFOF] * 256.0f / (float)44100.0f;
LFO->phase_step = (unsigned int)((float)(1 << LFO_SHIFT)*step);
if (ALFO)
{