- Optimize the Java-based OPL3 emulator some by not calling expensive math functions liberally during the rendering loop.

SVN r3968 (trunk)
This commit is contained in:
Randy Heit 2012-11-16 06:27:03 +00:00
parent e59d7bc8b8
commit 93bd380fee
4 changed files with 174 additions and 113 deletions

View file

@ -49,11 +49,24 @@
#include "doomtype.h" #include "doomtype.h"
#include "opl.h" #include "opl.h"
#include "m_random.h" #include "m_random.h"
#include "xs_Float.h"
static FRandom pr_opl3; static FRandom pr_opl3;
#define VOLUME_MUL 0.25
class Operator; class Operator;
static inline double StripIntPart(double num)
{
#if 0
double dontcare;
return modf(num, &dontcare);
#else
return num - xs_RoundToInt(num);
#endif
}
// //
// Channels // Channels
// //
@ -64,7 +77,7 @@ class Channel
protected: protected:
double feedback[2]; double feedback[2];
int fnuml, fnumh, kon, block, cha, chb, chc, chd, fb, cnt; int fnuml, fnumh, kon, block, fb, cha, chb, cnt;
// Factor to convert between normalized amplitude to normalized // Factor to convert between normalized amplitude to normalized
// radians. The amplitude maximum is equivalent to 8*Pi radians. // radians. The amplitude maximum is equivalent to 8*Pi radians.
@ -73,19 +86,19 @@ protected:
public: public:
int channelBaseAddress; int channelBaseAddress;
double leftPan, rightPan;
Channel (int baseAddress); Channel (int baseAddress);
void update_2_KON1_BLOCK3_FNUMH2(class OPL3 *OPL3); void update_2_KON1_BLOCK3_FNUMH2(class OPL3 *OPL3);
void update_FNUML8(class OPL3 *OPL3); void update_FNUML8(class OPL3 *OPL3);
void update_CHD1_CHC1_CHB1_CHA1_FB3_CNT1(class OPL3 *OPL3); void update_CHD1_CHC1_CHB1_CHA1_FB3_CNT1(class OPL3 *OPL3);
void updateChannel(class OPL3 *OPL3); void updateChannel(class OPL3 *OPL3);
virtual void getChannelOutput(class OPL3 *OPL3, double output[4]) = 0; void updatePan(class OPL3 *OPL3);
virtual double getChannelOutput(class OPL3 *OPL3) = 0;
virtual void keyOn() = 0; virtual void keyOn() = 0;
virtual void keyOff() = 0; virtual void keyOff() = 0;
virtual void updateOperators(class OPL3 *OPL3) = 0; virtual void updateOperators(class OPL3 *OPL3) = 0;
protected:
void getInFourChannels(class OPL3 *OPL3, double channelOutput, double output[4]);
}; };
@ -95,7 +108,7 @@ public:
Operator *op1, *op2; Operator *op1, *op2;
Channel2op (int baseAddress, Operator *o1, Operator *o2); Channel2op (int baseAddress, Operator *o1, Operator *o2);
void getChannelOutput(class OPL3 *OPL3, double output[4]); double getChannelOutput(class OPL3 *OPL3);
void keyOn(); void keyOn();
void keyOff(); void keyOff();
@ -109,7 +122,7 @@ public:
Operator *op1, *op2, *op3, *op4; Operator *op1, *op2, *op3, *op4;
Channel4op (int baseAddress, Operator *o1, Operator *o2, Operator *o3, Operator *o4); Channel4op (int baseAddress, Operator *o1, Operator *o2, Operator *o3, Operator *o4);
void getChannelOutput(class OPL3 *OPL3, double output[4]); double getChannelOutput(class OPL3 *OPL3);
void keyOn(); void keyOn();
void keyOff(); void keyOff();
@ -121,7 +134,7 @@ class DisabledChannel : public Channel
{ {
public: public:
DisabledChannel() : Channel(0) { } DisabledChannel() : Channel(0) { }
void getChannelOutput(class OPL3 *OPL3, double output[4]) { return getInFourChannels(OPL3, 0, output); } double getChannelOutput(class OPL3 *OPL3) { return 0; }
void keyOn() { } void keyOn() { }
void keyOff() { } void keyOff() { }
void updateOperators(class OPL3 *OPL3) { } void updateOperators(class OPL3 *OPL3) { }
@ -234,7 +247,7 @@ public:
RhythmChannel(int baseAddress, Operator *o1, Operator *o2) RhythmChannel(int baseAddress, Operator *o1, Operator *o2)
: Channel2op(baseAddress, o1, o2) : Channel2op(baseAddress, o1, o2)
{ } { }
void getChannelOutput(class OPL3 *OPL3, double output[4]); double getChannelOutput(class OPL3 *OPL3);
// Rhythm channels are always running, // Rhythm channels are always running,
// only the envelope is activated by the user. // only the envelope is activated by the user.
@ -296,7 +309,7 @@ class BassDrumChannel : public Channel2op {
public: public:
BassDrumChannel(); BassDrumChannel();
void getChannelOutput(class OPL3 *OPL3, double output[4]); double getChannelOutput(class OPL3 *OPL3);
// Key ON and OFF are unused in rhythm channels. // Key ON and OFF are unused in rhythm channels.
void keyOn() { } void keyOn() { }
@ -319,12 +332,10 @@ public:
_7_NEW1_Offset = 0x105, _7_NEW1_Offset = 0x105,
_2_CONNECTIONSEL6_Offset = 0x104; _2_CONNECTIONSEL6_Offset = 0x104;
#define sampleRate (49700.0)
// The OPL3 tremolo repetition rate is 3.7 Hz. // The OPL3 tremolo repetition rate is 3.7 Hz.
#define tremoloFrequency (3.7) #define tremoloFrequency (3.7)
static const int tremoloTableLength = (int)(sampleRate/tremoloFrequency); static const int tremoloTableLength = (int)(OPL_SAMPLE_RATE/tremoloFrequency);
static const int vibratoTableLength = 8192; static const int vibratoTableLength = 8192;
OPL3Data::OPL3Data() OPL3Data::OPL3Data()
@ -340,7 +351,7 @@ public:
double tremoloTable[2][tremoloTableLength]; double tremoloTable[2][tremoloTableLength];
static double calculateIncrement(double begin, double end, double period) { static double calculateIncrement(double begin, double end, double period) {
return (end-begin)/sampleRate * (1/period); return (end-begin)/OPL_SAMPLE_RATE * (1/period);
} }
private: private:
@ -392,9 +403,24 @@ struct OperatorData
//OPL3 has eight waveforms: //OPL3 has eight waveforms:
double waveforms[8][waveLength]; double waveforms[8][waveLength];
#define MIN_DB (-120.0)
#define DB_TABLE_RES (4.0)
#define DB_TABLE_SIZE (int)(-MIN_DB * DB_TABLE_RES)
double dbpow[DB_TABLE_SIZE];
#define ATTACK_MIN (-5.0)
#define ATTACK_MAX (8.0)
#define ATTACK_RES (0.03125)
#define ATTACK_TABLE_SIZE (int)((ATTACK_MAX - ATTACK_MIN) / ATTACK_RES)
double attackTable[ATTACK_TABLE_SIZE];
OperatorData() OperatorData()
{ {
loadWaveforms(); loadWaveforms();
loaddBPowTable();
loadAttackTable();
} }
static double log2(double x) { static double log2(double x) {
@ -402,6 +428,8 @@ struct OperatorData
} }
private: private:
void loadWaveforms(); void loadWaveforms();
void loaddBPowTable();
void loadAttackTable();
}; };
const float OperatorData::multTable[16] = {0.5,1,2,3,4,5,6,7,8,9,10,10,12,12,15,15}; const float OperatorData::multTable[16] = {0.5,1,2,3,4,5,6,7,8,9,10,10,12,12,15,15};
@ -466,7 +494,7 @@ namespace EnvelopeGeneratorData
{0.00,0.00}, {0.00,0.00}, {0.00,0.00}, {0.00,0.00} {0.00,0.00}, {0.00,0.00}, {0.00,0.00}, {0.00,0.00}
}; };
// These decay and release periods in miliseconds were taken from the YMF278B manual. // These decay and release periods in milliseconds were taken from the YMF278B manual.
// The rate index range from 0 to 63, with different data for // The rate index range from 0 to 63, with different data for
// 0%-100% and for 10%-90%: // 0%-100% and for 10%-90%:
static const double decayAndReleaseTimeValuesTable[64][2] = { static const double decayAndReleaseTimeValuesTable[64][2] = {
@ -534,7 +562,7 @@ public:
// with each frame being four 16-bit samples, // with each frame being four 16-bit samples,
// corresponding to the OPL3 four output channels CHA...CHD. // corresponding to the OPL3 four output channels CHA...CHD.
public: public:
void read(double output[4]); //void read(float output[2]);
void write(int array, int address, int data); void write(int array, int address, int data);
OPL3(); OPL3();
@ -550,6 +578,7 @@ private:
void update_DAM1_DVB1_RYT1_BD1_SD1_TOM1_TC1_HH1(); void update_DAM1_DVB1_RYT1_BD1_SD1_TOM1_TC1_HH1();
void update_7_NEW1(); void update_7_NEW1();
void setEnabledChannels(); void setEnabledChannels();
void updateChannelPans();
void update_2_CONNECTIONSEL6(); void update_2_CONNECTIONSEL6();
void set4opConnections(); void set4opConnections();
void setRhythmMode(); void setRhythmMode();
@ -568,29 +597,30 @@ OperatorData *OPL3::OperatorData;
OPL3Data *OPL3::OPL3Data; OPL3Data *OPL3::OPL3Data;
int OPL3::InstanceCount; int OPL3::InstanceCount;
void OPL3::read(double output[4]) { void OPL3::Update(float *output, int numsamples) {
double channelOutput[4]; while (numsamples--) {
for(int outputChannelNumber=0; outputChannelNumber<4; outputChannelNumber++)
output[outputChannelNumber] = 0;
// If _new = 0, use OPL2 mode with 9 channels. If _new = 1, use OPL3 18 channels; // If _new = 0, use OPL2 mode with 9 channels. If _new = 1, use OPL3 18 channels;
for(int array=0; array < (_new + 1); array++) for(int array=0; array < (_new + 1); array++)
for(int channelNumber=0; channelNumber < 9; channelNumber++) { for(int channelNumber=0; channelNumber < 9; channelNumber++) {
// Reads output from each OPL3 channel, and accumulates it in the output buffer: // Reads output from each OPL3 channel, and accumulates it in the output buffer:
channels[array][channelNumber]->getChannelOutput(this, channelOutput); Channel *channel = channels[array][channelNumber];
for(int outputChannelNumber=0; outputChannelNumber<4; outputChannelNumber++) if (channel != &disabledChannel)
output[outputChannelNumber] += channelOutput[outputChannelNumber]; {
double channelOutput = channel->getChannelOutput(this);
output[0] += float(channelOutput * channel->leftPan);
output[1] += float(channelOutput * channel->rightPan);
}
} }
// Advances the OPL3-wide vibrato index, which is used by // Advances the OPL3-wide vibrato index, which is used by
// PhaseGenerator.getPhase() in each Operator. // PhaseGenerator.getPhase() in each Operator.
vibratoIndex++; vibratoIndex = (vibratoIndex + 1) & (OPL3Data::vibratoTableLength - 1);
if(vibratoIndex >= OPL3Data::vibratoTableLength) vibratoIndex = 0;
// Advances the OPL3-wide tremolo index, which is used by // Advances the OPL3-wide tremolo index, which is used by
// EnvelopeGenerator.getEnvelope() in each Operator. // EnvelopeGenerator.getEnvelope() in each Operator.
tremoloIndex++; tremoloIndex++;
if(tremoloIndex >= OPL3Data::tremoloTableLength) tremoloIndex = 0; if(tremoloIndex >= OPL3Data::tremoloTableLength) tremoloIndex = 0;
output += 2;
}
} }
void OPL3::write(int array, int address, int data) { void OPL3::write(int array, int address, int data) {
@ -854,6 +884,7 @@ void OPL3::update_7_NEW1() {
_new = (_7_new1 & 0x01); _new = (_7_new1 & 0x01);
if(_new==1) setEnabledChannels(); if(_new==1) setEnabledChannels();
set4opConnections(); set4opConnections();
updateChannelPans();
} }
void OPL3::setEnabledChannels() { void OPL3::setEnabledChannels() {
@ -865,6 +896,16 @@ void OPL3::setEnabledChannels() {
} }
} }
void OPL3::updateChannelPans() {
for(int array=0; array<2; array++)
for(int i=0; i<9; i++) {
int baseAddress = channels[array][i]->channelBaseAddress;
registers[baseAddress+ChannelData::CHD1_CHC1_CHB1_CHA1_FB3_CNT1_Offset] |= 0xF0;
channels[array][i]->updatePan(this);
}
}
void OPL3::update_2_CONNECTIONSEL6() { void OPL3::update_2_CONNECTIONSEL6() {
// This method is called only if _new is set. // This method is called only if _new is set.
int _2_connectionsel6 = registers[OPL3Data::_2_CONNECTIONSEL6_Offset]; int _2_connectionsel6 = registers[OPL3Data::_2_CONNECTIONSEL6_Offset];
@ -915,10 +956,22 @@ void OPL3::setRhythmMode() {
for(int i=6; i<=8; i++) channels[0][i]->updateChannel(this); for(int i=6; i<=8; i++) channels[0][i]->updateChannel(this);
} }
static double EnvelopeFromDB(double db)
{
#if 0
return pow(10.0, db/10);
#else
if (db < MIN_DB)
return 0;
return OPL3::OperatorData->dbpow[xs_FloorToInt(-db * DB_TABLE_RES)];
#endif
}
Channel::Channel (int baseAddress) { Channel::Channel (int baseAddress) {
channelBaseAddress = baseAddress; channelBaseAddress = baseAddress;
fnuml = fnumh = kon = block = cha = chb = chc = chd = fb = cnt = 0; fnuml = fnumh = kon = block = fb = cnt = 0;
feedback[0] = feedback[1] = 0; feedback[0] = feedback[1] = 0;
leftPan = rightPan = 1;
} }
void Channel::update_2_KON1_BLOCK3_FNUMH2(OPL3 *OPL3) { void Channel::update_2_KON1_BLOCK3_FNUMH2(OPL3 *OPL3) {
@ -949,32 +1002,35 @@ void Channel::update_FNUML8(OPL3 *OPL3) {
void Channel::update_CHD1_CHC1_CHB1_CHA1_FB3_CNT1(OPL3 *OPL3) { void Channel::update_CHD1_CHC1_CHB1_CHA1_FB3_CNT1(OPL3 *OPL3) {
int chd1_chc1_chb1_cha1_fb3_cnt1 = OPL3->registers[channelBaseAddress+ChannelData::CHD1_CHC1_CHB1_CHA1_FB3_CNT1_Offset]; int chd1_chc1_chb1_cha1_fb3_cnt1 = OPL3->registers[channelBaseAddress+ChannelData::CHD1_CHC1_CHB1_CHA1_FB3_CNT1_Offset];
chd = (chd1_chc1_chb1_cha1_fb3_cnt1 & 0x80) >> 7; // chd = (chd1_chc1_chb1_cha1_fb3_cnt1 & 0x80) >> 7;
chc = (chd1_chc1_chb1_cha1_fb3_cnt1 & 0x40) >> 6; // chc = (chd1_chc1_chb1_cha1_fb3_cnt1 & 0x40) >> 6;
chb = (chd1_chc1_chb1_cha1_fb3_cnt1 & 0x20) >> 5; chb = (chd1_chc1_chb1_cha1_fb3_cnt1 & 0x20) >> 5;
cha = (chd1_chc1_chb1_cha1_fb3_cnt1 & 0x10) >> 4; cha = (chd1_chc1_chb1_cha1_fb3_cnt1 & 0x10) >> 4;
fb = (chd1_chc1_chb1_cha1_fb3_cnt1 & 0x0E) >> 1; fb = (chd1_chc1_chb1_cha1_fb3_cnt1 & 0x0E) >> 1;
cnt = chd1_chc1_chb1_cha1_fb3_cnt1 & 0x01; cnt = chd1_chc1_chb1_cha1_fb3_cnt1 & 0x01;
updatePan(OPL3);
updateOperators(OPL3); updateOperators(OPL3);
} }
void Channel::updatePan(OPL3 *OPL3) {
if (OPL3->_new == 0)
{
leftPan = VOLUME_MUL;
rightPan = VOLUME_MUL;
}
else
{
leftPan = cha * VOLUME_MUL;
rightPan = chb * VOLUME_MUL;
}
}
void Channel::updateChannel(OPL3 *OPL3) { void Channel::updateChannel(OPL3 *OPL3) {
update_2_KON1_BLOCK3_FNUMH2(OPL3); update_2_KON1_BLOCK3_FNUMH2(OPL3);
update_FNUML8(OPL3); update_FNUML8(OPL3);
update_CHD1_CHC1_CHB1_CHA1_FB3_CNT1(OPL3); update_CHD1_CHC1_CHB1_CHA1_FB3_CNT1(OPL3);
} }
void Channel::getInFourChannels(OPL3 *OPL3, double channelOutput, double output[4]) {
if( OPL3->_new==0)
output[0] = output[1] = output[2] = output[3] = channelOutput;
else {
output[0] = (cha==1) ? channelOutput : 0;
output[1] = (chb==1) ? channelOutput : 0;
output[2] = (chc==1) ? channelOutput : 0;
output[3] = (chd==1) ? channelOutput : 0;
}
}
Channel2op::Channel2op (int baseAddress, Operator *o1, Operator *o2) Channel2op::Channel2op (int baseAddress, Operator *o1, Operator *o2)
: Channel(baseAddress) : Channel(baseAddress)
{ {
@ -982,18 +1038,17 @@ Channel2op::Channel2op (int baseAddress, Operator *o1, Operator *o2)
op2 = o2; op2 = o2;
} }
void Channel2op::getChannelOutput(OPL3 *OPL3, double output[4]) { double Channel2op::getChannelOutput(OPL3 *OPL3) {
double channelOutput = 0, op1Output = 0, op2Output = 0; double channelOutput = 0, op1Output = 0, op2Output = 0;
// The feedback uses the last two outputs from // The feedback uses the last two outputs from
// the first operator, instead of just the last one. // the first operator, instead of just the last one.
double feedbackOutput = (feedback[0] + feedback[1]) / 2; double feedbackOutput = (feedback[0] + feedback[1]) / 2;
double dontcare;
switch(cnt) { switch(cnt) {
// CNT = 0, the operators are in series, with the first in feedback. // CNT = 0, the operators are in series, with the first in feedback.
case 0: case 0:
if(op2->envelopeGenerator.stage==EnvelopeGenerator::OFF) if(op2->envelopeGenerator.stage==EnvelopeGenerator::OFF)
return getInFourChannels(OPL3, 0, output); return 0;
op1Output = op1->getOperatorOutput(OPL3, feedbackOutput); op1Output = op1->getOperatorOutput(OPL3, feedbackOutput);
channelOutput = op2->getOperatorOutput(OPL3, op1Output*toPhase); channelOutput = op2->getOperatorOutput(OPL3, op1Output*toPhase);
break; break;
@ -1001,15 +1056,15 @@ void Channel2op::getChannelOutput(OPL3 *OPL3, double output[4]) {
case 1: case 1:
if(op1->envelopeGenerator.stage==EnvelopeGenerator::OFF && if(op1->envelopeGenerator.stage==EnvelopeGenerator::OFF &&
op2->envelopeGenerator.stage==EnvelopeGenerator::OFF) op2->envelopeGenerator.stage==EnvelopeGenerator::OFF)
return getInFourChannels(OPL3, 0, output); return 0;
op1Output = op1->getOperatorOutput(OPL3, feedbackOutput); op1Output = op1->getOperatorOutput(OPL3, feedbackOutput);
op2Output = op2->getOperatorOutput(OPL3, Operator::noModulator); op2Output = op2->getOperatorOutput(OPL3, Operator::noModulator);
channelOutput = (op1Output + op2Output) / 2; channelOutput = (op1Output + op2Output) / 2;
} }
feedback[0] = feedback[1]; feedback[0] = feedback[1];
feedback[1] = modf(op1Output * ChannelData::feedback[fb], &dontcare); feedback[1] = StripIntPart(op1Output * ChannelData::feedback[fb]);
getInFourChannels(OPL3, channelOutput, output); return channelOutput;
} }
void Channel2op::keyOn() { void Channel2op::keyOn() {
@ -1040,10 +1095,9 @@ Channel4op::Channel4op (int baseAddress, Operator *o1, Operator *o2, Operator *o
op4 = o4; op4 = o4;
} }
void Channel4op::getChannelOutput(OPL3 *OPL3, double output[4]) { double Channel4op::getChannelOutput(OPL3 *OPL3) {
double channelOutput = 0, double channelOutput = 0,
op1Output = 0, op2Output = 0, op3Output = 0, op4Output = 0; op1Output = 0, op2Output = 0, op3Output = 0, op4Output = 0;
double dontcare;
int secondChannelBaseAddress = channelBaseAddress+3; int secondChannelBaseAddress = channelBaseAddress+3;
int secondCnt = OPL3->registers[secondChannelBaseAddress+ChannelData::CHD1_CHC1_CHB1_CHA1_FB3_CNT1_Offset] & 0x1; int secondCnt = OPL3->registers[secondChannelBaseAddress+ChannelData::CHD1_CHC1_CHB1_CHA1_FB3_CNT1_Offset] & 0x1;
@ -1054,7 +1108,7 @@ void Channel4op::getChannelOutput(OPL3 *OPL3, double output[4]) {
switch(cnt4op) { switch(cnt4op) {
case 0: case 0:
if(op4->envelopeGenerator.stage==EnvelopeGenerator::OFF) if(op4->envelopeGenerator.stage==EnvelopeGenerator::OFF)
return getInFourChannels(OPL3, 0, output); return 0;
op1Output = op1->getOperatorOutput(OPL3, feedbackOutput); op1Output = op1->getOperatorOutput(OPL3, feedbackOutput);
op2Output = op2->getOperatorOutput(OPL3, op1Output*toPhase); op2Output = op2->getOperatorOutput(OPL3, op1Output*toPhase);
@ -1065,7 +1119,7 @@ void Channel4op::getChannelOutput(OPL3 *OPL3, double output[4]) {
case 1: case 1:
if(op2->envelopeGenerator.stage==EnvelopeGenerator::OFF && if(op2->envelopeGenerator.stage==EnvelopeGenerator::OFF &&
op4->envelopeGenerator.stage==EnvelopeGenerator::OFF) op4->envelopeGenerator.stage==EnvelopeGenerator::OFF)
return getInFourChannels(OPL3, 0, output); return 0;
op1Output = op1->getOperatorOutput(OPL3, feedbackOutput); op1Output = op1->getOperatorOutput(OPL3, feedbackOutput);
op2Output = op2->getOperatorOutput(OPL3, op1Output*toPhase); op2Output = op2->getOperatorOutput(OPL3, op1Output*toPhase);
@ -1078,7 +1132,7 @@ void Channel4op::getChannelOutput(OPL3 *OPL3, double output[4]) {
case 2: case 2:
if(op1->envelopeGenerator.stage==EnvelopeGenerator::OFF && if(op1->envelopeGenerator.stage==EnvelopeGenerator::OFF &&
op4->envelopeGenerator.stage==EnvelopeGenerator::OFF) op4->envelopeGenerator.stage==EnvelopeGenerator::OFF)
return getInFourChannels(OPL3, 0, output); return 0;
op1Output = op1->getOperatorOutput(OPL3, feedbackOutput); op1Output = op1->getOperatorOutput(OPL3, feedbackOutput);
@ -1092,7 +1146,7 @@ void Channel4op::getChannelOutput(OPL3 *OPL3, double output[4]) {
if(op1->envelopeGenerator.stage==EnvelopeGenerator::OFF && if(op1->envelopeGenerator.stage==EnvelopeGenerator::OFF &&
op3->envelopeGenerator.stage==EnvelopeGenerator::OFF && op3->envelopeGenerator.stage==EnvelopeGenerator::OFF &&
op4->envelopeGenerator.stage==EnvelopeGenerator::OFF) op4->envelopeGenerator.stage==EnvelopeGenerator::OFF)
return getInFourChannels(OPL3, 0, output); return 0;
op1Output = op1->getOperatorOutput(OPL3, feedbackOutput); op1Output = op1->getOperatorOutput(OPL3, feedbackOutput);
@ -1105,9 +1159,9 @@ void Channel4op::getChannelOutput(OPL3 *OPL3, double output[4]) {
} }
feedback[0] = feedback[1]; feedback[0] = feedback[1];
feedback[1] = modf(op1Output * ChannelData::feedback[fb], &dontcare); feedback[1] = StripIntPart(op1Output * ChannelData::feedback[fb]);
getInFourChannels(OPL3, channelOutput, output); return channelOutput;
} }
void Channel4op::keyOn() { void Channel4op::keyOn() {
@ -1216,7 +1270,7 @@ double Operator::getOperatorOutput(OPL3 *OPL3, double modulator) {
if(envelopeGenerator.stage == EnvelopeGenerator::OFF) return 0; if(envelopeGenerator.stage == EnvelopeGenerator::OFF) return 0;
double envelopeInDB = envelopeGenerator.getEnvelope(OPL3, egt, am); double envelopeInDB = envelopeGenerator.getEnvelope(OPL3, egt, am);
envelope = pow(10, envelopeInDB/10.0); envelope = EnvelopeFromDB(envelopeInDB);
// If it is in OPL2 mode, use first four waveforms only: // If it is in OPL2 mode, use first four waveforms only:
ws &= ((OPL3->_new<<2) + 3); ws &= ((OPL3->_new<<2) + 3);
@ -1229,14 +1283,7 @@ double Operator::getOperatorOutput(OPL3 *OPL3, double modulator) {
} }
double Operator::getOutput(double modulator, double outputPhase, double *waveform) { double Operator::getOutput(double modulator, double outputPhase, double *waveform) {
double dontcare; int sampleIndex = xs_FloorToInt((outputPhase + modulator) * OperatorData::waveLength) & (OperatorData::waveLength - 1);
outputPhase = modf(outputPhase + modulator, &dontcare);
if(outputPhase<0) {
outputPhase++;
// If the double could not afford to be less than 1:
outputPhase = modf(outputPhase, &dontcare);
}
int sampleIndex = (int) (outputPhase * OperatorData::waveLength);
return waveform[sampleIndex] * envelope; return waveform[sampleIndex] * envelope;
} }
@ -1323,9 +1370,9 @@ void EnvelopeGenerator::setActualAttackRate(int attackRate, int ksr, int keyScal
// and 'period10to90' seconds between 10% and 90% of the curve total level. // and 'period10to90' seconds between 10% and 90% of the curve total level.
actualAttackRate = calculateActualRate(attackRate, ksr, keyScaleNumber); actualAttackRate = calculateActualRate(attackRate, ksr, keyScaleNumber);
double period0to100inSeconds = EnvelopeGeneratorData::attackTimeValuesTable[actualAttackRate][0]/1000.0; double period0to100inSeconds = EnvelopeGeneratorData::attackTimeValuesTable[actualAttackRate][0]/1000.0;
int period0to100inSamples = (int)(period0to100inSeconds*sampleRate); int period0to100inSamples = (int)(period0to100inSeconds*OPL_SAMPLE_RATE);
double period10to90inSeconds = EnvelopeGeneratorData::attackTimeValuesTable[actualAttackRate][1]/1000.0; double period10to90inSeconds = EnvelopeGeneratorData::attackTimeValuesTable[actualAttackRate][1]/1000.0;
int period10to90inSamples = (int)(period10to90inSeconds*sampleRate); int period10to90inSamples = (int)(period10to90inSeconds*OPL_SAMPLE_RATE);
// The x increment is dictated by the period between 10% and 90%: // The x increment is dictated by the period between 10% and 90%:
xAttackIncrement = OPL3Data::calculateIncrement(percentageToX(0.1), percentageToX(0.9), period10to90inSeconds); xAttackIncrement = OPL3Data::calculateIncrement(percentageToX(0.1), percentageToX(0.9), period10to90inSeconds);
// Discover how many samples are still from the top. // Discover how many samples are still from the top.
@ -1389,7 +1436,17 @@ double EnvelopeGenerator::getEnvelope(OPL3 *OPL3, int egt, int am) {
// we´ll work with the next to maximum in the envelope resolution. // we´ll work with the next to maximum in the envelope resolution.
if(envelope<-envelopeResolution && xAttackIncrement != -EnvelopeGeneratorData::INFINITY) { if(envelope<-envelopeResolution && xAttackIncrement != -EnvelopeGeneratorData::INFINITY) {
// The attack is exponential. // The attack is exponential.
#if 0
envelope = -pow(2.0,x); envelope = -pow(2.0,x);
#else
int index = xs_FloorToInt((x - ATTACK_MIN) / ATTACK_RES);
if (index < 0)
envelope = OPL3::OperatorData->attackTable[0];
else if (index >= ATTACK_TABLE_SIZE)
envelope = OPL3::OperatorData->attackTable[ATTACK_TABLE_SIZE-1];
else
envelope = OPL3::OperatorData->attackTable[index];
#endif
x += xAttackIncrement; x += xAttackIncrement;
break; break;
} }
@ -1478,30 +1535,29 @@ PhaseGenerator::PhaseGenerator() {
void PhaseGenerator::setFrequency(int f_number, int block, int mult) { void PhaseGenerator::setFrequency(int f_number, int block, int mult) {
// This frequency formula is derived from the following equation: // This frequency formula is derived from the following equation:
// f_number = baseFrequency * pow(2,19) / sampleRate / pow(2,block-1); // f_number = baseFrequency * pow(2,19) / OPL_SAMPLE_RATE / pow(2,block-1);
double baseFrequency = double baseFrequency =
f_number * pow(2.0, block-1) * sampleRate / pow(2.0,19); f_number * pow(2.0, block-1) * OPL_SAMPLE_RATE / pow(2.0,19);
double operatorFrequency = baseFrequency*OperatorData::multTable[mult]; double operatorFrequency = baseFrequency*OperatorData::multTable[mult];
// phase goes from 0 to 1 at // phase goes from 0 to 1 at
// period = (1/frequency) seconds -> // period = (1/frequency) seconds ->
// Samples in each period is (1/frequency)*sampleRate = // Samples in each period is (1/frequency)*OPL_SAMPLE_RATE =
// = sampleRate/frequency -> // = OPL_SAMPLE_RATE/frequency ->
// So the increment in each sample, to go from 0 to 1, is: // So the increment in each sample, to go from 0 to 1, is:
// increment = (1-0) / samples in the period -> // increment = (1-0) / samples in the period ->
// increment = 1 / (OPL3Data.sampleRate/operatorFrequency) -> // increment = 1 / (OPL_SAMPLE_RATE/operatorFrequency) ->
phaseIncrement = operatorFrequency/sampleRate; phaseIncrement = operatorFrequency/OPL_SAMPLE_RATE;
} }
double PhaseGenerator::getPhase(OPL3 *OPL3, int vib) { double PhaseGenerator::getPhase(OPL3 *OPL3, int vib) {
if(vib==1) if(vib==1)
// phaseIncrement = (operatorFrequency * vibrato) / sampleRate // phaseIncrement = (operatorFrequency * vibrato) / OPL_SAMPLE_RATE
phase += phaseIncrement*OPL3::OPL3Data->vibratoTable[OPL3->dvb][OPL3->vibratoIndex]; phase += phaseIncrement*OPL3::OPL3Data->vibratoTable[OPL3->dvb][OPL3->vibratoIndex];
else else
// phaseIncrement = operatorFrequency / sampleRate // phaseIncrement = operatorFrequency / OPL_SAMPLE_RATE
phase += phaseIncrement; phase += phaseIncrement;
double dontcare; // Originally clamped phase to [0,1), but that's not needed
phase = modf(phase, &dontcare);
return phase; return phase;
} }
@ -1509,7 +1565,7 @@ void PhaseGenerator::keyOn() {
phase = 0; phase = 0;
} }
void RhythmChannel::getChannelOutput(OPL3 *OPL3, double output[4]) { double RhythmChannel::getChannelOutput(OPL3 *OPL3) {
double channelOutput = 0, op1Output = 0, op2Output = 0; double channelOutput = 0, op1Output = 0, op2Output = 0;
// Note that, different from the common channel, // Note that, different from the common channel,
@ -1520,7 +1576,7 @@ void RhythmChannel::getChannelOutput(OPL3 *OPL3, double output[4]) {
op2Output = op2->getOperatorOutput(OPL3, Operator::noModulator); op2Output = op2->getOperatorOutput(OPL3, Operator::noModulator);
channelOutput = (op1Output + op2Output) / 2; channelOutput = (op1Output + op2Output) / 2;
getInFourChannels(OPL3, channelOutput, output); return channelOutput;
}; };
TopCymbalOperator::TopCymbalOperator(int baseAddress) TopCymbalOperator::TopCymbalOperator(int baseAddress)
@ -1534,7 +1590,7 @@ TopCymbalOperator::TopCymbalOperator()
double TopCymbalOperator::getOperatorOutput(OPL3 *OPL3, double modulator) { double TopCymbalOperator::getOperatorOutput(OPL3 *OPL3, double modulator) {
double highHatOperatorPhase = double highHatOperatorPhase =
OPL3->highHatOperator.phase * OperatorData::multTable[OPL3->highHatOperator.mult]; OPL3->highHatOperator.phase * OperatorData::multTable[OPL3->highHatOperator.mult];
// The Top Cymbal operator uses his own phase together with the High Hat phase. // The Top Cymbal operator uses its own phase together with the High Hat phase.
return getOperatorOutput(OPL3, modulator, highHatOperatorPhase); return getOperatorOutput(OPL3, modulator, highHatOperatorPhase);
} }
@ -1544,7 +1600,7 @@ double TopCymbalOperator::getOperatorOutput(OPL3 *OPL3, double modulator) {
// now with the TopCymbalOperator phase as the externalPhase. // now with the TopCymbalOperator phase as the externalPhase.
double TopCymbalOperator::getOperatorOutput(OPL3 *OPL3, double modulator, double externalPhase) { double TopCymbalOperator::getOperatorOutput(OPL3 *OPL3, double modulator, double externalPhase) {
double envelopeInDB = envelopeGenerator.getEnvelope(OPL3, egt, am); double envelopeInDB = envelopeGenerator.getEnvelope(OPL3, egt, am);
envelope = pow(10.0, envelopeInDB/10.0); envelope = EnvelopeFromDB(envelopeInDB);
phase = phaseGenerator.getPhase(OPL3, vib); phase = phaseGenerator.getPhase(OPL3, vib);
@ -1552,11 +1608,10 @@ double TopCymbalOperator::getOperatorOutput(OPL3 *OPL3, double modulator, double
double *waveform = OPL3::OperatorData->waveforms[waveIndex]; double *waveform = OPL3::OperatorData->waveforms[waveIndex];
// Empirically tested multiplied phase for the Top Cymbal: // Empirically tested multiplied phase for the Top Cymbal:
double dontcare; double carrierPhase = 8 * phase;
double carrierPhase = modf(8 * phase, &dontcare);
double modulatorPhase = externalPhase; double modulatorPhase = externalPhase;
double modulatorOutput = getOutput(Operator::noModulator, modulatorPhase, waveform); double modulatorOutput = getOutput(Operator::noModulator, modulatorPhase, waveform);
double carrierOutput = getOutput(modulatorOutput,carrierPhase, waveform); double carrierOutput = getOutput(modulatorOutput, carrierPhase, waveform);
int cycles = 4; int cycles = 4;
double chopped = (carrierPhase * cycles) /* %cycles */; double chopped = (carrierPhase * cycles) /* %cycles */;
@ -1589,7 +1644,7 @@ double SnareDrumOperator::getOperatorOutput(OPL3 *OPL3, double modulator) {
if(envelopeGenerator.stage == EnvelopeGenerator::OFF) return 0; if(envelopeGenerator.stage == EnvelopeGenerator::OFF) return 0;
double envelopeInDB = envelopeGenerator.getEnvelope(OPL3, egt, am); double envelopeInDB = envelopeGenerator.getEnvelope(OPL3, egt, am);
envelope = pow(10.0, envelopeInDB/10.0); envelope = EnvelopeFromDB(envelopeInDB);
// If it is in OPL2 mode, use first four waveforms only: // If it is in OPL2 mode, use first four waveforms only:
int waveIndex = ws & ((OPL3->_new<<2) + 3); int waveIndex = ws & ((OPL3->_new<<2) + 3);
@ -1615,10 +1670,10 @@ BassDrumChannel::BassDrumChannel()
my_op1(op1BaseAddress), my_op2(op2BaseAddress) my_op1(op1BaseAddress), my_op2(op2BaseAddress)
{ } { }
void BassDrumChannel::getChannelOutput(OPL3 *OPL3, double output[4]) { double BassDrumChannel::getChannelOutput(OPL3 *OPL3) {
// Bass Drum ignores first operator, when it is in series. // Bass Drum ignores first operator, when it is in series.
if(cnt == 1) op1->ar=0; if(cnt == 1) op1->ar=0;
return Channel2op::getChannelOutput(OPL3, output); return Channel2op::getChannelOutput(OPL3);
} }
void OPL3Data::loadVibratoTable() { void OPL3Data::loadVibratoTable() {
@ -1685,7 +1740,7 @@ void OPL3Data::loadTremoloTable()
calculateIncrement(tremoloDepth[1],0,1/(2*tremoloFrequency)) calculateIncrement(tremoloDepth[1],0,1/(2*tremoloFrequency))
}; };
int tremoloTableLength = (int)(sampleRate/tremoloFrequency); int tremoloTableLength = (int)(OPL_SAMPLE_RATE/tremoloFrequency);
// This is undocumented. The tremolo starts at the maximum attenuation, // This is undocumented. The tremolo starts at the maximum attenuation,
// instead of at 0 dB: // instead of at 0 dB:
@ -1752,6 +1807,22 @@ void OperatorData::loadWaveforms() {
} }
} }
void OperatorData::loaddBPowTable()
{
for (int i = 0; i < DB_TABLE_SIZE; ++i)
{
dbpow[i] = pow(10.0, -(i / DB_TABLE_RES) / 10.0);
}
}
void OperatorData::loadAttackTable()
{
for (int i = 0; i < ATTACK_TABLE_SIZE; ++i)
{
attackTable[i] = -pow(2.0, ATTACK_MIN + i * ATTACK_RES);
}
}
void OPL3::Reset() void OPL3::Reset()
{ {
} }
@ -1761,18 +1832,6 @@ void OPL3::WriteReg(int reg, int v)
write(reg >> 8, reg & 0xFF, v); write(reg >> 8, reg & 0xFF, v);
} }
void OPL3::Update(float *buffer, int length)
{
double output[4];
for (int i = 0; i < length; ++i)
{
read(output);
buffer[i*2 ] += float(output[0] * 0.25f);
buffer[i*2+1] += float(output[1] * 0.25f);
}
}
void OPL3::SetPanning(int c, float left, float right) void OPL3::SetPanning(int c, float left, float right)
{ {
} }

View file

@ -294,7 +294,6 @@ enum MUSctrl {
ctrlPoly, ctrlPoly,
}; };
#define OPL_SAMPLE_RATE 49716.0
#define ADLIB_CLOCK_MUL 24.0 #define ADLIB_CLOCK_MUL 24.0
#endif // __MUSLIB_H_ #endif // __MUSLIB_H_

View file

@ -22,4 +22,6 @@ OPLEmul *YM3812Create(bool stereo);
OPLEmul *DBOPLCreate(bool stereo); OPLEmul *DBOPLCreate(bool stereo);
OPLEmul *JavaOPLCreate(bool stereo); OPLEmul *JavaOPLCreate(bool stereo);
#define OPL_SAMPLE_RATE 49716.0
#endif #endif

View file

@ -1,5 +1,6 @@
#include "i_musicinterns.h" #include "i_musicinterns.h"
#include "oplsynth/muslib.h" #include "oplsynth/muslib.h"
#include "oplsynth/opl.h"
static bool OPL_Active; static bool OPL_Active;