- Changed the output of the OPL emulator from 32-bit integers to 32-bit

floats, so I can write its output directly to the stream buffer. In
  addition, this lets me bring the OPL volume level much closer to the
  standard MIDI volume.


SVN r863 (trunk)
This commit is contained in:
Randy Heit 2008-03-29 01:30:37 +00:00
parent 938ae1767b
commit 8f49ea7f48
9 changed files with 37 additions and 202 deletions

View file

@ -583,7 +583,7 @@ static signed int output[1];
static UINT32 LFO_AM;
static INT32 LFO_PM;
static bool CalcVoice (FM_OPL *OPL, int voice, INT32 *buffer, int length);
static bool CalcVoice (FM_OPL *OPL, int voice, float *buffer, int length);
@ -782,70 +782,8 @@ INLINE void advance(FM_OPL *OPL)
}
INLINE signed int op_calc(UINT32 phase, unsigned int env, signed int pm, unsigned int wave_tab)
{
UINT32 p;
p = (env<<4) + sin_tab[wave_tab + ((((signed int)((phase & ~FREQ_MASK) + (pm<<16))) >> FREQ_SH ) & SIN_MASK) ];
if (p >= TL_TAB_LEN)
return 0;
return tl_tab[p];
}
INLINE signed int op_calc1(UINT32 phase, unsigned int env, signed int pm, unsigned int wave_tab)
{
UINT32 p;
INT32 i;
i = (phase & ~FREQ_MASK) + pm;
/*logerror("i=%08x (i>>16)&511=%8i phase=%i [pm=%08x] ",i, (i>>16)&511, phase>>FREQ_SH, pm);*/
p = (env<<4) + sin_tab[ wave_tab + ((i>>FREQ_SH) & SIN_MASK)];
/*logerror("(p&255=%i p>>8=%i) out= %i\n", p&255,p>>8, tl_tab[p&255]>>(p>>8) );*/
if (p >= TL_TAB_LEN)
return 0;
return tl_tab[p];
}
#define volume_calc(OP) ((OP)->TLL + ((UINT32)(OP)->volume) + (LFO_AM & (OP)->AMmask))
/* calculate output */
INLINE void OPL_CALC_CH( OPL_CH *CH )
{
OPL_SLOT *SLOT;
unsigned int env;
signed int out;
phase_modulation = 0;
/* SLOT 1 */
SLOT = &CH->SLOT[SLOT1];
env = volume_calc(SLOT);
out = SLOT->op1_out[0] + SLOT->op1_out[1];
SLOT->op1_out[0] = SLOT->op1_out[1];
*SLOT->connect1 += SLOT->op1_out[0];
SLOT->op1_out[1] = 0;
if( env < ENV_QUIET )
{
if (!SLOT->FB)
out = 0;
SLOT->op1_out[1] = op_calc1(SLOT->Cnt, env, (out<<SLOT->FB), SLOT->wavetable );
}
/* SLOT 2 */
SLOT++;
env = volume_calc(SLOT);
if( env < ENV_QUIET )
{
output[0] += op_calc(SLOT->Cnt, env, phase_modulation, SLOT->wavetable);
}
}
/* generic table initialize */
static int init_tables(void)
{
@ -1608,33 +1546,15 @@ void YM3812SetUpdateHandler(int which,OPL_UPDATEHANDLER UpdateHandler,int param)
** '*buffer' is the output buffer pointer
** 'length' is the number of samples that should be generated
*/
void YM3812UpdateOne(int which, INT32 *buffer, int length)
void YM3812UpdateOne(int which, float *buffer, int length)
{
FM_OPL *OPL = OPL_YM3812[which];
INT32 *buf = buffer;
int i;
if( (void *)OPL != cur_chip )
{
cur_chip = (void *)OPL;
}
#if 0
for( i=0; i < length ; i++ )
{
output[0] = 0;
advance_lfo(OPL);
/* FM part */
for (int j = 0; j <= 8; ++j)
{
OPL_CALC_CH(&OPL->P_CH[j]);
}
// [RH] Just add to the output. Clamping will happen later.
buf[i] += output[0];
advance(OPL);
}
#else
UINT32 lfo_am_cnt_bak = OPL->lfo_am_cnt;
UINT32 eg_timer_bak = OPL->eg_timer;
UINT32 eg_cnt_bak = OPL->eg_cnt;
@ -1648,7 +1568,7 @@ void YM3812UpdateOne(int which, INT32 *buffer, int length)
OPL->lfo_am_cnt = lfo_am_cnt_bak;
OPL->eg_timer = eg_timer_bak;
OPL->eg_cnt = eg_cnt_bak;
if (CalcVoice (OPL, i, buf, length))
if (CalcVoice (OPL, i, buffer, length))
{
lfo_am_cnt_out = OPL->lfo_am_cnt;
eg_timer_out = OPL->eg_timer;
@ -1659,13 +1579,12 @@ void YM3812UpdateOne(int which, INT32 *buffer, int length)
OPL->lfo_am_cnt = lfo_am_cnt_out;
OPL->eg_timer = eg_timer_out;
OPL->eg_cnt = eg_cnt_out;
#endif
}
// [RH] Render a whole voice at once. If nothing else, it lets us avoid
// wasting a lot of time on voices that aren't playing anything.
static bool CalcVoice (FM_OPL *OPL, int voice, INT32 *buffer, int length)
static bool CalcVoice (FM_OPL *OPL, int voice, float *buffer, int length)
{
OPL_CH *const CH = &OPL->P_CH[voice];
int i, j;
@ -1729,8 +1648,8 @@ static bool CalcVoice (FM_OPL *OPL, int voice, INT32 *buffer, int length)
{
output[0] += tl_tab[p];
}
// [RH] make the output louder.
buffer[i] += (output[0] + (output[0]>>1));
// [RH] Convert to floating point.
buffer[i] += float(output[0]) / 7168.f;
}
// advance

View file

@ -38,7 +38,7 @@ void YM3812ResetChip(int which);
int YM3812Write(int which, int a, int v);
unsigned char YM3812Read(int which, int a);
int YM3812TimerOver(int which, int c);
void YM3812UpdateOne(int which, INT32 *buffer, int length);
void YM3812UpdateOne(int which, float *buffer, int length);
void YM3812SetTimerHandler(int which, OPL_TIMERHANDLER TimerHandler, int channelOffset);
void YM3812SetIRQHandler(int which, OPL_IRQHANDLER IRQHandler, int param);

View file

@ -24,8 +24,6 @@ static OPLmusicBlock *BlockForStats;
OPLmusicBlock::OPLmusicBlock()
{
scoredata = NULL;
SampleBuff = NULL;
SampleBuffSize = 0;
NextTickIn = 0;
TwoChips = !opl_onechip;
Looping = false;
@ -45,11 +43,6 @@ OPLmusicBlock::OPLmusicBlock()
OPLmusicBlock::~OPLmusicBlock()
{
BlockForStats = NULL;
if (SampleBuff != NULL)
{
delete[] SampleBuff;
SampleBuff = NULL;
}
#ifdef _WIN32
DeleteCriticalSection (&ChipAccess);
#else
@ -227,23 +220,14 @@ void OPLmusicFile::Restart ()
bool OPLmusicBlock::ServiceStream (void *buff, int numbytes)
{
short *samples = (short *)buff;
int *samples1;
int numsamples = numbytes / 2;
float *samples = (float *)buff;
float *samples1;
int numsamples = numbytes / sizeof(float);
bool prevEnded = false;
bool res = true;
// SampleBuff is allocated here, since FMOD might not give us exactly
// the buffer size we requested. (Actually, it's giving us twice what
// was asked for. Am I doing something wrong?)
numbytes = numsamples * sizeof(int);
if (SampleBuff == NULL || numbytes > SampleBuffSize)
{
SampleBuff = (int *)M_Realloc(SampleBuff, numbytes);
SampleBuffSize = numbytes;
}
memset(SampleBuff, 0, numbytes);
samples1 = SampleBuff;
samples1 = samples;
memset(buff, 0, numbytes);
#ifdef _WIN32
EnterCriticalSection (&ChipAccess);
@ -307,86 +291,6 @@ bool OPLmusicBlock::ServiceStream (void *buff, int numbytes)
#else
SDL_mutexV (ChipAccess);
#endif
numsamples = numbytes / sizeof(int);
samples1 = SampleBuff;
#if defined(_MSC_VER) && defined(USEASM)
if (CPU.bCMOV && numsamples > 1)
{
__asm
{
mov ecx, numsamples
mov esi, samples1
mov edi, samples
shr ecx, 1
lea esi, [esi+ecx*8]
lea edi, [edi+ecx*4]
neg ecx
mov edx, 0x00007fff
looper: mov eax, [esi+ecx*8]
mov ebx, [esi+ecx*8+4]
// Shift the samples down to reduce the chance of clipping at the high end.
// Since most of the waveforms we produce only use the upper half of the
// sine wave, this is a good thing. Although it does leave less room at
// the bottom of the sine before clipping, almost none of the songs I tested
// went below -9000.
sub eax, 18000
sub ebx, 18000
// Clamp high
cmp eax, edx
cmovg eax, edx
cmp ebx, edx
cmovg ebx, edx
// Clamp low
not edx
cmp eax, edx
cmovl eax, edx
cmp ebx, edx
cmovl ebx, edx
not edx
mov [edi+ecx*4], ax
mov [edi+ecx*4+2], bx
inc ecx
jnz looper
test numsamples, 1
jz done
mov eax, [esi+ecx*8]
sub eax, 18000
cmp eax, edx
cmovg eax, edx
not edx
cmp eax, edx
cmovl eax, edx
mov [edi+ecx*2], ax
done:
}
}
else
#endif
{
for (int i = 1; i < numsamples; i+=2)
{
int u = samples1[i-1] - 18000;
int v = samples1[i ] - 18000;
if (u < -32768) u = -32768; else if (u > 32767) u = 32767;
if (v < -32768) v = -32768; else if (v > 32767) v = 32767;
samples[i-1] = u;
samples[i ] = v;
}
if (numsamples & 1)
{
int v = samples1[numsamples-1] - 18000;
if (v < -32768) v = -32768; else if (v > 32767) v = 32767;
samples[numsamples-1] = v;
}
}
return res;
}

View file

@ -30,9 +30,6 @@ protected:
bool TwoChips;
bool Looping;
int *SampleBuff;
int SampleBuffSize;
#ifdef _WIN32
CRITICAL_SECTION ChipAccess;
#else

View file

@ -945,7 +945,7 @@ SoundStream *FMODSoundRenderer::CreateStream (SoundStreamCallback callback, int
capsule = new FMODStreamCapsule (userdata, callback, this);
mode = FMOD_2D | FMOD_OPENUSER | FMOD_LOOP_NORMAL | FMOD_SOFTWARE | FMOD_CREATESTREAM | FMOD_OPENONLY;
sample_shift = (flags & SoundStream::Bits8) ? 0 : 1;
sample_shift = (flags & (SoundStream::Bits32 | SoundStream::Float)) ? 2 : (flags & SoundStream::Bits8) ? 0 : 1;
channel_shift = (flags & SoundStream::Mono) ? 0 : 1;
// Chunk size of stream update in samples. This will be the amount of data
@ -963,7 +963,22 @@ SoundStream *FMODSoundRenderer::CreateStream (SoundStreamCallback callback, int
exinfo.defaultfrequency = samplerate;
// Data format of sound.
exinfo.format = (flags & SoundStream::Bits8) ? FMOD_SOUND_FORMAT_PCM8 : FMOD_SOUND_FORMAT_PCM16;
if (flags & SoundStream::Float)
{
exinfo.format = FMOD_SOUND_FORMAT_PCMFLOAT;
}
else if (flags & SoundStream::Bits32)
{
exinfo.format = FMOD_SOUND_FORMAT_PCM32;
}
else if (flags & SoundStream::Bits8)
{
exinfo.format = FMOD_SOUND_FORMAT_PCM8;
}
else
{
exinfo.format = FMOD_SOUND_FORMAT_PCM16;
}
// User callback for reading.
exinfo.pcmreadcallback = FMODStreamCapsule::PCMReadCallback;

View file

@ -49,8 +49,6 @@ public:
void ResetEnvironment ();
private:
unsigned int DriverCaps;
int OutputType;
bool SFXPaused;
bool InitSuccess;

View file

@ -46,9 +46,11 @@ public:
{ // For CreateStream
Mono = 1,
Bits8 = 2,
Bits32 = 4,
Float = 8,
// For OpenStream
Loop = 4
Loop = 16
};
virtual bool Play (bool looping, float volume, bool normalize) = 0;

View file

@ -19,8 +19,8 @@ OPLMUSSong::OPLMUSSong (FILE *file, char *musiccache, int len)
Music = new OPLmusicFile (file, musiccache, len, samples);
m_Stream = GSnd->CreateStream (FillStream, samples*2,
SoundStream::Mono, int(OPL_SAMPLE_RATE), this);
m_Stream = GSnd->CreateStream (FillStream, samples*4,
SoundStream::Mono | SoundStream::Float, int(OPL_SAMPLE_RATE), this);
if (m_Stream == NULL)
{
Printf (PRINT_BOLD, "Could not create music stream.\n");
@ -63,7 +63,7 @@ void OPLMUSSong::Play (bool looping)
Music->SetLooping (looping);
Music->Restart ();
if (m_Stream->Play (true, snd_musicvolume, true))
if (m_Stream->Play (true, snd_musicvolume, false))
{
m_Status = STATE_Playing;
}

View file

@ -1,6 +1,6 @@
/*
** music_mididevice.cpp
** Provides a WinMM implementation of a generic MIDI output device.
** music_win_mididevice.cpp
** Provides a WinMM implementation of a MIDI output device.
**
**---------------------------------------------------------------------------
** Copyright 2008 Randy Heit