mirror of
https://github.com/ZDoom/gzdoom-gles.git
synced 2024-12-01 00:21:35 +00:00
- Since our OPL chips are emulated, we can give them a feature that real OPL2 chips lacked:
Full stereo panning for the MIDI player. (The raw OPL players are unaffected.) To get the mono output back, you can set opl_stereo to false. - Changed SoftSynthMIDIDevice::OpenStream() to allocate the same number of samples for stereo and mono streams. SVN r3929 (trunk)
This commit is contained in:
parent
1b5bff9603
commit
3b0ad55285
9 changed files with 95 additions and 18 deletions
|
@ -115,6 +115,8 @@ Revision History:
|
||||||
#define INLINE __inline
|
#define INLINE __inline
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#define CENTER_PANNING_POWER 0.70710678118 /* [RH] volume at center for EQP */
|
||||||
|
#define HALF_PI (PI/2)
|
||||||
|
|
||||||
#define FREQ_SH 16 /* 16.16 fixed point (frequency calculations) */
|
#define FREQ_SH 16 /* 16.16 fixed point (frequency calculations) */
|
||||||
#define EG_SH 16 /* 16.16 fixed point (EG timing) */
|
#define EG_SH 16 /* 16.16 fixed point (EG timing) */
|
||||||
|
@ -246,6 +248,8 @@ typedef struct{
|
||||||
UINT32 fc; /* Freq. Increment base */
|
UINT32 fc; /* Freq. Increment base */
|
||||||
UINT32 ksl_base; /* KeyScaleLevel Base step */
|
UINT32 ksl_base; /* KeyScaleLevel Base step */
|
||||||
UINT8 kcode; /* key code (for key scaling) */
|
UINT8 kcode; /* key code (for key scaling) */
|
||||||
|
float LeftVol; /* volumes for stereo panning */
|
||||||
|
float RightVol;
|
||||||
} OPL_CH;
|
} OPL_CH;
|
||||||
|
|
||||||
/* OPL state */
|
/* OPL state */
|
||||||
|
@ -297,6 +301,7 @@ typedef struct fm_opl_f {
|
||||||
int rate; /* sampling rate (Hz) */
|
int rate; /* sampling rate (Hz) */
|
||||||
double freqbase; /* frequency base */
|
double freqbase; /* frequency base */
|
||||||
double TimerBase; /* Timer base time (==sampling time)*/
|
double TimerBase; /* Timer base time (==sampling time)*/
|
||||||
|
bool IsStereo; /* Write stereo output */
|
||||||
} FM_OPL;
|
} FM_OPL;
|
||||||
|
|
||||||
|
|
||||||
|
@ -876,7 +881,7 @@ INLINE signed int op_calc1(UINT32 phase, unsigned int env, signed int pm, unsign
|
||||||
#define volume_calc(OP) ((OP)->TLL + ((UINT32)(OP)->volume) + (LFO_AM & (OP)->AMmask))
|
#define volume_calc(OP) ((OP)->TLL + ((UINT32)(OP)->volume) + (LFO_AM & (OP)->AMmask))
|
||||||
|
|
||||||
/* calculate output */
|
/* calculate output */
|
||||||
INLINE void OPL_CALC_CH( OPL_CH *CH, float *buffer )
|
INLINE float OPL_CALC_CH( OPL_CH *CH )
|
||||||
{
|
{
|
||||||
OPL_SLOT *SLOT;
|
OPL_SLOT *SLOT;
|
||||||
unsigned int env;
|
unsigned int env;
|
||||||
|
@ -905,8 +910,9 @@ INLINE void OPL_CALC_CH( OPL_CH *CH, float *buffer )
|
||||||
{
|
{
|
||||||
output += op_calc(SLOT->Cnt, env, phase_modulation, SLOT->wavetable);
|
output += op_calc(SLOT->Cnt, env, phase_modulation, SLOT->wavetable);
|
||||||
/* [RH] Convert to floating point. */
|
/* [RH] Convert to floating point. */
|
||||||
*buffer += float(output) / 10240;
|
return float(output) / 10240;
|
||||||
}
|
}
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -1282,6 +1288,13 @@ static void OPL_initalize(FM_OPL *OPL)
|
||||||
OPL->eg_timer_overflow = UINT32(( 1 ) * (1<<EG_SH));
|
OPL->eg_timer_overflow = UINT32(( 1 ) * (1<<EG_SH));
|
||||||
/*logerror("OPLinit eg_timer_add=%8x eg_timer_overflow=%8x\n", OPL->eg_timer_add, OPL->eg_timer_overflow);*/
|
/*logerror("OPLinit eg_timer_add=%8x eg_timer_overflow=%8x\n", OPL->eg_timer_add, OPL->eg_timer_overflow);*/
|
||||||
|
|
||||||
|
// [RH] Support full MIDI panning. (But default to mono and center panning.)
|
||||||
|
OPL->IsStereo = false;
|
||||||
|
for (int i = 0; i < 9; ++i)
|
||||||
|
{
|
||||||
|
OPL->P_CH[i].LeftVol = (float)CENTER_PANNING_POWER;
|
||||||
|
OPL->P_CH[i].RightVol = (float)CENTER_PANNING_POWER;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
INLINE void FM_KEYON(OPL_SLOT *SLOT, UINT32 key_set)
|
INLINE void FM_KEYON(OPL_SLOT *SLOT, UINT32 key_set)
|
||||||
|
@ -1897,6 +1910,28 @@ void YM3812SetUpdateHandler(void *chip,OPL_UPDATEHANDLER UpdateHandler,int param
|
||||||
OPLSetUpdateHandler(YM3812, UpdateHandler, param);
|
OPLSetUpdateHandler(YM3812, UpdateHandler, param);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* [RH] Full support for MIDI panning */
|
||||||
|
void YM3812SetStereo(void *chip, bool stereo)
|
||||||
|
{
|
||||||
|
if (chip != NULL)
|
||||||
|
{
|
||||||
|
FM_OPL *YM3812 = (FM_OPL *)chip;
|
||||||
|
YM3812->IsStereo = stereo;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void YM3812SetPanning(void *chip, int c, int pan)
|
||||||
|
{
|
||||||
|
if (chip != NULL)
|
||||||
|
{
|
||||||
|
FM_OPL *YM3812 = (FM_OPL *)chip;
|
||||||
|
// This is the MIDI-recommended pan formula. 0 and 1 are
|
||||||
|
// both hard left so that 64 can be perfectly center.
|
||||||
|
double level = (pan <= 1) ? 0 : (pan - 1) / 126.0;
|
||||||
|
YM3812->P_CH[c].LeftVol = (float)cos(HALF_PI * level);
|
||||||
|
YM3812->P_CH[c].RightVol = (float)sin(HALF_PI * level);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
** Generate samples for one of the YM3812's
|
** Generate samples for one of the YM3812's
|
||||||
|
@ -1969,7 +2004,16 @@ static bool CalcVoice (FM_OPL *OPL, int voice, float *buffer, int length)
|
||||||
advance_lfo(OPL);
|
advance_lfo(OPL);
|
||||||
|
|
||||||
output = 0;
|
output = 0;
|
||||||
OPL_CALC_CH(CH, buffer + i);
|
float sample = OPL_CALC_CH(CH);
|
||||||
|
if (!OPL->IsStereo)
|
||||||
|
{
|
||||||
|
buffer[i] += sample;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
buffer[i*2] += sample * CH->LeftVol;
|
||||||
|
buffer[i*2+1] += sample * CH->RightVol;
|
||||||
|
}
|
||||||
|
|
||||||
advance(OPL, voice, voice);
|
advance(OPL, voice, voice);
|
||||||
}
|
}
|
||||||
|
@ -1987,7 +2031,18 @@ static bool CalcRhythm (FM_OPL *OPL, float *buffer, int length)
|
||||||
output = 0;
|
output = 0;
|
||||||
OPL_CALC_RH(&OPL->P_CH[0], OPL->noise_rng & 1);
|
OPL_CALC_RH(&OPL->P_CH[0], OPL->noise_rng & 1);
|
||||||
/* [RH] Convert to floating point. */
|
/* [RH] Convert to floating point. */
|
||||||
buffer[i] += float(output) / 10240;
|
float sample = float(output) / 10240;
|
||||||
|
if (!OPL->IsStereo)
|
||||||
|
{
|
||||||
|
buffer[i] += sample;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// [RH] Always use center panning for rhythm.
|
||||||
|
// The MIDI player doesn't use the rhythm section anyway.
|
||||||
|
buffer[i*2] += sample * CENTER_PANNING_POWER;
|
||||||
|
buffer[i*2+1] += sample * CENTER_PANNING_POWER;
|
||||||
|
}
|
||||||
|
|
||||||
advance(OPL, 6, 8);
|
advance(OPL, 6, 8);
|
||||||
advance_noise(OPL);
|
advance_noise(OPL);
|
||||||
|
|
|
@ -32,6 +32,8 @@ int YM3812Write(void *chip, int a, int v);
|
||||||
unsigned char YM3812Read(void *chip, int a);
|
unsigned char YM3812Read(void *chip, int a);
|
||||||
int YM3812TimerOver(void *chip, int c);
|
int YM3812TimerOver(void *chip, int c);
|
||||||
void YM3812UpdateOne(void *chip, float *buffer, int length);
|
void YM3812UpdateOne(void *chip, float *buffer, int length);
|
||||||
|
void YM3812SetStereo(void *chip, bool stereo);
|
||||||
|
void YM3812SetPanning(void *chip, int c, int pan);
|
||||||
|
|
||||||
void YM3812SetTimerHandler(void *chip, OPL_TIMERHANDLER TimerHandler, int channelOffset);
|
void YM3812SetTimerHandler(void *chip, OPL_TIMERHANDLER TimerHandler, int channelOffset);
|
||||||
void YM3812SetIRQHandler(void *chip, OPL_IRQHANDLER IRQHandler, int param);
|
void YM3812SetIRQHandler(void *chip, OPL_IRQHANDLER IRQHandler, int param);
|
||||||
|
|
|
@ -252,6 +252,12 @@ void OPLio::OPLwritePan(uint channel, struct OPL2instrument *instr, int pan)
|
||||||
else bits = 0x30; // both
|
else bits = 0x30; // both
|
||||||
|
|
||||||
OPLwriteValue(0xC0, channel, instr->feedback | bits);
|
OPLwriteValue(0xC0, channel, instr->feedback | bits);
|
||||||
|
|
||||||
|
// Set real panning if we're using emulated chips.
|
||||||
|
if (chips[0] != NULL)
|
||||||
|
{
|
||||||
|
YM3812SetPanning(chips[channel/9], channel%9, pan+64);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -298,13 +304,14 @@ void OPLio::OPLshutup(void)
|
||||||
/*
|
/*
|
||||||
* Initialize hardware upon startup
|
* Initialize hardware upon startup
|
||||||
*/
|
*/
|
||||||
int OPLio::OPLinit(uint numchips)
|
int OPLio::OPLinit(uint numchips, bool stereo)
|
||||||
{
|
{
|
||||||
assert(numchips >= 1 && numchips <= 2);
|
assert(numchips >= 1 && numchips <= 2);
|
||||||
chips[0] = YM3812Init (3579545, int(OPL_SAMPLE_RATE));
|
chips[0] = YM3812Init (3579545, int(OPL_SAMPLE_RATE));
|
||||||
chips[1] = NULL;
|
chips[1] = NULL;
|
||||||
if (chips[0] != NULL)
|
if (chips[0] != NULL)
|
||||||
{
|
{
|
||||||
|
YM3812SetStereo(chips[0], stereo);
|
||||||
if (numchips > 1)
|
if (numchips > 1)
|
||||||
{
|
{
|
||||||
chips[1] = YM3812Init (3579545, int(OPL_SAMPLE_RATE));
|
chips[1] = YM3812Init (3579545, int(OPL_SAMPLE_RATE));
|
||||||
|
@ -314,6 +321,10 @@ int OPLio::OPLinit(uint numchips)
|
||||||
chips[0] = NULL;
|
chips[0] = NULL;
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
YM3812SetStereo(chips[1], stereo);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|
|
@ -64,6 +64,8 @@
|
||||||
|
|
||||||
// PUBLIC DATA DEFINITIONS -------------------------------------------------
|
// PUBLIC DATA DEFINITIONS -------------------------------------------------
|
||||||
|
|
||||||
|
CVAR(Bool, opl_stereo, true, CVAR_ARCHIVE);
|
||||||
|
|
||||||
// CODE --------------------------------------------------------------------
|
// CODE --------------------------------------------------------------------
|
||||||
|
|
||||||
//==========================================================================
|
//==========================================================================
|
||||||
|
@ -74,6 +76,7 @@
|
||||||
|
|
||||||
OPLMIDIDevice::OPLMIDIDevice()
|
OPLMIDIDevice::OPLMIDIDevice()
|
||||||
{
|
{
|
||||||
|
IsStereo = opl_stereo;
|
||||||
FWadLump data = Wads.OpenLumpName("GENMIDI");
|
FWadLump data = Wads.OpenLumpName("GENMIDI");
|
||||||
OPLloadBank(data);
|
OPLloadBank(data);
|
||||||
SampleRate = (int)OPL_SAMPLE_RATE;
|
SampleRate = (int)OPL_SAMPLE_RATE;
|
||||||
|
@ -89,11 +92,11 @@ OPLMIDIDevice::OPLMIDIDevice()
|
||||||
|
|
||||||
int OPLMIDIDevice::Open(void (*callback)(unsigned int, void *, DWORD, DWORD), void *userdata)
|
int OPLMIDIDevice::Open(void (*callback)(unsigned int, void *, DWORD, DWORD), void *userdata)
|
||||||
{
|
{
|
||||||
if (io == NULL || io->OPLinit(TwoChips + 1))
|
if (io == NULL || io->OPLinit(TwoChips + 1, IsStereo))
|
||||||
{
|
{
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
int ret = OpenStream(14, SoundStream::Mono, callback, userdata);
|
int ret = OpenStream(14, IsStereo ? 0 : SoundStream::Mono, callback, userdata);
|
||||||
if (ret == 0)
|
if (ret == 0)
|
||||||
{
|
{
|
||||||
OPLstopMusic();
|
OPLstopMusic();
|
||||||
|
|
|
@ -177,7 +177,7 @@ struct OPLio {
|
||||||
void OPLshutup(void);
|
void OPLshutup(void);
|
||||||
void OPLwriteInitState();
|
void OPLwriteInitState();
|
||||||
|
|
||||||
virtual int OPLinit(uint numchips);
|
virtual int OPLinit(uint numchips, bool stereo=false);
|
||||||
virtual void OPLdeinit(void);
|
virtual void OPLdeinit(void);
|
||||||
virtual void OPLwriteReg(int which, uint reg, uchar data);
|
virtual void OPLwriteReg(int which, uint reg, uchar data);
|
||||||
virtual void SetClockRate(double samples_per_tick);
|
virtual void SetClockRate(double samples_per_tick);
|
||||||
|
@ -192,7 +192,7 @@ struct DiskWriterIO : public OPLio
|
||||||
DiskWriterIO(const char *filename);
|
DiskWriterIO(const char *filename);
|
||||||
~DiskWriterIO();
|
~DiskWriterIO();
|
||||||
|
|
||||||
int OPLinit(uint numchips);
|
int OPLinit(uint numchips, bool notused=false);
|
||||||
void OPLdeinit();
|
void OPLdeinit();
|
||||||
void OPLwriteReg(int which, uint reg, uchar data);
|
void OPLwriteReg(int which, uint reg, uchar data);
|
||||||
void SetClockRate(double samples_per_tick);
|
void SetClockRate(double samples_per_tick);
|
||||||
|
|
|
@ -25,6 +25,7 @@ OPLmusicBlock::OPLmusicBlock()
|
||||||
LastOffset = 0;
|
LastOffset = 0;
|
||||||
TwoChips = !opl_onechip;
|
TwoChips = !opl_onechip;
|
||||||
Looping = false;
|
Looping = false;
|
||||||
|
IsStereo = false;
|
||||||
io = NULL;
|
io = NULL;
|
||||||
io = new OPLio;
|
io = new OPLio;
|
||||||
}
|
}
|
||||||
|
@ -39,7 +40,7 @@ void OPLmusicBlock::ResetChips ()
|
||||||
TwoChips = !opl_onechip;
|
TwoChips = !opl_onechip;
|
||||||
ChipAccess.Enter();
|
ChipAccess.Enter();
|
||||||
io->OPLdeinit ();
|
io->OPLdeinit ();
|
||||||
io->OPLinit (TwoChips + 1);
|
io->OPLinit (TwoChips + 1, IsStereo);
|
||||||
ChipAccess.Leave();
|
ChipAccess.Leave();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -222,13 +223,11 @@ void OPLmusicFile::Restart ()
|
||||||
|
|
||||||
bool OPLmusicBlock::ServiceStream (void *buff, int numbytes)
|
bool OPLmusicBlock::ServiceStream (void *buff, int numbytes)
|
||||||
{
|
{
|
||||||
float *samples = (float *)buff;
|
float *samples1 = (float *)buff;
|
||||||
float *samples1;
|
int numsamples = numbytes / (sizeof(float) << int(IsStereo));
|
||||||
int numsamples = numbytes / sizeof(float);
|
|
||||||
bool prevEnded = false;
|
bool prevEnded = false;
|
||||||
bool res = true;
|
bool res = true;
|
||||||
|
|
||||||
samples1 = samples;
|
|
||||||
memset(buff, 0, numbytes);
|
memset(buff, 0, numbytes);
|
||||||
|
|
||||||
ChipAccess.Enter();
|
ChipAccess.Enter();
|
||||||
|
@ -242,12 +241,12 @@ bool OPLmusicBlock::ServiceStream (void *buff, int numbytes)
|
||||||
{
|
{
|
||||||
YM3812UpdateOne (io->chips[0], samples1, samplesleft);
|
YM3812UpdateOne (io->chips[0], samples1, samplesleft);
|
||||||
YM3812UpdateOne (io->chips[1], samples1, samplesleft);
|
YM3812UpdateOne (io->chips[1], samples1, samplesleft);
|
||||||
OffsetSamples(samples1, samplesleft);
|
OffsetSamples(samples1, samplesleft << int(IsStereo));
|
||||||
assert(NextTickIn == ticky);
|
assert(NextTickIn == ticky);
|
||||||
NextTickIn -= samplesleft;
|
NextTickIn -= samplesleft;
|
||||||
assert (NextTickIn >= 0);
|
assert (NextTickIn >= 0);
|
||||||
numsamples -= samplesleft;
|
numsamples -= samplesleft;
|
||||||
samples1 += samplesleft;
|
samples1 += samplesleft << int(IsStereo);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (NextTickIn < 1)
|
if (NextTickIn < 1)
|
||||||
|
@ -262,7 +261,7 @@ bool OPLmusicBlock::ServiceStream (void *buff, int numbytes)
|
||||||
{
|
{
|
||||||
YM3812UpdateOne (io->chips[0], samples1, numsamples);
|
YM3812UpdateOne (io->chips[0], samples1, numsamples);
|
||||||
YM3812UpdateOne (io->chips[1], samples1, numsamples);
|
YM3812UpdateOne (io->chips[1], samples1, numsamples);
|
||||||
OffsetSamples(samples1, numsamples);
|
OffsetSamples(samples1, numsamples << int(IsStereo));
|
||||||
}
|
}
|
||||||
res = false;
|
res = false;
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -21,6 +21,7 @@ protected:
|
||||||
bool TwoChips;
|
bool TwoChips;
|
||||||
bool Looping;
|
bool Looping;
|
||||||
double LastOffset;
|
double LastOffset;
|
||||||
|
bool IsStereo;
|
||||||
|
|
||||||
FCriticalSection ChipAccess;
|
FCriticalSection ChipAccess;
|
||||||
};
|
};
|
||||||
|
|
|
@ -96,7 +96,12 @@ SoftSynthMIDIDevice::~SoftSynthMIDIDevice()
|
||||||
|
|
||||||
int SoftSynthMIDIDevice::OpenStream(int chunks, int flags, void (*callback)(unsigned int, void *, DWORD, DWORD), void *userdata)
|
int SoftSynthMIDIDevice::OpenStream(int chunks, int flags, void (*callback)(unsigned int, void *, DWORD, DWORD), void *userdata)
|
||||||
{
|
{
|
||||||
Stream = GSnd->CreateStream(FillStream, (SampleRate / chunks) * 4, SoundStream::Float | flags, SampleRate, this);
|
int chunksize = (SampleRate / chunks) * 4;
|
||||||
|
if (!(flags & SoundStream::Mono))
|
||||||
|
{
|
||||||
|
chunksize *= 2;
|
||||||
|
}
|
||||||
|
Stream = GSnd->CreateStream(FillStream, chunksize, SoundStream::Float | flags, SampleRate, this);
|
||||||
if (Stream == NULL)
|
if (Stream == NULL)
|
||||||
{
|
{
|
||||||
return 2;
|
return 2;
|
||||||
|
|
|
@ -1409,6 +1409,7 @@ OptionMenu AdvSoundOptions
|
||||||
StaticText " "
|
StaticText " "
|
||||||
StaticText "OPL Synthesis", 1
|
StaticText "OPL Synthesis", 1
|
||||||
Option "Only emulate one OPL chip", "opl_onechip", "OnOff"
|
Option "Only emulate one OPL chip", "opl_onechip", "OnOff"
|
||||||
|
Option "Support MIDI stero panning", "opl_stereo", "OnOff"
|
||||||
StaticText " "
|
StaticText " "
|
||||||
StaticText "GUS Emulation", 1
|
StaticText "GUS Emulation", 1
|
||||||
Slider "MIDI voices", "midi_voices", 16, 256, 4, 0
|
Slider "MIDI voices", "midi_voices", 16, 256, 4, 0
|
||||||
|
|
Loading…
Reference in a new issue