mirror of
https://github.com/ZDoom/qzdoom.git
synced 2024-11-10 23:02:08 +00:00
- 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:
parent
938ae1767b
commit
8f49ea7f48
9 changed files with 37 additions and 202 deletions
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -30,9 +30,6 @@ protected:
|
|||
bool TwoChips;
|
||||
bool Looping;
|
||||
|
||||
int *SampleBuff;
|
||||
int SampleBuffSize;
|
||||
|
||||
#ifdef _WIN32
|
||||
CRITICAL_SECTION ChipAccess;
|
||||
#else
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -49,8 +49,6 @@ public:
|
|||
void ResetEnvironment ();
|
||||
|
||||
private:
|
||||
unsigned int DriverCaps;
|
||||
int OutputType;
|
||||
bool SFXPaused;
|
||||
bool InitSuccess;
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue