aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CMakeLists.txt10
-rw-r--r--include/adlmidi.h2
-rw-r--r--src/adlmidi_opl3.cpp14
-rw-r--r--src/chips/java/OPL3.cpp1873
-rw-r--r--src/chips/java_opl3.cpp94
-rw-r--r--src/chips/java_opl3.h44
-rw-r--r--utils/midiplay/adlmidiplay.cpp5
7 files changed, 2040 insertions, 2 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 3bbc24e..306d4e0 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -109,6 +109,7 @@ option(WITH_XMI_SUPPORT "Build with support for AIL XMI files" ON)
option(USE_DOSBOX_EMULATOR "Use DosBox 0.74 OPL3 emulator (semi-accurate, suggested for slow or mobile platforms)" ON)
option(USE_NUKED_EMULATOR "Use Nuked OPL3 emulator (most accurate, powerful)" ON)
option(USE_OPAL_EMULATOR "Use Opal emulator (inaccurate)" ON)
+option(USE_JAVA_EMULATOR "Use JavaOPL emulator" ON)
option(WITH_GENADLDATA "Build and run full rebuild of embedded banks cache" OFF)
option(WITH_GENADLDATA_COMMENTS "Enable comments in generated ADLDATA cache file" OFF)
@@ -168,6 +169,15 @@ function(handle_options targetLib)
target_compile_definitions(${targetLib} PUBLIC ADLMIDI_DISABLE_OPAL_EMULATOR)
endif()
+ if(USE_JAVA_EMULATOR)
+ set(HAS_EMULATOR TRUE)
+ target_sources(${targetLib} PRIVATE
+ ${libADLMIDI_SOURCE_DIR}/src/chips/java_opl3.cpp
+ )
+ else()
+ target_compile_definitions(${targetLib} PUBLIC ADLMIDI_DISABLE_JAVA_EMULATOR)
+ endif()
+
if(NOT HAS_EMULATOR)
message(FATAL_ERROR "No emulators enabled! You must enable at least one emulator!")
endif()
diff --git a/include/adlmidi.h b/include/adlmidi.h
index 2a84565..b963629 100644
--- a/include/adlmidi.h
+++ b/include/adlmidi.h
@@ -625,6 +625,8 @@ enum ADL_Emulator
ADLMIDI_EMU_DOSBOX,
/*! Opal */
ADLMIDI_EMU_OPAL,
+ /*! Java */
+ ADLMIDI_EMU_JAVA,
/*! Count instrument on the level */
ADLMIDI_EMU_end
};
diff --git a/src/adlmidi_opl3.cpp b/src/adlmidi_opl3.cpp
index 7101319..a7bffc3 100644
--- a/src/adlmidi_opl3.cpp
+++ b/src/adlmidi_opl3.cpp
@@ -48,6 +48,11 @@ static const unsigned OPLBase = 0x388;
# ifndef ADLMIDI_DISABLE_OPAL_EMULATOR
# include "chips/opal_opl3.h"
# endif
+
+// Java emulator
+# ifndef ADLMIDI_DISABLE_JAVA_EMULATOR
+# include "chips/java_opl3.h"
+# endif
#endif
static const unsigned adl_emulatorSupport = 0
@@ -63,6 +68,10 @@ static const unsigned adl_emulatorSupport = 0
# ifndef ADLMIDI_DISABLE_OPAL_EMULATOR
| (1u << ADLMIDI_EMU_OPAL)
# endif
+
+# ifndef ADLMIDI_DISABLE_JAVA_EMULATOR
+ | (1u << ADLMIDI_EMU_JAVA)
+# endif
#endif
;
@@ -781,6 +790,11 @@ void OPL3::reset(int emulator, unsigned long PCM_RATE, void *audioTickHandler)
chip = new OpalOPL3;
break;
#endif
+#ifndef ADLMIDI_DISABLE_JAVA_EMULATOR
+ case ADLMIDI_EMU_JAVA:
+ chip = new JavaOPL3;
+ break;
+#endif
}
m_chips[i].reset(chip);
chip->setChipId((uint32_t)i);
diff --git a/src/chips/java/OPL3.cpp b/src/chips/java/OPL3.cpp
new file mode 100644
index 0000000..9a4469b
--- /dev/null
+++ b/src/chips/java/OPL3.cpp
@@ -0,0 +1,1873 @@
+/*
+ * File: OPL3.java
+ * Software implementation of the Yamaha YMF262 sound generator.
+ * Copyright (C) 2008 Robson Cozendey <robson@cozendey.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * One of the objectives of this emulator is to stimulate further research in the
+ * OPL3 chip emulation. There was an explicit effort in making no optimizations,
+ * and making the code as legible as possible, so that a new programmer
+ * interested in modify and improve upon it could do so more easily.
+ * This emulator's main body of information was taken from reverse engineering of
+ * the OPL3 chip, from the YMF262 Datasheet and from the OPL3 section in the
+ * YMF278b Application's Manual,
+ * together with the vibrato table information, eighth waveform parameter
+ * information and feedback averaging information provided in MAME's YMF262 and
+ * YM3812 emulators, by Jarek Burczynski and Tatsuyuki Satoh.
+ * This emulator has a high degree of accuracy, and most of music files sound
+ * almost identical, exception made in some games which uses specific parts of
+ * the rhythm section. In this respect, some parts of the rhythm mode are still
+ * only an approximation of the real chip.
+ * The other thing to note is that this emulator was done through recordings of
+ * the SB16 DAC, so it has not bitwise precision. Additional equipment should be
+ * used to verify the samples directly from the chip, and allow this exact
+ * per-sample correspondence. As a good side-effect, since this emulator uses
+ * floating point and has a more fine-grained envelope generator, it can produce
+ * sometimes a crystal-clear, denser kind of OPL3 sound that, because of that,
+ * may be useful for creating new music.
+ *
+ * Version 1.0.6
+ *
+ */
+
+#include <math.h>
+#include <stdint.h>
+#include <string.h>
+#include <limits>
+
+#define OPL_SAMPLE_RATE 49716.0
+#define CENTER_PANNING_POWER 0.70710678118 /* [RH] volume at center for EQP */
+#define VOLUME_MUL 0.3333
+
+namespace JavaOPL
+{
+
+/* libADLMIDI: random generator */
+struct FRandom
+{
+ uint32_t GenRand32() { return (seed = seed * 1664525u + 1013904223u); }
+ double GenRand_Real1() { return GenRand32() * (1.0 / UINT32_MAX); }
+private:
+ uint32_t seed;
+};
+
+static FRandom pr_opl3;
+
+class Operator;
+
+static inline double StripIntPart(double num)
+{
+ return num - trunc(num); /* libOPNMIDI */
+}
+
+//
+// Channels
+//
+
+
+class Channel
+{
+protected:
+ double feedback[2];
+
+ int fnuml, fnumh, kon, block, fb, cha, chb, cnt;
+
+ // Factor to convert between normalized amplitude to normalized
+ // radians. The amplitude maximum is equivalent to 8*Pi radians.
+#define toPhase (4.f)
+
+public:
+ int channelBaseAddress;
+
+ double leftPan, rightPan;
+
+ Channel (int baseAddress, double startvol);
+ virtual ~Channel() {}
+ void update_2_KON1_BLOCK3_FNUMH2(class OPL3 *OPL3);
+ void update_FNUML8(class OPL3 *OPL3);
+ void update_CHD1_CHC1_CHB1_CHA1_FB3_CNT1(class OPL3 *OPL3);
+ void updateChannel(class OPL3 *OPL3);
+ void updatePan(class OPL3 *OPL3);
+ virtual double getChannelOutput(class OPL3 *OPL3) = 0;
+
+ virtual void keyOn() = 0;
+ virtual void keyOff() = 0;
+ virtual void updateOperators(class OPL3 *OPL3) = 0;
+};
+
+
+class Channel2op : public Channel
+{
+public:
+ Operator *op1, *op2;
+
+ Channel2op (int baseAddress, double startvol, Operator *o1, Operator *o2);
+ double getChannelOutput(class OPL3 *OPL3);
+
+ void keyOn();
+ void keyOff();
+ void updateOperators(class OPL3 *OPL3);
+};
+
+
+class Channel4op : public Channel
+{
+public:
+ Operator *op1, *op2, *op3, *op4;
+
+ Channel4op (int baseAddress, double startvol, Operator *o1, Operator *o2, Operator *o3, Operator *o4);
+ double getChannelOutput(class OPL3 *OPL3);
+
+ void keyOn();
+ void keyOff();
+ void updateOperators(class OPL3 *OPL3);
+};
+
+// There's just one instance of this class, that fills the eventual gaps in the Channel array;
+class DisabledChannel : public Channel
+{
+public:
+ DisabledChannel() : Channel(0, 0) { }
+ double getChannelOutput(class OPL3 *OPL3) { return 0; }
+ void keyOn() { }
+ void keyOff() { }
+ void updateOperators(class OPL3 *OPL3) { }
+};
+
+
+
+//
+// Envelope Generator
+//
+
+
+class EnvelopeGenerator
+{
+public:
+ enum Stage {ATTACK,DECAY,SUSTAIN,RELEASE,OFF};
+ Stage stage;
+ int actualAttackRate, actualDecayRate, actualReleaseRate;
+ double xAttackIncrement, xMinimumInAttack;
+ double dBdecayIncrement;
+ double dBreleaseIncrement;
+ double attenuation, totalLevel, sustainLevel;
+ double x, envelope;
+
+public:
+ EnvelopeGenerator();
+ void setActualSustainLevel(int sl);
+ void setTotalLevel(int tl);
+ void setAtennuation(int f_number, int block, int ksl);
+ void setActualAttackRate(int attackRate, int ksr, int keyScaleNumber);
+ void setActualDecayRate(int decayRate, int ksr, int keyScaleNumber);
+ void setActualReleaseRate(int releaseRate, int ksr, int keyScaleNumber);
+private:
+ int calculateActualRate(int rate, int ksr, int keyScaleNumber);
+public:
+ double getEnvelope(OPL3 *OPL3, int egt, int am);
+ void keyOn();
+ void keyOff();
+
+private:
+ static double dBtoX(double dB);
+ static double percentageToDB(double percentage);
+ static double percentageToX(double percentage);
+};
+
+
+//
+// Phase Generator
+//
+
+
+class PhaseGenerator {
+ double phase, phaseIncrement;
+
+public:
+ PhaseGenerator();
+ void setFrequency(int f_number, int block, int mult);
+ double getPhase(class OPL3 *OPL3, int vib);
+ void keyOn();
+};
+
+
+//
+// Operators
+//
+
+
+class Operator
+{
+public:
+ PhaseGenerator phaseGenerator;
+ EnvelopeGenerator envelopeGenerator;
+
+ double envelope, phase;
+
+ int operatorBaseAddress;
+ int am, vib, ksr, egt, mult, ksl, tl, ar, dr, sl, rr, ws;
+ int keyScaleNumber, f_number, block;
+
+ static const double noModulator;
+
+public:
+ Operator(int baseAddress);
+ void update_AM1_VIB1_EGT1_KSR1_MULT4(class OPL3 *OPL3);
+ void update_KSL2_TL6(class OPL3 *OPL3);
+ void update_AR4_DR4(class OPL3 *OPL3);
+ void update_SL4_RR4(class OPL3 *OPL3);
+ void update_5_WS3(class OPL3 *OPL3);
+ double getOperatorOutput(class OPL3 *OPL3, double modulator);
+
+ void keyOn();
+ void keyOff();
+ void updateOperator(class OPL3 *OPL3, int ksn, int f_num, int blk);
+protected:
+ double getOutput(double modulator, double outputPhase, double *waveform);
+};
+
+
+//
+// Rhythm
+//
+
+// The getOperatorOutput() method in TopCymbalOperator, HighHatOperator and SnareDrumOperator
+// were made through purely empyrical reverse engineering of the OPL3 output.
+
+class RhythmChannel : public Channel2op
+{
+public:
+ RhythmChannel(int baseAddress, double startvol, Operator *o1, Operator *o2)
+ : Channel2op(baseAddress, startvol, o1, o2)
+ { }
+ double getChannelOutput(class OPL3 *OPL3);
+
+ // Rhythm channels are always running,
+ // only the envelope is activated by the user.
+ void keyOn() { }
+ void keyOff() { }
+};
+
+class HighHatSnareDrumChannel : public RhythmChannel {
+ static const int highHatSnareDrumChannelBaseAddress = 7;
+public:
+ HighHatSnareDrumChannel(double startvol, Operator *o1, Operator *o2)
+ : RhythmChannel(highHatSnareDrumChannelBaseAddress, startvol, o1, o2)
+ { }
+};
+
+class TomTomTopCymbalChannel : public RhythmChannel {
+ static const int tomTomTopCymbalChannelBaseAddress = 8;
+public:
+ TomTomTopCymbalChannel(double startvol, Operator *o1, Operator *o2)
+ : RhythmChannel(tomTomTopCymbalChannelBaseAddress, startvol, o1, o2)
+ { }
+};
+
+class TopCymbalOperator : public Operator {
+ static const int topCymbalOperatorBaseAddress = 0x15;
+public:
+ TopCymbalOperator(int baseAddress);
+ TopCymbalOperator();
+ double getOperatorOutput(class OPL3 *OPL3, double modulator);
+ double getOperatorOutput(class OPL3 *OPL3, double modulator, double externalPhase);
+};
+
+class HighHatOperator : public TopCymbalOperator {
+ static const int highHatOperatorBaseAddress = 0x11;
+public:
+ HighHatOperator();
+ double getOperatorOutput(class OPL3 *OPL3, double modulator);
+};
+
+class SnareDrumOperator : public Operator {
+ static const int snareDrumOperatorBaseAddress = 0x14;
+public:
+ SnareDrumOperator();
+ double getOperatorOutput(class OPL3 *OPL3, double modulator);
+};
+
+class TomTomOperator : public Operator {
+ static const int tomTomOperatorBaseAddress = 0x12;
+public:
+ TomTomOperator() : Operator(tomTomOperatorBaseAddress) { }
+};
+
+class BassDrumChannel : public Channel2op {
+ static const int bassDrumChannelBaseAddress = 6;
+ static const int op1BaseAddress = 0x10;
+ static const int op2BaseAddress = 0x13;
+
+ Operator my_op1, my_op2;
+
+public:
+ BassDrumChannel(double startvol);
+ double getChannelOutput(class OPL3 *OPL3);
+
+ // Key ON and OFF are unused in rhythm channels.
+ void keyOn() { }
+ void keyOff() { }
+};
+
+
+//
+// OPl3 Data
+//
+
+
+struct OPL3DataStruct
+{
+public:
+ // OPL3-wide registers offsets:
+ static const int
+ _1_NTS1_6_Offset = 0x08,
+ DAM1_DVB1_RYT1_BD1_SD1_TOM1_TC1_HH1_Offset = 0xBD,
+ _7_NEW1_Offset = 0x105,
+ _2_CONNECTIONSEL6_Offset = 0x104;
+
+ // The OPL3 tremolo repetition rate is 3.7 Hz.
+ #define tremoloFrequency (3.7)
+
+ static const int tremoloTableLength = (int)(OPL_SAMPLE_RATE/tremoloFrequency);
+ static const int vibratoTableLength = 8192;
+
+ OPL3DataStruct()
+ {
+ loadVibratoTable();
+ loadTremoloTable();
+ }
+
+ // The first array is used when DVB=0 and the second array is used when DVB=1.
+ double vibratoTable[2][vibratoTableLength];
+
+ // First array used when AM = 0 and second array used when AM = 1.
+ double tremoloTable[2][tremoloTableLength];
+
+ static double calculateIncrement(double begin, double end, double period) {
+ return (end-begin)/OPL_SAMPLE_RATE * (1/period);
+ }
+
+private:
+ void loadVibratoTable();
+ void loadTremoloTable();
+};
+
+
+//
+// Channel Data
+//
+
+
+struct ChannelData
+{
+ static const int
+ _2_KON1_BLOCK3_FNUMH2_Offset = 0xB0,
+ FNUML8_Offset = 0xA0,
+ CHD1_CHC1_CHB1_CHA1_FB3_CNT1_Offset = 0xC0;
+
+ // Feedback rate in fractions of 2*Pi, normalized to (0,1):
+ // 0, Pi/16, Pi/8, Pi/4, Pi/2, Pi, 2*Pi, 4*Pi turns to be:
+ static const float feedback[8];
+};
+const float ChannelData::feedback[8] = {0,1/32.f,1/16.f,1/8.f,1/4.f,1/2.f,1,2};
+
+
+//
+// Operator Data
+//
+
+
+struct OperatorDataStruct
+{
+ static const int
+ AM1_VIB1_EGT1_KSR1_MULT4_Offset = 0x20,
+ KSL2_TL6_Offset = 0x40,
+ AR4_DR4_Offset = 0x60,
+ SL4_RR4_Offset = 0x80,
+ _5_WS3_Offset = 0xE0;
+
+ enum type {NO_MODULATION, CARRIER, FEEDBACK};
+
+ static const int waveLength = 1024;
+
+ static const float multTable[16];
+ static const float ksl3dBtable[16][8];
+
+ //OPL3 has eight waveforms:
+ 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)
+#define DB_TABLE_SIZE 480 /* libADLMIDI */
+
+ 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)
+#define ATTACK_TABLE_SIZE 416 /* libADLMIDI */
+
+ double attackTable[ATTACK_TABLE_SIZE];
+
+ OperatorDataStruct()
+ {
+ loadWaveforms();
+ loaddBPowTable();
+ loadAttackTable();
+ }
+
+ static double log2(double x) {
+ return log(x)/log(2.0);
+ }
+private:
+ void loadWaveforms();
+ void loaddBPowTable();
+ void loadAttackTable();
+};
+const float OperatorDataStruct::multTable[16] = {0.5,1,2,3,4,5,6,7,8,9,10,10,12,12,15,15};
+
+const float OperatorDataStruct::ksl3dBtable[16][8] = {
+ {0,0,0,0,0,0,0,0},
+ {0,0,0,0,0,-3,-6,-9},
+ {0,0,0,0,-3,-6,-9,-12},
+ {0,0,0, -1.875, -4.875, -7.875, -10.875, -13.875},
+
+ {0,0,0,-3,-6,-9,-12,-15},
+ {0,0, -1.125, -4.125, -7.125, -10.125, -13.125, -16.125},
+ {0,0, -1.875, -4.875, -7.875, -10.875, -13.875, -16.875},
+ {0,0, -2.625, -5.625, -8.625, -11.625, -14.625, -17.625},
+
+ {0,0,-3,-6,-9,-12,-15,-18},
+ {0, -0.750, -3.750, -6.750, -9.750, -12.750, -15.750, -18.750},
+ {0, -1.125, -4.125, -7.125, -10.125, -13.125, -16.125, -19.125},
+ {0, -1.500, -4.500, -7.500, -10.500, -13.500, -16.500, -19.500},
+
+ {0, -1.875, -4.875, -7.875, -10.875, -13.875, -16.875, -19.875},
+ {0, -2.250, -5.250, -8.250, -11.250, -14.250, -17.250, -20.250},
+ {0, -2.625, -5.625, -8.625, -11.625, -14.625, -17.625, -20.625},
+ {0,-3,-6,-9,-12,-15,-18,-21}
+};
+
+//
+// Envelope Generator Data
+//
+
+
+namespace EnvelopeGeneratorData
+{
+ static const double MUGEN = std::numeric_limits<double>::infinity();
+ // This table is indexed by the value of Operator.ksr
+ // and the value of ChannelRegister.keyScaleNumber.
+ static const int rateOffset[2][16] = {
+ {0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3},
+ {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15}
+ };
+ // These attack periods in miliseconds were taken from the YMF278B manual.
+ // The attack actual rates range from 0 to 63, with different data for
+ // 0%-100% and for 10%-90%:
+ static const double attackTimeValuesTable[64][2] = {
+ {MUGEN,MUGEN}, {MUGEN,MUGEN}, {MUGEN,MUGEN}, {MUGEN,MUGEN},
+ {2826.24,1482.75}, {2252.80,1155.07}, {1884.16,991.23}, {1597.44,868.35},
+ {1413.12,741.38}, {1126.40,577.54}, {942.08,495.62}, {798.72,434.18},
+ {706.56,370.69}, {563.20,288.77}, {471.04,247.81}, {399.36,217.09},
+
+ {353.28,185.34}, {281.60,144.38}, {235.52,123.90}, {199.68,108.54},
+ {176.76,92.67}, {140.80,72.19}, {117.76,61.95}, {99.84,54.27},
+ {88.32,46.34}, {70.40,36.10}, {58.88,30.98}, {49.92,27.14},
+ {44.16,23.17}, {35.20,18.05}, {29.44,15.49}, {24.96,13.57},
+
+ {22.08,11.58}, {17.60,9.02}, {14.72,7.74}, {12.48,6.78},
+ {11.04,5.79}, {8.80,4.51}, {7.36,3.87}, {6.24,3.39},
+ {5.52,2.90}, {4.40,2.26}, {3.68,1.94}, {3.12,1.70},
+ {2.76,1.45}, {2.20,1.13}, {1.84,0.97}, {1.56,0.85},
+
+ {1.40,0.73}, {1.12,0.61}, {0.92,0.49}, {0.80,0.43},
+ {0.70,0.37}, {0.56,0.31}, {0.46,0.26}, {0.42,0.22},
+ {0.38,0.19}, {0.30,0.14}, {0.24,0.11}, {0.20,0.11},
+ {0.00,0.00}, {0.00,0.00}, {0.00,0.00}, {0.00,0.00}
+ };
+
+ // 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
+ // 0%-100% and for 10%-90%:
+ static const double decayAndReleaseTimeValuesTable[64][2] = {
+ {MUGEN,MUGEN}, {MUGEN,MUGEN}, {MUGEN,MUGEN}, {MUGEN,MUGEN},
+ {39280.64,8212.48}, {31416.32,6574.08}, {26173.44,5509.12}, {22446.08,4730.88},
+ {19640.32,4106.24}, {15708.16,3287.04}, {13086.72,2754.56}, {11223.04,2365.44},
+ {9820.16,2053.12}, {7854.08,1643.52}, {6543.36,1377.28}, {5611.52,1182.72},
+
+ {4910.08,1026.56}, {3927.04,821.76}, {3271.68,688.64}, {2805.76,591.36},
+ {2455.04,513.28}, {1936.52,410.88}, {1635.84,344.34}, {1402.88,295.68},
+ {1227.52,256.64}, {981.76,205.44}, {817.92,172.16}, {701.44,147.84},
+ {613.76,128.32}, {490.88,102.72}, {488.96,86.08}, {350.72,73.92},
+
+ {306.88,64.16}, {245.44,51.36}, {204.48,43.04}, {175.36,36.96},
+ {153.44,32.08}, {122.72,25.68}, {102.24,21.52}, {87.68,18.48},
+ {76.72,16.04}, {61.36,12.84}, {51.12,10.76}, {43.84,9.24},
+ {38.36,8.02}, {30.68,6.42}, {25.56,5.38}, {21.92,4.62},
+
+ {19.20,4.02}, {15.36,3.22}, {12.80,2.68}, {10.96,2.32},
+ {9.60,2.02}, {7.68,1.62}, {6.40,1.35}, {5.48,1.15},
+ {4.80,1.01}, {3.84,0.81}, {3.20,0.69}, {2.74,0.58},
+ {2.40,0.51}, {2.40,0.51}, {2.40,0.51}, {2.40,0.51}
+ };
+};
+
+class OPL3
+{
+public:
+ uint8_t registers[0x200];
+
+ Operator *operators[2][0x20];
+ Channel2op *channels2op[2][9];
+ Channel4op *channels4op[2][3];
+ Channel *channels[2][9];
+
+ // Unique instance to fill future gaps in the Channel array,
+ // when there will be switches between 2op and 4op mode.
+ DisabledChannel disabledChannel;
+
+ // Specific operators to switch when in rhythm mode:
+ HighHatOperator highHatOperator;
+ SnareDrumOperator snareDrumOperator;
+ TomTomOperator tomTomOperator;
+ TomTomTopCymbalChannel tomTomTopCymbalChannel;
+
+ // Rhythm channels
+ BassDrumChannel bassDrumChannel;
+ HighHatSnareDrumChannel highHatSnareDrumChannel;
+ TopCymbalOperator topCymbalOperator;
+
+ Operator *highHatOperatorInNonRhythmMode;
+ Operator *snareDrumOperatorInNonRhythmMode;
+ Operator *tomTomOperatorInNonRhythmMode;
+ Operator *topCymbalOperatorInNonRhythmMode;
+
+ int nts, dam, dvb, ryt, bd, sd, tom, tc, hh, _new, connectionsel;
+ int vibratoIndex, tremoloIndex;
+
+ bool FullPan;
+
+ static OperatorDataStruct *OperatorData;
+ static OPL3DataStruct *OPL3Data;
+
+ // The methods read() and write() are the only
+ // ones needed by the user to interface with the emulator.
+ // read() returns one frame at a time, to be played at 49700 Hz,
+ // with each frame being four 16-bit samples,
+ // corresponding to the OPL3 four output channels CHA...CHD.
+public:
+ //void read(float output[2]);
+ void write(int array, int address, int data);
+
+ OPL3(bool fullpan);
+ ~OPL3();
+
+private:
+ void initOperators();
+ void initChannels2op();
+ void initChannels4op();
+ void initRhythmChannels();
+ void initChannels();
+ void update_1_NTS1_6();
+ void update_DAM1_DVB1_RYT1_BD1_SD1_TOM1_TC1_HH1();
+ void update_7_NEW1();
+ void setEnabledChannels();
+ void updateChannelPans();
+ void update_2_CONNECTIONSEL6();
+ void set4opConnections();
+ void setRhythmMode();
+
+ static int InstanceCount;
+
+ // OPLEmul interface
+public:
+ void Reset();
+ void WriteReg(int reg, int v);
+ void Update(float *buffer, int length);
+ void SetPanning(int c, float left, float right);
+};
+
+OperatorDataStruct *OPL3::OperatorData;
+OPL3DataStruct *OPL3::OPL3Data;
+int OPL3::InstanceCount;
+
+void OPL3::Update(float *output, int numsamples) {
+ while (numsamples--) {
+ // 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 channelNumber=0; channelNumber < 9; channelNumber++) {
+ // Reads output from each OPL3 channel, and accumulates it in the output buffer:
+ Channel *channel = channels[array][channelNumber];
+ if (channel != &disabledChannel)
+ {
+ 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
+ // PhaseGenerator.getPhase() in each Operator.
+ vibratoIndex = (vibratoIndex + 1) & (OPL3DataStruct::vibratoTableLength - 1);
+ // Advances the OPL3-wide tremolo index, which is used by
+ // EnvelopeGenerator.getEnvelope() in each Operator.
+ tremoloIndex++;
+ if(tremoloIndex >= OPL3DataStruct::tremoloTableLength) tremoloIndex = 0;
+ output += 2;
+ }
+}
+
+void OPL3::write(int array, int address, int data) {
+ // The OPL3 has two registers arrays, each with adresses ranging
+ // from 0x00 to 0xF5.
+ // This emulator uses one array, with the two original register arrays
+ // starting at 0x00 and at 0x100.
+ int registerAddress = (array<<8) | address;
+ // If the address is out of the OPL3 memory map, returns.
+ if(registerAddress<0 || registerAddress>=0x200) return;
+
+ registers[registerAddress] = data;
+ switch(address&0xE0) {
+ // The first 3 bits masking gives the type of the register by using its base address:
+ // 0x00, 0x20, 0x40, 0x60, 0x80, 0xA0, 0xC0, 0xE0
+ // When it is needed, we further separate the register type inside each base address,
+ // which is the case of 0x00 and 0xA0.
+
+ // Through out this emulator we will use the same name convention to
+ // reference a byte with several bit registers.
+ // The name of each bit register will be followed by the number of bits
+ // it occupies inside the byte.
+ // Numbers without accompanying names are unused bits.
+ case 0x00:
+ // Unique registers for the entire OPL3:
+ if(array==1) {
+ if(address==0x04)
+ update_2_CONNECTIONSEL6();
+ else if(address==0x05)
+ update_7_NEW1();
+ }
+ else if(address==0x08) update_1_NTS1_6();
+ break;
+
+ case 0xA0:
+ // 0xBD is a control register for the entire OPL3:
+ if(address==0xBD) {
+ if(array==0)
+ update_DAM1_DVB1_RYT1_BD1_SD1_TOM1_TC1_HH1();
+ break;
+ }
+ // Registers for each channel are in A0-A8, B0-B8, C0-C8, in both register arrays.
+ // 0xB0...0xB8 keeps kon,block,fnum(h) for each channel.
+ if( (address&0xF0) == 0xB0 && address <= 0xB8) {
+ // If the address is in the second register array, adds 9 to the channel number.
+ // The channel number is given by the last four bits, like in A0,...,A8.
+ channels[array][address&0x0F]->update_2_KON1_BLOCK3_FNUMH2(this);
+ break;
+ }
+ // 0xA0...0xA8 keeps fnum(l) for each channel.
+ if( (address&0xF0) == 0xA0 && address <= 0xA8)
+ channels[array][address&0x0F]->update_FNUML8(this);
+ break;
+ // 0xC0...0xC8 keeps cha,chb,chc,chd,fb,cnt for each channel:
+ case 0xC0:
+ if(address <= 0xC8)
+ channels[array][address&0x0F]->update_CHD1_CHC1_CHB1_CHA1_FB3_CNT1(this);
+ break;
+
+ // Registers for each of the 36 Operators:
+ default:
+ int operatorOffset = address&0x1F;
+ if(operators[array][operatorOffset] == NULL) break;
+ switch(address&0xE0) {
+ // 0x20...0x35 keeps am,vib,egt,ksr,mult for each operator:
+ case 0x20:
+ operators[array][operatorOffset]->update_AM1_VIB1_EGT1_KSR1_MULT4(this);
+ break;
+ // 0x40...0x55 keeps ksl,tl for each operator:
+ case 0x40:
+ operators[array][operatorOffset]->update_KSL2_TL6(this);
+ break;
+ // 0x60...0x75 keeps ar,dr for each operator:
+ case 0x60:
+ operators[array][operatorOffset]->update_AR4_DR4(this);
+ break;
+ // 0x80...0x95 keeps sl,rr for each operator:
+ case 0x80:
+ operators[array][operatorOffset]->update_SL4_RR4(this);
+ break;
+ // 0xE0...0xF5 keeps ws for each operator:
+ case 0xE0:
+ operators[array][operatorOffset]->update_5_WS3(this);
+ }
+ }
+}
+
+OPL3::OPL3(bool fullpan)
+: tomTomTopCymbalChannel(fullpan ? CENTER_PANNING_POWER : 1, &tomTomOperator, &topCymbalOperator),
+ bassDrumChannel(fullpan ? CENTER_PANNING_POWER : 1),
+ highHatSnareDrumChannel(fullpan ? CENTER_PANNING_POWER : 1, &highHatOperator, &snareDrumOperator)
+{
+ FullPan = fullpan;
+ nts = dam = dvb = ryt = bd = sd = tom = tc = hh = _new = connectionsel = 0;
+ vibratoIndex = tremoloIndex = 0;
+
+ if (InstanceCount++ == 0)
+ {
+ OPL3Data = new struct OPL3DataStruct;
+ OperatorData = new struct OperatorDataStruct;
+ }
+
+ initOperators();
+ initChannels2op();
+ initChannels4op();
+ initRhythmChannels();
+ initChannels();
+}
+
+OPL3::~OPL3()
+{
+ ryt = 0;
+ setRhythmMode(); // Make sure all operators point to the dynamically allocated ones.
+ for (int array = 0; array < 2; array++)
+ {
+ for (int operatorNumber = 0; operatorNumber < 0x20; operatorNumber++)
+ {
+ if (operators[array][operatorNumber] != NULL)
+ {
+ delete operators[array][operatorNumber];
+ }
+ }
+ for (int channelNumber = 0; channelNumber < 9; channelNumber++)
+ {
+ delete channels2op[array][channelNumber];
+ }
+ for (int channelNumber = 0; channelNumber < 3; channelNumber++)
+ {
+ delete channels4op[array][channelNumber];
+ }
+ }
+ if (--InstanceCount == 0)
+ {
+ delete OPL3Data;
+ OPL3Data = NULL;
+ delete OperatorData;
+ OperatorData = NULL;
+ }
+}
+
+
+void OPL3::initOperators() {
+ int baseAddress;
+ // The YMF262 has 36 operators:
+ memset(operators, 0, sizeof(operators));
+ for(int array=0; array<2; array++)
+ for(int group = 0; group<=0x10; group+=8)
+ for(int offset=0; offset<6; offset++) {
+ baseAddress = (array<<8) | (group+offset);
+ operators[array][group+offset] = new Operator(baseAddress);
+ }
+
+ // Save operators when they are in non-rhythm mode:
+ // Channel 7:
+ highHatOperatorInNonRhythmMode = operators[0][0x11];
+ snareDrumOperatorInNonRhythmMode = operators[0][0x14];
+ // Channel 8:
+ tomTomOperatorInNonRhythmMode = operators[0][0x12];
+ topCymbalOperatorInNonRhythmMode = operators[0][0x15];
+
+}
+
+void OPL3::initChannels2op() {
+ // The YMF262 has 18 2-op channels.
+ // Each 2-op channel can be at a serial or parallel operator configuration:
+ memset(channels2op, 0, sizeof(channels2op));
+ double startvol = FullPan ? CENTER_PANNING_POWER : 1;
+ for(int array=0; array<2; array++)
+ for(int channelNumber=0; channelNumber<3; channelNumber++) {
+ int baseAddress = (array<<8) | channelNumber;
+ // Channels 1, 2, 3 -> Operator offsets 0x0,0x3; 0x1,0x4; 0x2,0x5
+ channels2op[array][channelNumber] = new Channel2op(baseAddress, startvol, operators[array][channelNumber], operators[array][channelNumber+0x3]);
+ // Channels 4, 5, 6 -> Operator offsets 0x8,0xB; 0x9,0xC; 0xA,0xD
+ channels2op[array][channelNumber+3] = new Channel2op(baseAddress+3, startvol, operators[array][channelNumber+0x8], operators[array][channelNumber+0xB]);
+ // Channels 7, 8, 9 -> Operators 0x10,0x13; 0x11,0x14; 0x12,0x15
+ channels2op[array][channelNumber+6] = new Channel2op(baseAddress+6, startvol, operators[array][channelNumber+0x10], operators[array][channelNumber+0x13]);
+ }
+}
+
+void OPL3::initChannels4op() {
+ // The YMF262 has 3 4-op channels in each array:
+ memset(channels4op, 0, sizeof(channels4op));
+ double startvol = FullPan ? CENTER_PANNING_POWER : 1;
+ for(int array=0; array<2; array++)
+ for(int channelNumber=0; channelNumber<3; channelNumber++) {
+ int baseAddress = (array<<8) | channelNumber;
+ // Channels 1, 2, 3 -> Operators 0x0,0x3,0x8,0xB; 0x1,0x4,0x9,0xC; 0x2,0x5,0xA,0xD;
+ channels4op[array][channelNumber] = new Channel4op(baseAddress, startvol, operators[array][channelNumber], operators[array][channelNumber+0x3], operators[array][channelNumber+0x8], operators[array][channelNumber+0xB]);
+ }
+}
+
+void OPL3::initRhythmChannels() {
+}
+
+void OPL3::initChannels() {
+ // Channel is an abstract class that can be a 2-op, 4-op, rhythm or disabled channel,
+ // depending on the OPL3 configuration at the time.
+ // channels[] inits as a 2-op serial channel array:
+ for(int array=0; array<2; array++)
+ for(int i=0; i<9; i++) channels[array][i] = channels2op[array][i];
+}
+
+void OPL3::update_1_NTS1_6() {
+ int _1_nts1_6 = registers[OPL3DataStruct::_1_NTS1_6_Offset];
+ // Note Selection. This register is used in Channel.updateOperators() implementations,
+ // to calculate the channel´s Key Scale Number.
+ // The value of the actual envelope rate follows the value of
+ // OPL3.nts,Operator.keyScaleNumber and Operator.ksr
+ nts = (_1_nts1_6 & 0x40) >> 6;
+}
+
+void OPL3::update_DAM1_DVB1_RYT1_BD1_SD1_TOM1_TC1_HH1() {
+ int dam1_dvb1_ryt1_bd1_sd1_tom1_tc1_hh1 = registers[OPL3DataStruct::DAM1_DVB1_RYT1_BD1_SD1_TOM1_TC1_HH1_Offset];
+ // Depth of amplitude. This register is used in EnvelopeGenerator.getEnvelope();
+ dam = (dam1_dvb1_ryt1_bd1_sd1_tom1_tc1_hh1 & 0x80) >> 7;
+
+ // Depth of vibrato. This register is used in PhaseGenerator.getPhase();
+ dvb = (dam1_dvb1_ryt1_bd1_sd1_tom1_tc1_hh1 & 0x40) >> 6;
+
+ int new_ryt = (dam1_dvb1_ryt1_bd1_sd1_tom1_tc1_hh1 & 0x20) >> 5;
+ if(new_ryt != ryt) {
+ ryt = new_ryt;
+ setRhythmMode();
+ }
+
+ int new_bd = (dam1_dvb1_ryt1_bd1_sd1_tom1_tc1_hh1 & 0x10) >> 4;
+ if(new_bd != bd) {
+ bd = new_bd;
+ if(bd==1) {
+ bassDrumChannel.op1->keyOn();
+ bassDrumChannel.op2->keyOn();
+ }
+ }
+
+ int new_sd = (dam1_dvb1_ryt1_bd1_sd1_tom1_tc1_hh1 & 0x08) >> 3;
+ if(new_sd != sd) {
+ sd = new_sd;
+ if(sd==1) snareDrumOperator.keyOn();
+ }
+
+ int new_tom = (dam1_dvb1_ryt1_bd1_sd1_tom1_tc1_hh1 & 0x04) >> 2;
+ if(new_tom != tom) {
+ tom = new_tom;
+ if(tom==1) tomTomOperator.keyOn();
+ }
+
+ int new_tc = (dam1_dvb1_ryt1_bd1_sd1_tom1_tc1_hh1 & 0x02) >> 1;
+ if(new_tc != tc) {
+ tc = new_tc;
+ if(tc==1) topCymbalOperator.keyOn();
+ }
+
+ int new_hh = dam1_dvb1_ryt1_bd1_sd1_tom1_tc1_hh1 & 0x01;
+ if(new_hh != hh) {
+ hh = new_hh;
+ if(hh==1) highHatOperator.keyOn();
+ }
+
+}
+
+void OPL3::update_7_NEW1() {
+ int _7_new1 = registers[OPL3DataStruct::_7_NEW1_Offset];
+ // OPL2/OPL3 mode selection. This register is used in
+ // OPL3.read(), OPL3.write() and Operator.getOperatorOutput();
+ _new = (_7_new1 & 0x01);
+ if(_new==1) setEnabledChannels();
+ set4opConnections();
+ updateChannelPans();
+}
+
+void OPL3::setEnabledChannels() {
+ 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]->update_CHD1_CHC1_CHB1_CHA1_FB3_CNT1(this);
+ }
+}
+
+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() {
+ // This method is called only if _new is set.
+ int _2_connectionsel6 = registers[OPL3DataStruct::_2_CONNECTIONSEL6_Offset];
+ // 2-op/4-op channel selection. This register is used here to configure the OPL3.channels[] array.
+ connectionsel = (_2_connectionsel6 & 0x3F);
+ set4opConnections();
+}
+
+void OPL3::set4opConnections() {
+ // bits 0, 1, 2 sets respectively 2-op channels (1,4), (2,5), (3,6) to 4-op operation.
+ // bits 3, 4, 5 sets respectively 2-op channels (10,13), (11,14), (12,15) to 4-op operation.
+ for(int array=0; array<2; array++)
+ for(int i=0; i<3; i++) {
+ if(_new == 1) {
+ int shift = array*3 + i;
+ int connectionBit = (connectionsel >> shift) & 0x01;
+ if(connectionBit == 1) {
+ channels[array][i] = channels4op[array][i];
+ channels[array][i+3] = &disabledChannel;
+ channels[array][i]->updateChannel(this);
+ continue;
+ }
+ }
+ channels[array][i] = channels2op[array][i];
+ channels[array][i+3] = channels2op[array][i+3];
+ channels[array][i]->updateChannel(this);
+ channels[array][i+3]->updateChannel(this);
+ }
+}
+
+void OPL3::setRhythmMode() {
+ if(ryt==1) {
+ channels[0][6] = &bassDrumChannel;
+ channels[0][7] = &highHatSnareDrumChannel;
+ channels[0][8] = &tomTomTopCymbalChannel;
+ operators[0][0x11] = &highHatOperator;
+ operators[0][0x14] = &snareDrumOperator;
+ operators[0][0x12] = &tomTomOperator;
+ operators[0][0x15] = &topCymbalOperator;
+ }
+ else {
+ for(int i=6; i<=8; i++) channels[0][i] = channels2op[0][i];
+ operators[0][0x11] = highHatOperatorInNonRhythmMode;
+ operators[0][0x14] = snareDrumOperatorInNonRhythmMode;
+ operators[0][0x12] = tomTomOperatorInNonRhythmMode;
+ operators[0][0x15] = topCymbalOperatorInNonRhythmMode;
+ }
+ 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[(int)floor(-db * DB_TABLE_RES)];
+#endif
+}
+
+Channel::Channel (int baseAddress, double startvol) {
+ channelBaseAddress = baseAddress;
+ fnuml = fnumh = kon = block = fb = cnt = 0;
+ feedback[0] = feedback[1] = 0;
+ leftPan = rightPan = startvol;
+}
+
+void Channel::update_2_KON1_BLOCK3_FNUMH2(OPL3 *OPL3) {
+
+ int _2_kon1_block3_fnumh2 = OPL3->registers[channelBaseAddress+ChannelData::_2_KON1_BLOCK3_FNUMH2_Offset];
+
+ // Frequency Number (hi-register) and Block. These two registers, together with fnuml,
+ // sets the Channel´s base frequency;
+ block = (_2_kon1_block3_fnumh2 & 0x1C) >> 2;
+ fnumh = _2_kon1_block3_fnumh2 & 0x03;
+ updateOperators(OPL3);
+
+ // Key On. If changed, calls Channel.keyOn() / keyOff().
+ int newKon = (_2_kon1_block3_fnumh2 & 0x20) >> 5;
+ if(newKon != kon) {
+ if(newKon == 1) keyOn();
+ else keyOff();
+ kon = newKon;
+ }
+}
+
+void Channel::update_FNUML8(OPL3 *OPL3) {
+ int fnuml8 = OPL3->registers[channelBaseAddress+ChannelData::FNUML8_Offset];
+ // Frequency Number, low register.
+ fnuml = fnuml8&0xFF;
+ updateOperators(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];
+// chd = (chd1_chc1_chb1_cha1_fb3_cnt1 & 0x80) >> 7;
+// chc = (chd1_chc1_chb1_cha1_fb3_cnt1 & 0x40) >> 6;
+ chb = (chd1_chc1_chb1_cha1_fb3_cnt1 & 0x20) >> 5;
+ cha = (chd1_chc1_chb1_cha1_fb3_cnt1 & 0x10) >> 4;
+ fb = (chd1_chc1_chb1_cha1_fb3_cnt1 & 0x0E) >> 1;
+ cnt = chd1_chc1_chb1_cha1_fb3_cnt1 & 0x01;
+ updatePan(OPL3);
+ updateOperators(OPL3);
+}
+
+void Channel::updatePan(OPL3 *OPL3) {
+ if (!OPL3->FullPan)
+ {
+ if (OPL3->_new == 0)
+ {
+ leftPan = VOLUME_MUL;
+ rightPan = VOLUME_MUL;
+ }
+ else
+ {
+ leftPan = cha * VOLUME_MUL;
+ rightPan = chb * VOLUME_MUL;
+ }
+ }
+}
+
+void Channel::updateChannel(OPL3 *OPL3) {
+ update_2_KON1_BLOCK3_FNUMH2(OPL3);
+ update_FNUML8(OPL3);
+ update_CHD1_CHC1_CHB1_CHA1_FB3_CNT1(OPL3);
+}
+
+Channel2op::Channel2op (int baseAddress, double startvol, Operator *o1, Operator *o2)
+: Channel(baseAddress, startvol)
+{
+ op1 = o1;
+ op2 = o2;
+}
+
+double Channel2op::getChannelOutput(OPL3 *OPL3) {
+ double channelOutput = 0, op1Output = 0, op2Output = 0;
+ // The feedback uses the last two outputs from
+ // the first operator, instead of just the last one.
+ double feedbackOutput = (feedback[0] + feedback[1]) / 2;
+
+ switch(cnt) {
+ // CNT = 0, the operators are in series, with the first in feedback.
+ case 0:
+ if(op2->envelopeGenerator.stage==EnvelopeGenerator::OFF)
+ return 0;
+ op1Output = op1->getOperatorOutput(OPL3, feedbackOutput);
+ channelOutput = op2->getOperatorOutput(OPL3, op1Output*toPhase);
+ break;
+ // CNT = 1, the operators are in parallel, with the first in feedback.
+ case 1:
+ if(op1->envelopeGenerator.stage==EnvelopeGenerator::OFF &&
+ op2->envelopeGenerator.stage==EnvelopeGenerator::OFF)
+ return 0;
+ op1Output = op1->getOperatorOutput(OPL3, feedbackOutput);
+ op2Output = op2->getOperatorOutput(OPL3, Operator::noModulator);
+ channelOutput = (op1Output + op2Output) / 2;
+ }
+
+ feedback[0] = feedback[1];
+ feedback[1] = StripIntPart(op1Output * ChannelData::feedback[fb]);
+ return channelOutput;
+}
+
+void Channel2op::keyOn() {
+ op1->keyOn();
+ op2->keyOn();
+ feedback[0] = feedback[1] = 0;
+}
+
+void Channel2op::keyOff() {
+ op1->keyOff();
+ op2->keyOff();
+}
+
+void Channel2op::updateOperators(OPL3 *OPL3) {
+ // Key Scale Number, used in EnvelopeGenerator.setActualRates().
+ int keyScaleNumber = block*2 + ((fnumh>>OPL3->nts)&0x01);
+ int f_number = (fnumh<<8) | fnuml;
+ op1->updateOperator(OPL3, keyScaleNumber, f_number, block);
+ op2->updateOperator(OPL3, keyScaleNumber, f_number, block);
+}
+
+Channel4op::Channel4op (int baseAddress, double startvol, Operator *o1, Operator *o2, Operator *o3, Operator *o4)
+: Channel(baseAddress, startvol)
+{
+ op1 = o1;
+ op2 = o2;
+ op3 = o3;
+ op4 = o4;
+}
+
+double Channel4op::getChannelOutput(OPL3 *OPL3) {
+ double channelOutput = 0,
+ op1Output = 0, op2Output = 0, op3Output = 0, op4Output = 0;
+
+ int secondChannelBaseAddress = channelBaseAddress+3;
+ int secondCnt = OPL3->registers[secondChannelBaseAddress+ChannelData::CHD1_CHC1_CHB1_CHA1_FB3_CNT1_Offset] & 0x1;
+ int cnt4op = (cnt << 1) | secondCnt;
+
+ double feedbackOutput = (feedback[0] + feedback[1]) / 2;
+
+ switch(cnt4op) {
+ case 0:
+ if(op4->envelopeGenerator.stage==EnvelopeGenerator::OFF)
+ return 0;
+
+ op1Output = op1->getOperatorOutput(OPL3, feedbackOutput);
+ op2Output = op2->getOperatorOutput(OPL3, op1Output*toPhase);
+ op3Output = op3->getOperatorOutput(OPL3, op2Output*toPhase);
+ channelOutput = op4->getOperatorOutput(OPL3, op3Output*toPhase);
+
+ break;
+ case 1:
+ if(op2->envelopeGenerator.stage==EnvelopeGenerator::OFF &&
+ op4->envelopeGenerator.stage==EnvelopeGenerator::OFF)
+ return 0;
+
+ op1Output = op1->getOperatorOutput(OPL3, feedbackOutput);
+ op2Output = op2->getOperatorOutput(OPL3, op1Output*toPhase);
+
+ op3Output = op3->getOperatorOutput(OPL3, Operator::noModulator);
+ op4Output = op4->getOperatorOutput(OPL3, op3Output*toPhase);
+
+ channelOutput = (op2Output + op4Output) / 2;
+ break;
+ case 2:
+ if(op1->envelopeGenerator.stage==EnvelopeGenerator::OFF &&
+ op4->envelopeGenerator.stage==EnvelopeGenerator::OFF)
+ return 0;
+
+ op1Output = op1->getOperatorOutput(OPL3, feedbackOutput);
+
+ op2Output = op2->getOperatorOutput(OPL3, Operator::noModulator);
+ op3Output = op3->getOperatorOutput(OPL3, op2Output*toPhase);
+ op4Output = op4->getOperatorOutput(OPL3, op3Output*toPhase);
+
+ channelOutput = (op1Output + op4Output) / 2;
+ break;
+ case 3:
+ if(op1->envelopeGenerator.stage==EnvelopeGenerator::OFF &&
+ op3->envelopeGenerator.stage==EnvelopeGenerator::OFF &&
+ op4->envelopeGenerator.stage==EnvelopeGenerator::OFF)
+ return 0;
+
+ op1Output = op1->getOperatorOutput(OPL3, feedbackOutput);
+
+ op2Output = op2->getOperatorOutput(OPL3, Operator::noModulator);
+ op3Output = op3->getOperatorOutput(OPL3, op2Output*toPhase);
+
+ op4Output = op4->getOperatorOutput(OPL3, Operator::noModulator);
+
+ channelOutput = (op1Output + op3Output + op4Output) / 3;
+ }
+
+ feedback[0] = feedback[1];
+ feedback[1] = StripIntPart(op1Output * ChannelData::feedback[fb]);
+
+ return channelOutput;
+}
+
+void Channel4op::keyOn() {
+ op1->keyOn();
+ op2->keyOn();
+ op3->keyOn();
+ op4->keyOn();
+ feedback[0] = feedback[1] = 0;
+}
+
+void Channel4op::keyOff() {
+ op1->keyOff();
+ op2->keyOff();
+ op3->keyOff();
+ op4->keyOff();
+}
+
+void Channel4op::updateOperators(OPL3 *OPL3) {
+ // Key Scale Number, used in EnvelopeGenerator.setActualRates().
+ int keyScaleNumber = block*2 + ((fnumh>>OPL3->nts)&0x01);
+ int f_number = (fnumh<<8) | fnuml;
+ op1->updateOperator(OPL3, keyScaleNumber, f_number, block);
+ op2->updateOperator(OPL3, keyScaleNumber, f_number, block);
+ op3->updateOperator(OPL3, keyScaleNumber, f_number, block);
+ op4->updateOperator(OPL3, keyScaleNumber, f_number, block);
+}
+
+const double Operator::noModulator = 0;
+
+Operator::Operator(int baseAddress) {
+ operatorBaseAddress = baseAddress;
+
+ envelope = 0;
+ am = vib = ksr = egt = mult = ksl = tl = ar = dr = sl = rr = ws = 0;
+ keyScaleNumber = f_number = block = 0;
+}
+
+void Operator::update_AM1_VIB1_EGT1_KSR1_MULT4(OPL3 *OPL3) {
+
+ int am1_vib1_egt1_ksr1_mult4 = OPL3->registers[operatorBaseAddress+OperatorDataStruct::AM1_VIB1_EGT1_KSR1_MULT4_Offset];
+
+ // Amplitude Modulation. This register is used int EnvelopeGenerator.getEnvelope();
+ am = (am1_vib1_egt1_ksr1_mult4 & 0x80) >> 7;
+ // Vibrato. This register is used in PhaseGenerator.getPhase();
+ vib = (am1_vib1_egt1_ksr1_mult4 & 0x40) >> 6;
+ // Envelope Generator Type. This register is used in EnvelopeGenerator.getEnvelope();
+ egt = (am1_vib1_egt1_ksr1_mult4 & 0x20) >> 5;
+ // Key Scale Rate. Sets the actual envelope rate together with rate and keyScaleNumber.
+ // This register os used in EnvelopeGenerator.setActualAttackRate().
+ ksr = (am1_vib1_egt1_ksr1_mult4 & 0x10) >> 4;
+ // Multiple. Multiplies the Channel.baseFrequency to get the Operator.operatorFrequency.
+ // This register is used in PhaseGenerator.setFrequency().
+ mult = am1_vib1_egt1_ksr1_mult4 & 0x0F;
+
+ phaseGenerator.setFrequency(f_number, block, mult);
+ envelopeGenerator.setActualAttackRate(ar, ksr, keyScaleNumber);
+ envelopeGenerator.setActualDecayRate(dr, ksr, keyScaleNumber);
+ envelopeGenerator.setActualReleaseRate(rr, ksr, keyScaleNumber);
+}
+
+void Operator::update_KSL2_TL6(OPL3 *OPL3) {
+
+ int ksl2_tl6 = OPL3->registers[operatorBaseAddress+OperatorDataStruct::KSL2_TL6_Offset];
+
+ // Key Scale Level. Sets the attenuation in accordance with the octave.
+ ksl = (ksl2_tl6 & 0xC0) >> 6;
+ // Total Level. Sets the overall damping for the envelope.
+ tl = ksl2_tl6 & 0x3F;
+
+ envelopeGenerator.setAtennuation(f_number, block, ksl);
+ envelopeGenerator.setTotalLevel(tl);
+}
+
+void Operator::update_AR4_DR4(OPL3 *OPL3) {
+
+ int ar4_dr4 = OPL3->registers[operatorBaseAddress+OperatorDataStruct::AR4_DR4_Offset];
+
+ // Attack Rate.
+ ar = (ar4_dr4 & 0xF0) >> 4;
+ // Decay Rate.
+ dr = ar4_dr4 & 0x0F;
+
+ envelopeGenerator.setActualAttackRate(ar, ksr, keyScaleNumber);
+ envelopeGenerator.setActualDecayRate(dr, ksr, keyScaleNumber);
+}
+
+void Operator::update_SL4_RR4(OPL3 *OPL3) {
+
+ int sl4_rr4 = OPL3->registers[operatorBaseAddress+OperatorDataStruct::SL4_RR4_Offset];
+
+ // Sustain Level.
+ sl = (sl4_rr4 & 0xF0) >> 4;
+ // Release Rate.
+ rr = sl4_rr4 & 0x0F;
+
+ envelopeGenerator.setActualSustainLevel(sl);
+ envelopeGenerator.setActualReleaseRate(rr, ksr, keyScaleNumber);
+}
+
+void Operator::update_5_WS3(OPL3 *OPL3) {
+ int _5_ws3 = OPL3->registers[operatorBaseAddress+OperatorDataStruct::_5_WS3_Offset];
+ ws = _5_ws3 & 0x07;
+}
+
+double Operator::getOperatorOutput(OPL3 *OPL3, double modulator) {
+ if(envelopeGenerator.stage == EnvelopeGenerator::OFF) return 0;
+
+ double envelopeInDB = envelopeGenerator.getEnvelope(OPL3, egt, am);
+ envelope = EnvelopeFromDB(envelopeInDB);
+
+ // If it is in OPL2 mode, use first four waveforms only:
+ ws &= ((OPL3->_new<<2) + 3);
+ double *waveform = OPL3::OperatorData->waveforms[ws];
+
+ phase = phaseGenerator.getPhase(OPL3, vib);
+
+ double operatorOutput = getOutput(modulator, phase, waveform);
+ return operatorOutput;
+}
+
+double Operator::getOutput(double modulator, double outputPhase, double *waveform) {
+ int sampleIndex = (int)floor((outputPhase + modulator) * OperatorDataStruct::waveLength) & (OperatorDataStruct::waveLength - 1);
+ return waveform[sampleIndex] * envelope;
+}
+
+void Operator::keyOn() {
+ if(ar > 0) {
+ envelopeGenerator.keyOn();
+ phaseGenerator.keyOn();
+ }
+ else envelopeGenerator.stage = EnvelopeGenerator::OFF;
+}
+
+void Operator::keyOff() {
+ envelopeGenerator.keyOff();
+}
+
+void Operator::updateOperator(OPL3 *OPL3, int ksn, int f_num, int blk) {
+ keyScaleNumber = ksn;
+ f_number = f_num;
+ block = blk;
+ update_AM1_VIB1_EGT1_KSR1_MULT4(OPL3);
+ update_KSL2_TL6(OPL3);
+ update_AR4_DR4(OPL3);
+ update_SL4_RR4(OPL3);
+ update_5_WS3(OPL3);
+}
+
+EnvelopeGenerator::EnvelopeGenerator() {
+ stage = OFF;
+ actualAttackRate = actualDecayRate = actualReleaseRate = 0;
+ xAttackIncrement = xMinimumInAttack = 0;
+ dBdecayIncrement = 0;
+ dBreleaseIncrement = 0;
+ attenuation = totalLevel = sustainLevel = 0;
+ x = dBtoX(-96);
+ envelope = -96;
+}
+
+void EnvelopeGenerator::setActualSustainLevel(int sl) {
+ // If all SL bits are 1, sustain level is set to -93 dB:
+ if(sl == 0x0F) {
+ sustainLevel = -93;
+ return;
+ }
+ // The datasheet states that the SL formula is
+ // sustainLevel = -24*d7 -12*d6 -6*d5 -3*d4,
+ // translated as:
+ sustainLevel = -3*sl;
+}
+
+void EnvelopeGenerator::setTotalLevel(int tl) {
+ // The datasheet states that the TL formula is
+ // TL = -(24*d5 + 12*d4 + 6*d3 + 3*d2 + 1.5*d1 + 0.75*d0),
+ // translated as:
+ totalLevel = tl*-0.75;
+}
+
+void EnvelopeGenerator::setAtennuation(int f_number, int block, int ksl) {
+ int hi4bits = (f_number>>6)&0x0F;
+ switch(ksl) {
+ case 0:
+ attenuation = 0;
+ break;
+ case 1:
+ // ~3 dB/Octave
+ attenuation = OperatorDataStruct::ksl3dBtable[hi4bits][block];
+ break;
+ case 2:
+ // ~1.5 dB/Octave
+ attenuation = OperatorDataStruct::ksl3dBtable[hi4bits][block]/2;
+ break;
+ case 3:
+ // ~6 dB/Octave
+ attenuation = OperatorDataStruct::ksl3dBtable[hi4bits][block]*2;
+ }
+}
+
+void EnvelopeGenerator::setActualAttackRate(int attackRate, int ksr, int keyScaleNumber) {
+ // According to the YMF278B manual's OPL3 section, the attack curve is exponential,
+ // with a dynamic range from -96 dB to 0 dB and a resolution of 0.1875 dB
+ // per level.
+ //
+ // This method sets an attack increment and attack minimum value
+ // that creates a exponential dB curve with 'period0to100' seconds in length
+ // and 'period10to90' seconds between 10% and 90% of the curve total level.
+ actualAttackRate = calculateActualRate(attackRate, ksr, keyScaleNumber);
+ double period0to100inSeconds = EnvelopeGeneratorData::attackTimeValuesTable[actualAttackRate][0]/1000.0;
+ int period0to100inSamples = (int)(period0to100inSeconds*OPL_SAMPLE_RATE);
+ double period10to90inSeconds = EnvelopeGeneratorData::attackTimeValuesTable[actualAttackRate][1]/1000.0;
+ int period10to90inSamples = (int)(period10to90inSeconds*OPL_SAMPLE_RATE);
+ // The x increment is dictated by the period between 10% and 90%:
+ xAttackIncrement = OPL3DataStruct::calculateIncrement(percentageToX(0.1), percentageToX(0.9), period10to90inSeconds);
+ // Discover how many samples are still from the top.
+ // It cannot reach 0 dB, since x is a logarithmic parameter and would be
+ // negative infinity. So we will use -0.1875 dB as the resolution
+ // maximum.
+ //
+ // percentageToX(0.9) + samplesToTheTop*xAttackIncrement = dBToX(-0.1875); ->
+ // samplesToTheTop = (dBtoX(-0.1875) - percentageToX(0.9)) / xAttackIncrement); ->
+ // period10to100InSamples = period10to90InSamples + samplesToTheTop; ->
+ int period10to100inSamples = (int) (period10to90inSamples + (dBtoX(-0.1875) - percentageToX(0.9)) / xAttackIncrement);
+ // Discover the minimum x that, through the attackIncrement value, keeps
+ // the 10%-90% period, and reaches 0 dB at the total period:
+ xMinimumInAttack = percentageToX(0.1) - (period0to100inSamples-period10to100inSamples)*xAttackIncrement;
+}
+
+
+void EnvelopeGenerator::setActualDecayRate(int decayRate, int ksr, int keyScaleNumber) {
+ actualDecayRate = calculateActualRate(decayRate, ksr, keyScaleNumber);
+ double period10to90inSeconds = EnvelopeGeneratorData::decayAndReleaseTimeValuesTable[actualDecayRate][1]/1000.0;
+ // Differently from the attack curve, the decay/release curve is linear.
+ // The dB increment is dictated by the period between 10% and 90%:
+ dBdecayIncrement = OPL3DataStruct::calculateIncrement(percentageToDB(0.1), percentageToDB(0.9), period10to90inSeconds);
+}
+
+void EnvelopeGenerator::setActualReleaseRate(int releaseRate, int ksr, int keyScaleNumber) {
+ actualReleaseRate = calculateActualRate(releaseRate, ksr, keyScaleNumber);
+ double period10to90inSeconds = EnvelopeGeneratorData::decayAndReleaseTimeValuesTable[actualReleaseRate][1]/1000.0;
+ dBreleaseIncrement = OPL3DataStruct::calculateIncrement(percentageToDB(0.1), percentageToDB(0.9), period10to90inSeconds);
+}
+
+int EnvelopeGenerator::calculateActualRate(int rate, int ksr, int keyScaleNumber) {
+ int rof = EnvelopeGeneratorData::rateOffset[ksr][keyScaleNumber];
+ int actualRate = rate*4 + rof;
+ // If, as an example at the maximum, rate is 15 and the rate offset is 15,
+ // the value would
+ // be 75, but the maximum allowed is 63:
+ if(actualRate > 63) actualRate = 63;
+ return actualRate;
+}
+
+double EnvelopeGenerator::getEnvelope(OPL3 *OPL3, int egt, int am) {
+ // The datasheets attenuation values
+ // must be halved to match the real OPL3 output.
+ double envelopeSustainLevel = sustainLevel / 2;
+ double envelopeTremolo =
+ OPL3::OPL3Data->tremoloTable[OPL3->dam][OPL3->tremoloIndex] / 2;
+ double envelopeAttenuation = attenuation / 2;
+ double envelopeTotalLevel = totalLevel / 2;
+
+ double envelopeMinimum = -96;
+ double envelopeResolution = 0.1875;
+
+ double outputEnvelope;
+ //
+ // Envelope Generation
+ //
+ switch(stage) {
+ case ATTACK:
+ // Since the attack is exponential, it will never reach 0 dB, so
+ // we´ll work with the next to maximum in the envelope resolution.
+ if(envelope<-envelopeResolution && xAttackIncrement != -EnvelopeGeneratorData::MUGEN) {
+ // The attack is exponential.
+#if 0
+ envelope = -pow(2.0,x);
+#else
+ int index = (int)floor((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;
+ break;
+ }
+ else {
+ // It is needed here to explicitly set envelope = 0, since
+ // only the attack can have a period of
+ // 0 seconds and produce an infinity envelope increment.
+ envelope = 0;
+ stage = DECAY;
+ }
+ case DECAY:
+ // The decay and release are linear.
+ if(envelope>envelopeSustainLevel) {
+ envelope -= dBdecayIncrement;
+ break;
+ }
+ else
+ stage = SUSTAIN;
+ case SUSTAIN:
+ // The Sustain stage is mantained all the time of the Key ON,
+ // even if we are in non-sustaining mode.
+ // This is necessary because, if the key is still pressed, we can
+ // change back and forth the state of EGT, and it will release and
+ // hold again accordingly.
+ if(egt==1) break;
+ else {
+ if(envelope > envelopeMinimum)
+ envelope -= dBreleaseIncrement;
+ else stage = OFF;
+ }
+ break;
+ case RELEASE:
+ // If we have Key OFF, only here we are in the Release stage.
+ // Now, we can turn EGT back and forth and it will have no effect,i.e.,
+ // it will release inexorably to the Off stage.
+ if(envelope > envelopeMinimum)
+ envelope -= dBreleaseIncrement;
+ else stage = OFF;
+ case OFF:
+ break;
+ }
+
+ // Ongoing original envelope
+ outputEnvelope = envelope;
+
+ //Tremolo
+ if(am == 1) outputEnvelope += envelopeTremolo;
+
+ //Attenuation
+ outputEnvelope += envelopeAttenuation;
+
+ //Total Level
+ outputEnvelope += envelopeTotalLevel;
+
+ return outputEnvelope;
+}
+
+void EnvelopeGenerator::keyOn() {
+ // If we are taking it in the middle of a previous envelope,
+ // start to rise from the current level:
+ // envelope = - (2 ^ x); ->
+ // 2 ^ x = -envelope ->
+ // x = log2(-envelope); ->
+ double xCurrent = OperatorDataStruct::log2(-envelope);
+ x = xCurrent < xMinimumInAttack ? xCurrent : xMinimumInAttack;
+ stage = ATTACK;
+}
+
+void EnvelopeGenerator::keyOff() {
+ if(stage != OFF) stage = RELEASE;
+}
+
+double EnvelopeGenerator::dBtoX(double dB) {
+ return OperatorDataStruct::log2(-dB);
+}
+
+double EnvelopeGenerator::percentageToDB(double percentage) {
+ return log10(percentage) * 10.0;
+}
+
+double EnvelopeGenerator::percentageToX(double percentage) {
+ return dBtoX(percentageToDB(percentage));
+}
+
+PhaseGenerator::PhaseGenerator() {
+ phase = phaseIncrement = 0;
+}
+
+void PhaseGenerator::setFrequency(int f_number, int block, int mult) {
+ // This frequency formula is derived from the following equation:
+ // f_number = baseFrequency * pow(2,19) / OPL_SAMPLE_RATE / pow(2,block-1);
+ double baseFrequency =
+ f_number * pow(2.0, block-1) * OPL_SAMPLE_RATE / pow(2.0,19);
+ double operatorFrequency = baseFrequency*OperatorDataStruct::multTable[mult];
+
+ // phase goes from 0 to 1 at
+ // period = (1/frequency) seconds ->
+ // Samples in each period is (1/frequency)*OPL_SAMPLE_RATE =
+ // = OPL_SAMPLE_RATE/frequency ->
+ // So the increment in each sample, to go from 0 to 1, is:
+ // increment = (1-0) / samples in the period ->
+ // increment = 1 / (OPL_SAMPLE_RATE/operatorFrequency) ->
+ phaseIncrement = operatorFrequency/OPL_SAMPLE_RATE;
+}
+
+double PhaseGenerator::getPhase(OPL3 *OPL3, int vib) {
+ if(vib==1)
+ // phaseIncrement = (operatorFrequency * vibrato) / OPL_SAMPLE_RATE
+ phase += phaseIncrement*OPL3::OPL3Data->vibratoTable[OPL3->dvb][OPL3->vibratoIndex];
+ else
+ // phaseIncrement = operatorFrequency / OPL_SAMPLE_RATE
+ phase += phaseIncrement;
+ // Originally clamped phase to [0,1), but that's not needed
+ return phase;
+}
+
+void PhaseGenerator::keyOn() {
+ phase = 0;
+}
+
+double RhythmChannel::getChannelOutput(OPL3 *OPL3) {
+ double channelOutput = 0, op1Output = 0, op2Output = 0;
+
+ // Note that, different from the common channel,
+ // we do not check to see if the Operator's envelopes are Off.
+ // Instead, we always do the calculations,
+ // to update the publicly available phase.
+ op1Output = op1->getOperatorOutput(OPL3, Operator::noModulator);
+ op2Output = op2->getOperatorOutput(OPL3, Operator::noModulator);
+ channelOutput = (op1Output + op2Output) / 2;
+
+ return channelOutput;
+};
+
+TopCymbalOperator::TopCymbalOperator(int baseAddress)
+: Operator(baseAddress)
+{ }
+
+TopCymbalOperator::TopCymbalOperator()
+: Operator(topCymbalOperatorBaseAddress)
+{ }
+
+double TopCymbalOperator::getOperatorOutput(OPL3 *OPL3, double modulator) {
+ double highHatOperatorPhase =
+ OPL3->highHatOperator.phase * OperatorDataStruct::multTable[OPL3->highHatOperator.mult];
+ // The Top Cymbal operator uses its own phase together with the High Hat phase.
+ return getOperatorOutput(OPL3, modulator, highHatOperatorPhase);
+}
+
+// This method is used here with the HighHatOperator phase
+// as the externalPhase.
+// Conversely, this method is also used through inheritance by the HighHatOperator,
+// now with the TopCymbalOperator phase as the externalPhase.
+double TopCymbalOperator::getOperatorOutput(OPL3 *OPL3, double modulator, double externalPhase) {
+ double envelopeInDB = envelopeGenerator.getEnvelope(OPL3, egt, am);
+ envelope = EnvelopeFromDB(envelopeInDB);
+
+ phase = phaseGenerator.getPhase(OPL3, vib);
+
+ int waveIndex = ws & ((OPL3->_new<<2) + 3);
+ double *waveform = OPL3::OperatorData->waveforms[waveIndex];
+
+ // Empirically tested multiplied phase for the Top Cymbal:
+ double carrierPhase = 8 * phase;
+ double modulatorPhase = externalPhase;
+ double modulatorOutput = getOutput(Operator::noModulator, modulatorPhase, waveform);
+ double carrierOutput = getOutput(modulatorOutput, carrierPhase, waveform);
+
+ int cycles = 4;
+ double chopped = (carrierPhase * cycles) /* %cycles */;
+ chopped = chopped - floor(chopped / cycles) * cycles;
+ if( chopped > 0.1) carrierOutput = 0;
+
+ return carrierOutput*2;
+}
+
+HighHatOperator::HighHatOperator()
+: TopCymbalOperator(highHatOperatorBaseAddress)
+{ }
+
+double HighHatOperator::getOperatorOutput(OPL3 *OPL3, double modulator) {
+ double topCymbalOperatorPhase =
+ OPL3->topCymbalOperator.phase * OperatorDataStruct::multTable[OPL3->topCymbalOperator.mult];
+ // The sound output from the High Hat resembles the one from
+ // Top Cymbal, so we use the parent method and modify its output
+ // accordingly afterwards.
+ double operatorOutput = TopCymbalOperator::getOperatorOutput(OPL3, modulator, topCymbalOperatorPhase);
+ if(operatorOutput == 0) operatorOutput = pr_opl3.GenRand_Real1()*envelope;
+ return operatorOutput;
+}
+
+SnareDrumOperator::SnareDrumOperator()
+: Operator(snareDrumOperatorBaseAddress)
+{ }
+
+double SnareDrumOperator::getOperatorOutput(OPL3 *OPL3, double modulator) {
+ if(envelopeGenerator.stage == EnvelopeGenerator::OFF) return 0;
+
+ double envelopeInDB = envelopeGenerator.getEnvelope(OPL3, egt, am);
+ envelope = EnvelopeFromDB(envelopeInDB);
+
+ // If it is in OPL2 mode, use first four waveforms only:
+ int waveIndex = ws & ((OPL3->_new<<2) + 3);
+ double *waveform = OPL3::OperatorData->waveforms[waveIndex];
+
+ phase = OPL3->highHatOperator.phase * 2;
+
+ double operatorOutput = getOutput(modulator, phase, waveform);
+
+ double noise = pr_opl3.GenRand_Real1() * envelope;
+
+ if(operatorOutput/envelope != 1 && operatorOutput/envelope != -1) {
+ if(operatorOutput > 0) operatorOutput = noise;
+ else if(operatorOutput < 0) operatorOutput = -noise;
+ else operatorOutput = 0;
+ }
+
+ return operatorOutput*2;
+}
+
+BassDrumChannel::BassDrumChannel(double startvol)
+: Channel2op(bassDrumChannelBaseAddress, startvol, &my_op1, &my_op2),
+ my_op1(op1BaseAddress), my_op2(op2BaseAddress)
+{ }
+
+double BassDrumChannel::getChannelOutput(OPL3 *OPL3) {
+ // Bass Drum ignores first operator, when it is in series.
+ if(cnt == 1) op1->ar=0;
+ return Channel2op::getChannelOutput(OPL3);
+}
+
+void OPL3DataStruct::loadVibratoTable() {
+
+ // According to the YMF262 datasheet, the OPL3 vibrato repetition rate is 6.1 Hz.
+ // According to the YMF278B manual, it is 6.0 Hz.
+ // The information that the vibrato table has 8 levels standing 1024 samples each
+ // was taken from the emulator by Jarek Burczynski and Tatsuyuki Satoh,
+ // with a frequency of 6,06689453125 Hz, what makes sense with the difference
+ // in the information on the datasheets.
+
+ const double semitone = pow(2.0,1/12.0);
+ // A cent is 1/100 of a semitone:
+ const double cent = pow(semitone, 1/100.0);
+
+ // When dvb=0, the depth is 7 cents, when it is 1, the depth is 14 cents.
+ const double DVB0 = pow(cent,7.0);
+ const double DVB1 = pow(cent,14.0);
+ int i;
+ for(i = 0; i<1024; i++)
+ vibratoTable[0][i] = vibratoTable[1][i] = 1;
+ for(;i<2048; i++) {
+ vibratoTable[0][i] = sqrt(DVB0);
+ vibratoTable[1][i] = sqrt(DVB1);
+ }
+ for(;i<3072; i++) {
+ vibratoTable[0][i] = DVB0;
+ vibratoTable[1][i] = DVB1;
+ }
+ for(;i<4096; i++) {
+ vibratoTable[0][i] = sqrt(DVB0);
+ vibratoTable[1][i] = sqrt(DVB1);
+ }
+ for(; i<5120; i++)
+ vibratoTable[0][i] = vibratoTable[1][i] = 1;
+ for(;i<6144; i++) {
+ vibratoTable[0][i] = 1/sqrt(DVB0);
+ vibratoTable[1][i] = 1/sqrt(DVB1);
+ }
+ for(;i<7168; i++) {
+ vibratoTable[0][i] = 1/DVB0;
+ vibratoTable[1][i] = 1/DVB1;
+ }
+ for(;i<8192; i++) {
+ vibratoTable[0][i] = 1/sqrt(DVB0);
+ vibratoTable[1][i] = 1/sqrt(DVB1);
+ }
+
+}
+
+void OPL3DataStruct::loadTremoloTable()
+{
+ // The tremolo depth is -1 dB when DAM = 0, and -4.8 dB when DAM = 1.
+ static const double tremoloDepth[] = {-1, -4.8};
+
+ // According to the YMF278B manual's OPL3 section graph,
+ // the tremolo waveform is not
+ // \ / a sine wave, but a single triangle waveform.
+ // \ / Thus, the period to achieve the tremolo depth is T/2, and
+ // \ / the increment in each T/2 section uses a frequency of 2*f.
+ // \/ Tremolo varies from 0 dB to depth, to 0 dB again, at frequency*2:
+ const double tremoloIncrement[] = {
+ calculateIncrement(tremoloDepth[0],0,1/(2*tremoloFrequency)),
+ calculateIncrement(tremoloDepth[1],0,1/(2*tremoloFrequency))
+ };
+
+ int tremoloTableLength = (int)(OPL_SAMPLE_RATE/tremoloFrequency);
+
+ // This is undocumented. The tremolo starts at the maximum attenuation,
+ // instead of at 0 dB:
+ tremoloTable[0][0] = tremoloDepth[0];
+ tremoloTable[1][0] = tremoloDepth[1];
+ int counter = 0;
+ // The first half of the triangle waveform:
+ while(tremoloTable[0][counter]<0) {
+ counter++;
+ tremoloTable[0][counter] = tremoloTable[0][counter-1] + tremoloIncrement[0];
+ tremoloTable[1][counter] = tremoloTable[1][counter-1] + tremoloIncrement[1];
+ }
+ // The second half of the triangle waveform:
+ while(tremoloTable[0][counter]>tremoloDepth[0] && counter<tremoloTableLength-1) {
+ counter++;
+ tremoloTable[0][counter] = tremoloTable[0][counter-1] - tremoloIncrement[0];
+ tremoloTable[1][counter] = tremoloTable[1][counter-1] - tremoloIncrement[1];
+ }
+}
+
+void OperatorDataStruct::loadWaveforms() {
+ int i;
+ // 1st waveform: sinusoid.
+ double theta = 0, thetaIncrement = 2*M_PI / 1024;
+
+ for(i=0, theta=0; i<1024; i++, theta += thetaIncrement)
+ waveforms[0][i] = sin(theta);
+
+ double *sineTable = waveforms[0];
+ // 2nd: first half of a sinusoid.
+ for(i=0; i<512; i++) {
+ waveforms[1][i] = sineTable[i];
+ waveforms[1][512+i] = 0;
+ }
+ // 3rd: double positive sinusoid.
+ for(i=0; i<512; i++)
+ waveforms[2][i] = waveforms[2][512+i] = sineTable[i];
+ // 4th: first and third quarter of double positive sinusoid.
+ for(i=0; i<256; i++) {
+ waveforms[3][i] = waveforms[3][512+i] = sineTable[i];
+ waveforms[3][256+i] = waveforms[3][768+i] = 0;
+ }
+ // 5th: first half with double frequency sinusoid.
+ for(i=0; i<512; i++) {
+ waveforms[4][i] = sineTable[i*2];
+ waveforms[4][512+i] = 0;
+ }
+ // 6th: first half with double frequency positive sinusoid.
+ for(i=0; i<256; i++) {
+ waveforms[5][i] = waveforms[5][256+i] = sineTable[i*2];
+ waveforms[5][512+i] = waveforms[5][768+i] = 0;
+ }
+ // 7th: square wave
+ for(i=0; i<512; i++) {
+ waveforms[6][i] = 1;
+ waveforms[6][512+i] = -1;
+ }
+ // 8th: exponential
+ double x;
+ double xIncrement = 1 * 16.0 / 256.0;
+ for(i=0, x=0; i<512; i++, x+=xIncrement) {
+ waveforms[7][i] = pow(2.0,-x);
+ waveforms[7][1023-i] = -pow(2.0,-(x + 1/16.0));
+ }
+}
+
+void OperatorDataStruct::loaddBPowTable()
+{
+ for (int i = 0; i < DB_TABLE_SIZE; ++i)
+ {
+ dbpow[i] = pow(10.0, -(i / DB_TABLE_RES) / 10.0);
+ }
+}
+
+void OperatorDataStruct::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::WriteReg(int reg, int v)
+{
+ write(reg >> 8, reg & 0xFF, v);
+}
+
+void OPL3::SetPanning(int c, float left, float right)
+{
+ if (FullPan)
+ {
+ Channel *channel;
+
+ if (c < 9)
+ {
+ channel = channels[0][c];
+ }
+ else
+ {
+ channel = channels[1][c - 9];
+ }
+ channel->leftPan = left;
+ channel->rightPan = right;
+ }
+}
+
+} // JavaOPL
diff --git a/src/chips/java_opl3.cpp b/src/chips/java_opl3.cpp
new file mode 100644
index 0000000..ebf7899
--- /dev/null
+++ b/src/chips/java_opl3.cpp
@@ -0,0 +1,94 @@
+/*
+ * Interfaces over Yamaha OPL3 (YMF262) chip emulators
+ *
+ * Copyright (c) 2017-2019 Vitaly Novichkov (Wohlstand)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "java_opl3.h"
+#include "java/OPL3.cpp"
+
+JavaOPL3::JavaOPL3() :
+ OPLChipBaseBufferedT(),
+ m_chip(new JavaOPL::OPL3(true))
+{
+ reset();
+}
+
+JavaOPL3::~JavaOPL3()
+{
+ JavaOPL::OPL3 *chip_r = reinterpret_cast<JavaOPL::OPL3 *>(m_chip);
+ delete chip_r;
+}
+
+void JavaOPL3::setRate(uint32_t rate)
+{
+ OPLChipBaseBufferedT::setRate(rate);
+ JavaOPL::OPL3 *chip_r = reinterpret_cast<JavaOPL::OPL3 *>(m_chip);
+ chip_r->Reset();
+}
+
+void JavaOPL3::reset()
+{
+ OPLChipBaseBufferedT::reset();
+ JavaOPL::OPL3 *chip_r = reinterpret_cast<JavaOPL::OPL3 *>(m_chip);
+ chip_r->Reset();
+}
+
+void JavaOPL3::writeReg(uint16_t addr, uint8_t data)
+{
+ JavaOPL::OPL3 *chip_r = reinterpret_cast<JavaOPL::OPL3 *>(m_chip);
+ chip_r->WriteReg(addr, data);
+}
+
+void JavaOPL3::writePan(uint16_t addr, uint8_t data)
+{
+ JavaOPL::OPL3 *chip_r = reinterpret_cast<JavaOPL::OPL3 *>(m_chip);
+ // TODO panning
+}
+
+void JavaOPL3::nativeGenerateN(int16_t *output, size_t frames)
+{
+ JavaOPL::OPL3 *chip_r = reinterpret_cast<JavaOPL::OPL3 *>(m_chip);
+
+ enum { maxframes = 256 };
+
+ float buf[2 * maxframes];
+ while(frames > 0)
+ {
+ memset(buf, 0, sizeof(buf));
+
+ size_t curframes = (frames < maxframes) ? frames : maxframes;
+ chip_r->Update(buf, curframes);
+
+ size_t cursamples = 2 * curframes;
+ for(size_t i = 0; i < cursamples; ++i)
+ {
+ int32_t sample = (int32_t)lround(32768 * buf[i]);
+ sample = (sample > -32768) ? sample : -32768;
+ sample = (sample < +32767) ? sample : +32767;
+ output[i] = (int16_t)sample;
+ }
+
+ output += cursamples;
+ frames -= curframes;
+ }
+}
+
+const char *JavaOPL3::emulatorName()
+{
+ return "Java 1.0.6 OPL3";
+}
diff --git a/src/chips/java_opl3.h b/src/chips/java_opl3.h
new file mode 100644
index 0000000..51b5be2
--- /dev/null
+++ b/src/chips/java_opl3.h
@@ -0,0 +1,44 @@
+/*
+ * Interfaces over Yamaha OPL3 (YMF262) chip emulators
+ *
+ * Copyright (c) 2017-2019 Vitaly Novichkov (Wohlstand)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef JAVA_OPL3_H
+#define JAVA_OPL3_H
+
+#include "opl_chip_base.h"
+
+class JavaOPL3 final : public OPLChipBaseBufferedT<JavaOPL3>
+{
+ void *m_chip;
+public:
+ JavaOPL3();
+ ~JavaOPL3() override;
+
+ bool canRunAtPcmRate() const override { return false; }
+ void setRate(uint32_t rate) override;
+ void reset() override;
+ void writeReg(uint16_t addr, uint8_t data) override;
+ void writePan(uint16_t addr, uint8_t data) override;
+ void nativePreGenerate() override {}
+ void nativePostGenerate() override {}
+ void nativeGenerateN(int16_t *output, size_t frames) override;
+ const char *emulatorName() override;
+};
+
+#endif // JAVA_OPL3_H
diff --git a/utils/midiplay/adlmidiplay.cpp b/utils/midiplay/adlmidiplay.cpp
index 7de8a94..414b774 100644
--- a/utils/midiplay/adlmidiplay.cpp
+++ b/utils/midiplay/adlmidiplay.cpp
@@ -252,6 +252,7 @@ int main(int argc, char **argv)
" --emu-nuked7 Uses Nuked OPL3 v 1.7.4 emulator\n"
" --emu-dosbox Uses DosBox 0.74 OPL3 emulator\n"
" --emu-opal Uses Opal OPL3 emulator\n"
+ " --emu-java Uses Java OPL3 emulator\n"
#endif
"\n"
"Where <bank> - number of embeeded bank or filepath to custom WOPL bank file\n"
@@ -406,8 +407,8 @@ int main(int argc, char **argv)
emulator = ADLMIDI_EMU_NUKED_174;
else if(!std::strcmp("--emu-dosbox", argv[2]))
emulator = ADLMIDI_EMU_DOSBOX;
- else if(!std::strcmp("--emu-opal", argv[2]))
- emulator = ADLMIDI_EMU_OPAL;
+ else if(!std::strcmp("--emu-java", argv[2]))
+ emulator = ADLMIDI_EMU_JAVA;
#endif
else if(!std::strcmp("-fp", argv[2]))
adl_setSoftPanEnabled(myDevice, 1);