From 376fafcc1953e4c29e308466c5f35e9d537e4085 Mon Sep 17 00:00:00 2001 From: Roman Fomin Date: Sun, 29 Oct 2023 18:23:58 +0700 Subject: [PATCH 1/5] Replace DosBox OPL emulator with Nuke OPL3 --- prboom2/src/CMakeLists.txt | 4 +- prboom2/src/MUSIC/opl.c | 110 +-- prboom2/src/MUSIC/opl.h | 10 +- prboom2/src/MUSIC/opl3.c | 1387 ++++++++++++++++++++++++++++++ prboom2/src/MUSIC/opl3.h | 150 ++++ prboom2/src/dsda/configuration.c | 4 + prboom2/src/dsda/configuration.h | 1 + prboom2/src/m_misc.c | 1 + 8 files changed, 1614 insertions(+), 53 deletions(-) create mode 100644 prboom2/src/MUSIC/opl3.c create mode 100644 prboom2/src/MUSIC/opl3.h diff --git a/prboom2/src/CMakeLists.txt b/prboom2/src/CMakeLists.txt index 39ee29b36..f79c8f956 100644 --- a/prboom2/src/CMakeLists.txt +++ b/prboom2/src/CMakeLists.txt @@ -448,8 +448,6 @@ set(SDLDOOM_SOURCES ) set(DOOMMUSIC_SOURCES - MUSIC/dbopl.c - MUSIC/dbopl.h MUSIC/dumbplayer.c MUSIC/dumbplayer.h MUSIC/flplayer.c @@ -461,6 +459,8 @@ set(DOOMMUSIC_SOURCES MUSIC/musicplayer.h MUSIC/opl.c MUSIC/opl.h + MUSIC/opl3.c + MUSIC/opl3.h MUSIC/oplplayer.c MUSIC/oplplayer.h MUSIC/opl_queue.c diff --git a/prboom2/src/MUSIC/opl.c b/prboom2/src/MUSIC/opl.c index 9e8d6e968..9d10e5ef1 100644 --- a/prboom2/src/MUSIC/opl.c +++ b/prboom2/src/MUSIC/opl.c @@ -28,9 +28,9 @@ #include #include +#include "opl3.h" #include "opl.h" #include "opl_queue.h" -#include "dbopl.h" #include "dsda/configuration.h" @@ -62,10 +62,8 @@ static int opl_paused; static unsigned int pause_offset; // OPL software emulator structure. -static Chip opl_chip; - -// Temporary mixing buffer used by the mixing callback. -static int *mix_buffer = NULL; +static opl3_chip opl_chip; +static int opl_opl3mode; // Register number that was written. static int register_num = 0; @@ -74,13 +72,11 @@ static int register_num = 0; static opl_timer_t timer1 = { 12500, 0, 0, 0 }; static opl_timer_t timer2 = { 3125, 0, 0, 0 }; -static int mus_opl_gain; - // Initialize the OPL library. Returns true if initialized // successfully. int OPL_Init (unsigned int rate) { - mus_opl_gain = dsda_IntConfig(dsda_config_mus_opl_gain); + opl_opl3mode = dsda_IntConfig(dsda_config_mus_opl_opl3mode); opl_sample_rate = rate; opl_paused = 0; @@ -91,16 +87,10 @@ int OPL_Init (unsigned int rate) callback_queue = OPL_Queue_Create(); current_time = 0; - - mix_buffer = (int*)malloc(opl_sample_rate * sizeof(int)); - // Create the emulator structure: + OPL3_Reset(&opl_chip, opl_sample_rate); - DBOPL_InitTables(); - Chip__Chip(&opl_chip); - Chip__Setup(&opl_chip, opl_sample_rate); - - OPL_InitRegisters(); + OPL_InitRegisters(opl_opl3mode); init_stage_reg_writes = 0; @@ -113,10 +103,8 @@ void OPL_Shutdown(void) if (callback_queue) { OPL_Queue_Destroy(callback_queue); - free(mix_buffer); callback_queue = NULL; - mix_buffer = NULL; } } @@ -184,8 +172,11 @@ static void WriteRegister(unsigned int reg_num, unsigned int value) break; + case OPL_REG_NEW: + opl_opl3mode = value & 0x01; + default: - Chip__WriteReg(&opl_chip, reg_num, (unsigned char) value); + OPL3_WriteRegBuffered(&opl_chip, reg_num, value); break; } } @@ -215,39 +206,13 @@ static void OPL_AdvanceTime(unsigned int nsamples) } callback(callback_data); - - } - -} - -static void FillBuffer(int16_t *buffer, unsigned int nsamples) -{ - unsigned int i; - int sampval; - - // FIXME??? - //assert(nsamples < opl_sample_rate); - - Chip__GenerateBlock2(&opl_chip, nsamples, mix_buffer); - - // Mix into the destination buffer, doubling up into stereo. - for (i=0; i 32767) - sampval = 32767; - else if (sampval < -32768) - sampval = -32768; - buffer[i * 2] = (int16_t) sampval; - buffer[i * 2 + 1] = (int16_t) sampval; } } void OPL_Render_Samples (void *dest, unsigned buffer_len) { unsigned int filled = 0; - short *buffer = (short *) dest; + int16_t *buffer = (int16_t *) dest; // Repeatedly call the OPL emulator update function until the buffer is // full. @@ -276,7 +241,7 @@ void OPL_Render_Samples (void *dest, unsigned buffer_len) } // Add emulator output to buffer. - FillBuffer(buffer + filled * 2, nsamples); + OPL3_GenerateStream(&opl_chip, buffer + filled * 2, nsamples); filled += nsamples; // Invoke callbacks for this point in time. @@ -290,6 +255,10 @@ void OPL_WritePort(opl_port_t port, unsigned int value) { register_num = value; } + else if (port == OPL_REGISTER_PORT_OPL3) + { + register_num = value | 0x100; + } else if (port == OPL_DATA_PORT) { WriteRegister(register_num, value); @@ -300,6 +269,11 @@ unsigned int OPL_ReadPort(opl_port_t port) { unsigned int result = 0; + if (port == OPL_REGISTER_PORT_OPL3) + { + return 0xff; + } + if (timer1.enabled && current_time > timer1.expire_time) { result |= 0x80; // Either have expired @@ -329,7 +303,14 @@ void OPL_WriteRegister(int reg, int value) { int i; - OPL_WritePort(OPL_REGISTER_PORT, reg); + if (reg & 0x100) + { + OPL_WritePort(OPL_REGISTER_PORT_OPL3, reg); + } + else + { + OPL_WritePort(OPL_REGISTER_PORT, reg); + } // For timing, read the register port six times after writing the // register number to cause the appropriate delay @@ -359,7 +340,7 @@ void OPL_WriteRegister(int reg, int value) } // Initialize registers on startup -void OPL_InitRegisters(void) +void OPL_InitRegisters(int opl3) { int r; @@ -393,8 +374,39 @@ void OPL_InitRegisters(void) // "Allow FM chips to control the waveform of each operator": OPL_WriteRegister(OPL_REG_WAVEFORM_ENABLE, 0x20); + if (opl3) + { + OPL_WriteRegister(OPL_REG_NEW, 0x01); + + // Initialize level registers + for (r=OPL_REGS_LEVEL; r <= OPL_REGS_LEVEL + OPL_NUM_OPERATORS; ++r) + { + OPL_WriteRegister(r | 0x100, 0x3f); + } + + // Initialize other registers + // These two loops write to registers that actually don't exist, + // but this is what Doom does ... + // Similarly, the <= is also intenational. + for (r=OPL_REGS_ATTACK; r <= OPL_REGS_WAVEFORM + OPL_NUM_OPERATORS; ++r) + { + OPL_WriteRegister(r | 0x100, 0x00); + } + + // More registers ... + for (r=1; r < OPL_REGS_LEVEL; ++r) + { + OPL_WriteRegister(r | 0x100, 0x00); + } + } + // Keyboard split point on (?) OPL_WriteRegister(OPL_REG_FM_MODE, 0x40); + + if (opl3) + { + OPL_WriteRegister(OPL_REG_NEW, 0x01); + } } void OPL_SetPaused(int paused) diff --git a/prboom2/src/MUSIC/opl.h b/prboom2/src/MUSIC/opl.h index 6a9ccd1af..39f70192f 100644 --- a/prboom2/src/MUSIC/opl.h +++ b/prboom2/src/MUSIC/opl.h @@ -32,7 +32,8 @@ typedef void (*opl_callback_t)(void *data); typedef enum { OPL_REGISTER_PORT = 0, - OPL_DATA_PORT = 1 + OPL_DATA_PORT = 1, + OPL_REGISTER_PORT_OPL3 = 2 } opl_port_t; #define OPL_NUM_OPERATORS 21 @@ -43,6 +44,7 @@ typedef enum #define OPL_REG_TIMER2 0x03 #define OPL_REG_TIMER_CTRL 0x04 #define OPL_REG_FM_MODE 0x08 +#define OPL_REG_NEW 0x105 // Operator registers (21 of each): @@ -58,6 +60,10 @@ typedef enum #define OPL_REGS_FREQ_2 0xB0 #define OPL_REGS_FEEDBACK 0xC0 +// Times + +#define OPL_SECOND ((uint64_t) 1000 * 1000) + // // Low-level functions. // @@ -98,7 +104,7 @@ int OPL_Detect(void); // Initialize all registers, performed on startup. -void OPL_InitRegisters(void); +void OPL_InitRegisters(int opl3); // Block until the specified number of milliseconds have elapsed. diff --git a/prboom2/src/MUSIC/opl3.c b/prboom2/src/MUSIC/opl3.c new file mode 100644 index 000000000..25acd991c --- /dev/null +++ b/prboom2/src/MUSIC/opl3.c @@ -0,0 +1,1387 @@ +// +// Copyright (C) 2013-2018 Alexey Khokholov (Nuke.YKT) +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// This program 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 General Public License for more details. +// +// +// Nuked OPL3 emulator. +// Thanks: +// MAME Development Team(Jarek Burczynski, Tatsuyuki Satoh): +// Feedback and Rhythm part calculation information. +// forums.submarine.org.uk(carbon14, opl3): +// Tremolo and phase generator calculation information. +// OPLx decapsulated(Matthew Gambrell, Olli Niemitalo): +// OPL2 ROMs. +// siliconpr0n.org(John McMaster, digshadow): +// YMF262 and VRC VII decaps and die shots. +// +// version: 1.8 +// + +#include +#include +#include +#include "opl3.h" + +#include "dsda/configuration.h" +static int mus_opl_gain; + +#define RSM_FRAC 10 + +// Channel types + +enum { + ch_2op = 0, + ch_4op = 1, + ch_4op2 = 2, + ch_drum = 3 +}; + +// Envelope key types + +enum { + egk_norm = 0x01, + egk_drum = 0x02 +}; + + +// +// logsin table +// + +static const Bit16u logsinrom[256] = { + 0x859, 0x6c3, 0x607, 0x58b, 0x52e, 0x4e4, 0x4a6, 0x471, + 0x443, 0x41a, 0x3f5, 0x3d3, 0x3b5, 0x398, 0x37e, 0x365, + 0x34e, 0x339, 0x324, 0x311, 0x2ff, 0x2ed, 0x2dc, 0x2cd, + 0x2bd, 0x2af, 0x2a0, 0x293, 0x286, 0x279, 0x26d, 0x261, + 0x256, 0x24b, 0x240, 0x236, 0x22c, 0x222, 0x218, 0x20f, + 0x206, 0x1fd, 0x1f5, 0x1ec, 0x1e4, 0x1dc, 0x1d4, 0x1cd, + 0x1c5, 0x1be, 0x1b7, 0x1b0, 0x1a9, 0x1a2, 0x19b, 0x195, + 0x18f, 0x188, 0x182, 0x17c, 0x177, 0x171, 0x16b, 0x166, + 0x160, 0x15b, 0x155, 0x150, 0x14b, 0x146, 0x141, 0x13c, + 0x137, 0x133, 0x12e, 0x129, 0x125, 0x121, 0x11c, 0x118, + 0x114, 0x10f, 0x10b, 0x107, 0x103, 0x0ff, 0x0fb, 0x0f8, + 0x0f4, 0x0f0, 0x0ec, 0x0e9, 0x0e5, 0x0e2, 0x0de, 0x0db, + 0x0d7, 0x0d4, 0x0d1, 0x0cd, 0x0ca, 0x0c7, 0x0c4, 0x0c1, + 0x0be, 0x0bb, 0x0b8, 0x0b5, 0x0b2, 0x0af, 0x0ac, 0x0a9, + 0x0a7, 0x0a4, 0x0a1, 0x09f, 0x09c, 0x099, 0x097, 0x094, + 0x092, 0x08f, 0x08d, 0x08a, 0x088, 0x086, 0x083, 0x081, + 0x07f, 0x07d, 0x07a, 0x078, 0x076, 0x074, 0x072, 0x070, + 0x06e, 0x06c, 0x06a, 0x068, 0x066, 0x064, 0x062, 0x060, + 0x05e, 0x05c, 0x05b, 0x059, 0x057, 0x055, 0x053, 0x052, + 0x050, 0x04e, 0x04d, 0x04b, 0x04a, 0x048, 0x046, 0x045, + 0x043, 0x042, 0x040, 0x03f, 0x03e, 0x03c, 0x03b, 0x039, + 0x038, 0x037, 0x035, 0x034, 0x033, 0x031, 0x030, 0x02f, + 0x02e, 0x02d, 0x02b, 0x02a, 0x029, 0x028, 0x027, 0x026, + 0x025, 0x024, 0x023, 0x022, 0x021, 0x020, 0x01f, 0x01e, + 0x01d, 0x01c, 0x01b, 0x01a, 0x019, 0x018, 0x017, 0x017, + 0x016, 0x015, 0x014, 0x014, 0x013, 0x012, 0x011, 0x011, + 0x010, 0x00f, 0x00f, 0x00e, 0x00d, 0x00d, 0x00c, 0x00c, + 0x00b, 0x00a, 0x00a, 0x009, 0x009, 0x008, 0x008, 0x007, + 0x007, 0x007, 0x006, 0x006, 0x005, 0x005, 0x005, 0x004, + 0x004, 0x004, 0x003, 0x003, 0x003, 0x002, 0x002, 0x002, + 0x002, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, + 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000 +}; + +// +// exp table +// + +static const Bit16u exprom[256] = { + 0x7fa, 0x7f5, 0x7ef, 0x7ea, 0x7e4, 0x7df, 0x7da, 0x7d4, + 0x7cf, 0x7c9, 0x7c4, 0x7bf, 0x7b9, 0x7b4, 0x7ae, 0x7a9, + 0x7a4, 0x79f, 0x799, 0x794, 0x78f, 0x78a, 0x784, 0x77f, + 0x77a, 0x775, 0x770, 0x76a, 0x765, 0x760, 0x75b, 0x756, + 0x751, 0x74c, 0x747, 0x742, 0x73d, 0x738, 0x733, 0x72e, + 0x729, 0x724, 0x71f, 0x71a, 0x715, 0x710, 0x70b, 0x706, + 0x702, 0x6fd, 0x6f8, 0x6f3, 0x6ee, 0x6e9, 0x6e5, 0x6e0, + 0x6db, 0x6d6, 0x6d2, 0x6cd, 0x6c8, 0x6c4, 0x6bf, 0x6ba, + 0x6b5, 0x6b1, 0x6ac, 0x6a8, 0x6a3, 0x69e, 0x69a, 0x695, + 0x691, 0x68c, 0x688, 0x683, 0x67f, 0x67a, 0x676, 0x671, + 0x66d, 0x668, 0x664, 0x65f, 0x65b, 0x657, 0x652, 0x64e, + 0x649, 0x645, 0x641, 0x63c, 0x638, 0x634, 0x630, 0x62b, + 0x627, 0x623, 0x61e, 0x61a, 0x616, 0x612, 0x60e, 0x609, + 0x605, 0x601, 0x5fd, 0x5f9, 0x5f5, 0x5f0, 0x5ec, 0x5e8, + 0x5e4, 0x5e0, 0x5dc, 0x5d8, 0x5d4, 0x5d0, 0x5cc, 0x5c8, + 0x5c4, 0x5c0, 0x5bc, 0x5b8, 0x5b4, 0x5b0, 0x5ac, 0x5a8, + 0x5a4, 0x5a0, 0x59c, 0x599, 0x595, 0x591, 0x58d, 0x589, + 0x585, 0x581, 0x57e, 0x57a, 0x576, 0x572, 0x56f, 0x56b, + 0x567, 0x563, 0x560, 0x55c, 0x558, 0x554, 0x551, 0x54d, + 0x549, 0x546, 0x542, 0x53e, 0x53b, 0x537, 0x534, 0x530, + 0x52c, 0x529, 0x525, 0x522, 0x51e, 0x51b, 0x517, 0x514, + 0x510, 0x50c, 0x509, 0x506, 0x502, 0x4ff, 0x4fb, 0x4f8, + 0x4f4, 0x4f1, 0x4ed, 0x4ea, 0x4e7, 0x4e3, 0x4e0, 0x4dc, + 0x4d9, 0x4d6, 0x4d2, 0x4cf, 0x4cc, 0x4c8, 0x4c5, 0x4c2, + 0x4be, 0x4bb, 0x4b8, 0x4b5, 0x4b1, 0x4ae, 0x4ab, 0x4a8, + 0x4a4, 0x4a1, 0x49e, 0x49b, 0x498, 0x494, 0x491, 0x48e, + 0x48b, 0x488, 0x485, 0x482, 0x47e, 0x47b, 0x478, 0x475, + 0x472, 0x46f, 0x46c, 0x469, 0x466, 0x463, 0x460, 0x45d, + 0x45a, 0x457, 0x454, 0x451, 0x44e, 0x44b, 0x448, 0x445, + 0x442, 0x43f, 0x43c, 0x439, 0x436, 0x433, 0x430, 0x42d, + 0x42a, 0x428, 0x425, 0x422, 0x41f, 0x41c, 0x419, 0x416, + 0x414, 0x411, 0x40e, 0x40b, 0x408, 0x406, 0x403, 0x400 +}; + +// +// freq mult table multiplied by 2 +// +// 1/2, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 10, 12, 12, 15, 15 +// + +static const Bit8u mt[16] = { + 1, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 20, 24, 24, 30, 30 +}; + +// +// ksl table +// + +static const Bit8u kslrom[16] = { + 0, 32, 40, 45, 48, 51, 53, 55, 56, 58, 59, 60, 61, 62, 63, 64 +}; + +static const Bit8u kslshift[4] = { + 8, 1, 2, 0 +}; + +// +// envelope generator constants +// + +static const Bit8u eg_incstep[4][4] = { + { 0, 0, 0, 0 }, + { 1, 0, 0, 0 }, + { 1, 0, 1, 0 }, + { 1, 1, 1, 0 } +}; + +// +// address decoding +// + +static const Bit8s ad_slot[0x20] = { + 0, 1, 2, 3, 4, 5, -1, -1, 6, 7, 8, 9, 10, 11, -1, -1, + 12, 13, 14, 15, 16, 17, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 +}; + +static const Bit8u ch_slot[18] = { + 0, 1, 2, 6, 7, 8, 12, 13, 14, 18, 19, 20, 24, 25, 26, 30, 31, 32 +}; + +// +// Envelope generator +// + +typedef Bit16s(*envelope_sinfunc)(Bit16u phase, Bit16u envelope); +typedef void(*envelope_genfunc)(opl3_slot *slott); + +static Bit16s OPL3_EnvelopeCalcExp(Bit32u level) +{ + if (level > 0x1fff) + { + level = 0x1fff; + } + return (exprom[level & 0xff] << 1) >> (level >> 8); +} + +static Bit16s OPL3_EnvelopeCalcSin0(Bit16u phase, Bit16u envelope) +{ + Bit16u out = 0; + Bit16u neg = 0; + phase &= 0x3ff; + if (phase & 0x200) + { + neg = 0xffff; + } + if (phase & 0x100) + { + out = logsinrom[(phase & 0xff) ^ 0xff]; + } + else + { + out = logsinrom[phase & 0xff]; + } + return OPL3_EnvelopeCalcExp(out + (envelope << 3)) ^ neg; +} + +static Bit16s OPL3_EnvelopeCalcSin1(Bit16u phase, Bit16u envelope) +{ + Bit16u out = 0; + phase &= 0x3ff; + if (phase & 0x200) + { + out = 0x1000; + } + else if (phase & 0x100) + { + out = logsinrom[(phase & 0xff) ^ 0xff]; + } + else + { + out = logsinrom[phase & 0xff]; + } + return OPL3_EnvelopeCalcExp(out + (envelope << 3)); +} + +static Bit16s OPL3_EnvelopeCalcSin2(Bit16u phase, Bit16u envelope) +{ + Bit16u out = 0; + phase &= 0x3ff; + if (phase & 0x100) + { + out = logsinrom[(phase & 0xff) ^ 0xff]; + } + else + { + out = logsinrom[phase & 0xff]; + } + return OPL3_EnvelopeCalcExp(out + (envelope << 3)); +} + +static Bit16s OPL3_EnvelopeCalcSin3(Bit16u phase, Bit16u envelope) +{ + Bit16u out = 0; + phase &= 0x3ff; + if (phase & 0x100) + { + out = 0x1000; + } + else + { + out = logsinrom[phase & 0xff]; + } + return OPL3_EnvelopeCalcExp(out + (envelope << 3)); +} + +static Bit16s OPL3_EnvelopeCalcSin4(Bit16u phase, Bit16u envelope) +{ + Bit16u out = 0; + Bit16u neg = 0; + phase &= 0x3ff; + if ((phase & 0x300) == 0x100) + { + neg = 0xffff; + } + if (phase & 0x200) + { + out = 0x1000; + } + else if (phase & 0x80) + { + out = logsinrom[((phase ^ 0xff) << 1) & 0xff]; + } + else + { + out = logsinrom[(phase << 1) & 0xff]; + } + return OPL3_EnvelopeCalcExp(out + (envelope << 3)) ^ neg; +} + +static Bit16s OPL3_EnvelopeCalcSin5(Bit16u phase, Bit16u envelope) +{ + Bit16u out = 0; + phase &= 0x3ff; + if (phase & 0x200) + { + out = 0x1000; + } + else if (phase & 0x80) + { + out = logsinrom[((phase ^ 0xff) << 1) & 0xff]; + } + else + { + out = logsinrom[(phase << 1) & 0xff]; + } + return OPL3_EnvelopeCalcExp(out + (envelope << 3)); +} + +static Bit16s OPL3_EnvelopeCalcSin6(Bit16u phase, Bit16u envelope) +{ + Bit16u neg = 0; + phase &= 0x3ff; + if (phase & 0x200) + { + neg = 0xffff; + } + return OPL3_EnvelopeCalcExp(envelope << 3) ^ neg; +} + +static Bit16s OPL3_EnvelopeCalcSin7(Bit16u phase, Bit16u envelope) +{ + Bit16u out = 0; + Bit16u neg = 0; + phase &= 0x3ff; + if (phase & 0x200) + { + neg = 0xffff; + phase = (phase & 0x1ff) ^ 0x1ff; + } + out = phase << 3; + return OPL3_EnvelopeCalcExp(out + (envelope << 3)) ^ neg; +} + +static const envelope_sinfunc envelope_sin[8] = { + OPL3_EnvelopeCalcSin0, + OPL3_EnvelopeCalcSin1, + OPL3_EnvelopeCalcSin2, + OPL3_EnvelopeCalcSin3, + OPL3_EnvelopeCalcSin4, + OPL3_EnvelopeCalcSin5, + OPL3_EnvelopeCalcSin6, + OPL3_EnvelopeCalcSin7 +}; + +enum envelope_gen_num +{ + envelope_gen_num_attack = 0, + envelope_gen_num_decay = 1, + envelope_gen_num_sustain = 2, + envelope_gen_num_release = 3 +}; + +static void OPL3_EnvelopeUpdateKSL(opl3_slot *slot) +{ + Bit16s ksl = (kslrom[slot->channel->f_num >> 6] << 2) + - ((0x08 - slot->channel->block) << 5); + if (ksl < 0) + { + ksl = 0; + } + slot->eg_ksl = (Bit8u)ksl; +} + +static void OPL3_EnvelopeCalc(opl3_slot *slot) +{ + Bit8u nonzero; + Bit8u rate; + Bit8u rate_hi; + Bit8u rate_lo; + Bit8u reg_rate = 0; + Bit8u ks; + Bit8u eg_shift, shift; + Bit16u eg_rout; + Bit16s eg_inc; + Bit8u eg_off; + Bit8u reset = 0; + slot->eg_out = slot->eg_rout + (slot->reg_tl << 2) + + (slot->eg_ksl >> kslshift[slot->reg_ksl]) + *slot->trem; + if (slot->key && slot->eg_gen == envelope_gen_num_release) + { + reset = 1; + reg_rate = slot->reg_ar; + } + else + { + switch (slot->eg_gen) + { + case envelope_gen_num_attack: + reg_rate = slot->reg_ar; + break; + case envelope_gen_num_decay: + reg_rate = slot->reg_dr; + break; + case envelope_gen_num_sustain: + if (!slot->reg_type) + { + reg_rate = slot->reg_rr; + } + break; + case envelope_gen_num_release: + reg_rate = slot->reg_rr; + break; + } + } + slot->pg_reset = reset; + ks = slot->channel->ksv >> ((slot->reg_ksr ^ 1) << 1); + nonzero = (reg_rate != 0); + rate = ks + (reg_rate << 2); + rate_hi = rate >> 2; + rate_lo = rate & 0x03; + if (rate_hi & 0x10) + { + rate_hi = 0x0f; + } + eg_shift = rate_hi + slot->chip->eg_add; + shift = 0; + if (nonzero) + { + if (rate_hi < 12) + { + if (slot->chip->eg_state) + { + switch (eg_shift) + { + case 12: + shift = 1; + break; + case 13: + shift = (rate_lo >> 1) & 0x01; + break; + case 14: + shift = rate_lo & 0x01; + break; + default: + break; + } + } + } + else + { + shift = (rate_hi & 0x03) + eg_incstep[rate_lo][slot->chip->timer & 0x03]; + if (shift & 0x04) + { + shift = 0x03; + } + if (!shift) + { + shift = slot->chip->eg_state; + } + } + } + eg_rout = slot->eg_rout; + eg_inc = 0; + eg_off = 0; + // Instant attack + if (reset && rate_hi == 0x0f) + { + eg_rout = 0x00; + } + // Envelope off + if ((slot->eg_rout & 0x1f8) == 0x1f8) + { + eg_off = 1; + } + if (slot->eg_gen != envelope_gen_num_attack && !reset && eg_off) + { + eg_rout = 0x1ff; + } + switch (slot->eg_gen) + { + case envelope_gen_num_attack: + if (!slot->eg_rout) + { + slot->eg_gen = envelope_gen_num_decay; + } + else if (slot->key && shift > 0 && rate_hi != 0x0f) + { + eg_inc = ((~slot->eg_rout) << shift) >> 4; + } + break; + case envelope_gen_num_decay: + if ((slot->eg_rout >> 4) == slot->reg_sl) + { + slot->eg_gen = envelope_gen_num_sustain; + } + else if (!eg_off && !reset && shift > 0) + { + eg_inc = 1 << (shift - 1); + } + break; + case envelope_gen_num_sustain: + case envelope_gen_num_release: + if (!eg_off && !reset && shift > 0) + { + eg_inc = 1 << (shift - 1); + } + break; + } + slot->eg_rout = (eg_rout + eg_inc) & 0x1ff; + // Key off + if (reset) + { + slot->eg_gen = envelope_gen_num_attack; + } + if (!slot->key) + { + slot->eg_gen = envelope_gen_num_release; + } +} + +static void OPL3_EnvelopeKeyOn(opl3_slot *slot, Bit8u type) +{ + slot->key |= type; +} + +static void OPL3_EnvelopeKeyOff(opl3_slot *slot, Bit8u type) +{ + slot->key &= ~type; +} + +// +// Phase Generator +// + +static void OPL3_PhaseGenerate(opl3_slot *slot) +{ + opl3_chip *chip; + Bit16u f_num; + Bit32u basefreq; + Bit8u rm_xor, n_bit; + Bit32u noise; + Bit16u phase; + + chip = slot->chip; + f_num = slot->channel->f_num; + if (slot->reg_vib) + { + Bit8s range; + Bit8u vibpos; + + range = (f_num >> 7) & 7; + vibpos = slot->chip->vibpos; + + if (!(vibpos & 3)) + { + range = 0; + } + else if (vibpos & 1) + { + range >>= 1; + } + range >>= slot->chip->vibshift; + + if (vibpos & 4) + { + range = -range; + } + f_num += range; + } + basefreq = (f_num << slot->channel->block) >> 1; + phase = (Bit16u)(slot->pg_phase >> 9); + if (slot->pg_reset) + { + slot->pg_phase = 0; + } + slot->pg_phase += (basefreq * mt[slot->reg_mult]) >> 1; + // Rhythm mode + noise = chip->noise; + slot->pg_phase_out = phase; + if (slot->slot_num == 13) // hh + { + chip->rm_hh_bit2 = (phase >> 2) & 1; + chip->rm_hh_bit3 = (phase >> 3) & 1; + chip->rm_hh_bit7 = (phase >> 7) & 1; + chip->rm_hh_bit8 = (phase >> 8) & 1; + } + if (slot->slot_num == 17 && (chip->rhy & 0x20)) // tc + { + chip->rm_tc_bit3 = (phase >> 3) & 1; + chip->rm_tc_bit5 = (phase >> 5) & 1; + } + if (chip->rhy & 0x20) + { + rm_xor = (chip->rm_hh_bit2 ^ chip->rm_hh_bit7) + | (chip->rm_hh_bit3 ^ chip->rm_tc_bit5) + | (chip->rm_tc_bit3 ^ chip->rm_tc_bit5); + switch (slot->slot_num) + { + case 13: // hh + slot->pg_phase_out = rm_xor << 9; + if (rm_xor ^ (noise & 1)) + { + slot->pg_phase_out |= 0xd0; + } + else + { + slot->pg_phase_out |= 0x34; + } + break; + case 16: // sd + slot->pg_phase_out = (chip->rm_hh_bit8 << 9) + | ((chip->rm_hh_bit8 ^ (noise & 1)) << 8); + break; + case 17: // tc + slot->pg_phase_out = (rm_xor << 9) | 0x80; + break; + default: + break; + } + } + n_bit = ((noise >> 14) ^ noise) & 0x01; + chip->noise = (noise >> 1) | (n_bit << 22); +} + +// +// Slot +// + +static void OPL3_SlotWrite20(opl3_slot *slot, Bit8u data) +{ + if ((data >> 7) & 0x01) + { + slot->trem = &slot->chip->tremolo; + } + else + { + slot->trem = (Bit8u*)&slot->chip->zeromod; + } + slot->reg_vib = (data >> 6) & 0x01; + slot->reg_type = (data >> 5) & 0x01; + slot->reg_ksr = (data >> 4) & 0x01; + slot->reg_mult = data & 0x0f; +} + +static void OPL3_SlotWrite40(opl3_slot *slot, Bit8u data) +{ + slot->reg_ksl = (data >> 6) & 0x03; + slot->reg_tl = data & 0x3f; + OPL3_EnvelopeUpdateKSL(slot); +} + +static void OPL3_SlotWrite60(opl3_slot *slot, Bit8u data) +{ + slot->reg_ar = (data >> 4) & 0x0f; + slot->reg_dr = data & 0x0f; +} + +static void OPL3_SlotWrite80(opl3_slot *slot, Bit8u data) +{ + slot->reg_sl = (data >> 4) & 0x0f; + if (slot->reg_sl == 0x0f) + { + slot->reg_sl = 0x1f; + } + slot->reg_rr = data & 0x0f; +} + +static void OPL3_SlotWriteE0(opl3_slot *slot, Bit8u data) +{ + slot->reg_wf = data & 0x07; + if (slot->chip->newm == 0x00) + { + slot->reg_wf &= 0x03; + } +} + +static void OPL3_SlotGenerate(opl3_slot *slot) +{ + slot->out = envelope_sin[slot->reg_wf](slot->pg_phase_out + *slot->mod, slot->eg_out); +} + +static void OPL3_SlotCalcFB(opl3_slot *slot) +{ + if (slot->channel->fb != 0x00) + { + slot->fbmod = (slot->prout + slot->out) >> (0x09 - slot->channel->fb); + } + else + { + slot->fbmod = 0; + } + slot->prout = slot->out; +} + +// +// Channel +// + +static void OPL3_ChannelSetupAlg(opl3_channel *channel); + +static void OPL3_ChannelUpdateRhythm(opl3_chip *chip, Bit8u data) +{ + opl3_channel *channel6; + opl3_channel *channel7; + opl3_channel *channel8; + Bit8u chnum; + + chip->rhy = data & 0x3f; + if (chip->rhy & 0x20) + { + channel6 = &chip->channel[6]; + channel7 = &chip->channel[7]; + channel8 = &chip->channel[8]; + channel6->out[0] = &channel6->slots[1]->out; + channel6->out[1] = &channel6->slots[1]->out; + channel6->out[2] = &chip->zeromod; + channel6->out[3] = &chip->zeromod; + channel7->out[0] = &channel7->slots[0]->out; + channel7->out[1] = &channel7->slots[0]->out; + channel7->out[2] = &channel7->slots[1]->out; + channel7->out[3] = &channel7->slots[1]->out; + channel8->out[0] = &channel8->slots[0]->out; + channel8->out[1] = &channel8->slots[0]->out; + channel8->out[2] = &channel8->slots[1]->out; + channel8->out[3] = &channel8->slots[1]->out; + for (chnum = 6; chnum < 9; chnum++) + { + chip->channel[chnum].chtype = ch_drum; + } + OPL3_ChannelSetupAlg(channel6); + OPL3_ChannelSetupAlg(channel7); + OPL3_ChannelSetupAlg(channel8); + //hh + if (chip->rhy & 0x01) + { + OPL3_EnvelopeKeyOn(channel7->slots[0], egk_drum); + } + else + { + OPL3_EnvelopeKeyOff(channel7->slots[0], egk_drum); + } + //tc + if (chip->rhy & 0x02) + { + OPL3_EnvelopeKeyOn(channel8->slots[1], egk_drum); + } + else + { + OPL3_EnvelopeKeyOff(channel8->slots[1], egk_drum); + } + //tom + if (chip->rhy & 0x04) + { + OPL3_EnvelopeKeyOn(channel8->slots[0], egk_drum); + } + else + { + OPL3_EnvelopeKeyOff(channel8->slots[0], egk_drum); + } + //sd + if (chip->rhy & 0x08) + { + OPL3_EnvelopeKeyOn(channel7->slots[1], egk_drum); + } + else + { + OPL3_EnvelopeKeyOff(channel7->slots[1], egk_drum); + } + //bd + if (chip->rhy & 0x10) + { + OPL3_EnvelopeKeyOn(channel6->slots[0], egk_drum); + OPL3_EnvelopeKeyOn(channel6->slots[1], egk_drum); + } + else + { + OPL3_EnvelopeKeyOff(channel6->slots[0], egk_drum); + OPL3_EnvelopeKeyOff(channel6->slots[1], egk_drum); + } + } + else + { + for (chnum = 6; chnum < 9; chnum++) + { + chip->channel[chnum].chtype = ch_2op; + OPL3_ChannelSetupAlg(&chip->channel[chnum]); + OPL3_EnvelopeKeyOff(chip->channel[chnum].slots[0], egk_drum); + OPL3_EnvelopeKeyOff(chip->channel[chnum].slots[1], egk_drum); + } + } +} + +static void OPL3_ChannelWriteA0(opl3_channel *channel, Bit8u data) +{ + if (channel->chip->newm && channel->chtype == ch_4op2) + { + return; + } + channel->f_num = (channel->f_num & 0x300) | data; + channel->ksv = (channel->block << 1) + | ((channel->f_num >> (0x09 - channel->chip->nts)) & 0x01); + OPL3_EnvelopeUpdateKSL(channel->slots[0]); + OPL3_EnvelopeUpdateKSL(channel->slots[1]); + if (channel->chip->newm && channel->chtype == ch_4op) + { + channel->pair->f_num = channel->f_num; + channel->pair->ksv = channel->ksv; + OPL3_EnvelopeUpdateKSL(channel->pair->slots[0]); + OPL3_EnvelopeUpdateKSL(channel->pair->slots[1]); + } +} + +static void OPL3_ChannelWriteB0(opl3_channel *channel, Bit8u data) +{ + if (channel->chip->newm && channel->chtype == ch_4op2) + { + return; + } + channel->f_num = (channel->f_num & 0xff) | ((data & 0x03) << 8); + channel->block = (data >> 2) & 0x07; + channel->ksv = (channel->block << 1) + | ((channel->f_num >> (0x09 - channel->chip->nts)) & 0x01); + OPL3_EnvelopeUpdateKSL(channel->slots[0]); + OPL3_EnvelopeUpdateKSL(channel->slots[1]); + if (channel->chip->newm && channel->chtype == ch_4op) + { + channel->pair->f_num = channel->f_num; + channel->pair->block = channel->block; + channel->pair->ksv = channel->ksv; + OPL3_EnvelopeUpdateKSL(channel->pair->slots[0]); + OPL3_EnvelopeUpdateKSL(channel->pair->slots[1]); + } +} + +static void OPL3_ChannelSetupAlg(opl3_channel *channel) +{ + if (channel->chtype == ch_drum) + { + if (channel->ch_num == 7 || channel->ch_num == 8) + { + channel->slots[0]->mod = &channel->chip->zeromod; + channel->slots[1]->mod = &channel->chip->zeromod; + return; + } + switch (channel->alg & 0x01) + { + case 0x00: + channel->slots[0]->mod = &channel->slots[0]->fbmod; + channel->slots[1]->mod = &channel->slots[0]->out; + break; + case 0x01: + channel->slots[0]->mod = &channel->slots[0]->fbmod; + channel->slots[1]->mod = &channel->chip->zeromod; + break; + } + return; + } + if (channel->alg & 0x08) + { + return; + } + if (channel->alg & 0x04) + { + channel->pair->out[0] = &channel->chip->zeromod; + channel->pair->out[1] = &channel->chip->zeromod; + channel->pair->out[2] = &channel->chip->zeromod; + channel->pair->out[3] = &channel->chip->zeromod; + switch (channel->alg & 0x03) + { + case 0x00: + channel->pair->slots[0]->mod = &channel->pair->slots[0]->fbmod; + channel->pair->slots[1]->mod = &channel->pair->slots[0]->out; + channel->slots[0]->mod = &channel->pair->slots[1]->out; + channel->slots[1]->mod = &channel->slots[0]->out; + channel->out[0] = &channel->slots[1]->out; + channel->out[1] = &channel->chip->zeromod; + channel->out[2] = &channel->chip->zeromod; + channel->out[3] = &channel->chip->zeromod; + break; + case 0x01: + channel->pair->slots[0]->mod = &channel->pair->slots[0]->fbmod; + channel->pair->slots[1]->mod = &channel->pair->slots[0]->out; + channel->slots[0]->mod = &channel->chip->zeromod; + channel->slots[1]->mod = &channel->slots[0]->out; + channel->out[0] = &channel->pair->slots[1]->out; + channel->out[1] = &channel->slots[1]->out; + channel->out[2] = &channel->chip->zeromod; + channel->out[3] = &channel->chip->zeromod; + break; + case 0x02: + channel->pair->slots[0]->mod = &channel->pair->slots[0]->fbmod; + channel->pair->slots[1]->mod = &channel->chip->zeromod; + channel->slots[0]->mod = &channel->pair->slots[1]->out; + channel->slots[1]->mod = &channel->slots[0]->out; + channel->out[0] = &channel->pair->slots[0]->out; + channel->out[1] = &channel->slots[1]->out; + channel->out[2] = &channel->chip->zeromod; + channel->out[3] = &channel->chip->zeromod; + break; + case 0x03: + channel->pair->slots[0]->mod = &channel->pair->slots[0]->fbmod; + channel->pair->slots[1]->mod = &channel->chip->zeromod; + channel->slots[0]->mod = &channel->pair->slots[1]->out; + channel->slots[1]->mod = &channel->chip->zeromod; + channel->out[0] = &channel->pair->slots[0]->out; + channel->out[1] = &channel->slots[0]->out; + channel->out[2] = &channel->slots[1]->out; + channel->out[3] = &channel->chip->zeromod; + break; + } + } + else + { + switch (channel->alg & 0x01) + { + case 0x00: + channel->slots[0]->mod = &channel->slots[0]->fbmod; + channel->slots[1]->mod = &channel->slots[0]->out; + channel->out[0] = &channel->slots[1]->out; + channel->out[1] = &channel->chip->zeromod; + channel->out[2] = &channel->chip->zeromod; + channel->out[3] = &channel->chip->zeromod; + break; + case 0x01: + channel->slots[0]->mod = &channel->slots[0]->fbmod; + channel->slots[1]->mod = &channel->chip->zeromod; + channel->out[0] = &channel->slots[0]->out; + channel->out[1] = &channel->slots[1]->out; + channel->out[2] = &channel->chip->zeromod; + channel->out[3] = &channel->chip->zeromod; + break; + } + } +} + +static void OPL3_ChannelWriteC0(opl3_channel *channel, Bit8u data) +{ + channel->fb = (data & 0x0e) >> 1; + channel->con = data & 0x01; + channel->alg = channel->con; + if (channel->chip->newm) + { + if (channel->chtype == ch_4op) + { + channel->pair->alg = 0x04 | (channel->con << 1) | (channel->pair->con); + channel->alg = 0x08; + OPL3_ChannelSetupAlg(channel->pair); + } + else if (channel->chtype == ch_4op2) + { + channel->alg = 0x04 | (channel->pair->con << 1) | (channel->con); + channel->pair->alg = 0x08; + OPL3_ChannelSetupAlg(channel); + } + else + { + OPL3_ChannelSetupAlg(channel); + } + } + else + { + OPL3_ChannelSetupAlg(channel); + } + if (channel->chip->newm) + { + channel->cha = ((data >> 4) & 0x01) ? ~0 : 0; + channel->chb = ((data >> 5) & 0x01) ? ~0 : 0; + } + else + { + channel->cha = channel->chb = (Bit16u)~0; + } +} + +static void OPL3_ChannelKeyOn(opl3_channel *channel) +{ + if (channel->chip->newm) + { + if (channel->chtype == ch_4op) + { + OPL3_EnvelopeKeyOn(channel->slots[0], egk_norm); + OPL3_EnvelopeKeyOn(channel->slots[1], egk_norm); + OPL3_EnvelopeKeyOn(channel->pair->slots[0], egk_norm); + OPL3_EnvelopeKeyOn(channel->pair->slots[1], egk_norm); + } + else if (channel->chtype == ch_2op || channel->chtype == ch_drum) + { + OPL3_EnvelopeKeyOn(channel->slots[0], egk_norm); + OPL3_EnvelopeKeyOn(channel->slots[1], egk_norm); + } + } + else + { + OPL3_EnvelopeKeyOn(channel->slots[0], egk_norm); + OPL3_EnvelopeKeyOn(channel->slots[1], egk_norm); + } +} + +static void OPL3_ChannelKeyOff(opl3_channel *channel) +{ + if (channel->chip->newm) + { + if (channel->chtype == ch_4op) + { + OPL3_EnvelopeKeyOff(channel->slots[0], egk_norm); + OPL3_EnvelopeKeyOff(channel->slots[1], egk_norm); + OPL3_EnvelopeKeyOff(channel->pair->slots[0], egk_norm); + OPL3_EnvelopeKeyOff(channel->pair->slots[1], egk_norm); + } + else if (channel->chtype == ch_2op || channel->chtype == ch_drum) + { + OPL3_EnvelopeKeyOff(channel->slots[0], egk_norm); + OPL3_EnvelopeKeyOff(channel->slots[1], egk_norm); + } + } + else + { + OPL3_EnvelopeKeyOff(channel->slots[0], egk_norm); + OPL3_EnvelopeKeyOff(channel->slots[1], egk_norm); + } +} + +static void OPL3_ChannelSet4Op(opl3_chip *chip, Bit8u data) +{ + Bit8u bit; + Bit8u chnum; + for (bit = 0; bit < 6; bit++) + { + chnum = bit; + if (bit >= 3) + { + chnum += 9 - 3; + } + if ((data >> bit) & 0x01) + { + chip->channel[chnum].chtype = ch_4op; + chip->channel[chnum + 3].chtype = ch_4op2; + } + else + { + chip->channel[chnum].chtype = ch_2op; + chip->channel[chnum + 3].chtype = ch_2op; + } + } +} + +static Bit16s OPL3_ClipSample(Bit32s sample) +{ + if (sample > 32767) + { + sample = 32767; + } + else if (sample < -32768) + { + sample = -32768; + } + return (Bit16s)sample; +} + +void OPL3_Generate(opl3_chip *chip, Bit16s *buf) +{ + Bit8u ii; + Bit8u jj; + Bit16s accm; + Bit8u shift = 0; + + buf[1] = OPL3_ClipSample(chip->mixbuff[1]); + + for (ii = 0; ii < 15; ii++) + { + OPL3_SlotCalcFB(&chip->slot[ii]); + OPL3_EnvelopeCalc(&chip->slot[ii]); + OPL3_PhaseGenerate(&chip->slot[ii]); + OPL3_SlotGenerate(&chip->slot[ii]); + } + + chip->mixbuff[0] = 0; + for (ii = 0; ii < 18; ii++) + { + accm = 0; + for (jj = 0; jj < 4; jj++) + { + accm += *chip->channel[ii].out[jj]; + } + chip->mixbuff[0] += (Bit16s)(accm & chip->channel[ii].cha); + } + + for (ii = 15; ii < 18; ii++) + { + OPL3_SlotCalcFB(&chip->slot[ii]); + OPL3_EnvelopeCalc(&chip->slot[ii]); + OPL3_PhaseGenerate(&chip->slot[ii]); + OPL3_SlotGenerate(&chip->slot[ii]); + } + + buf[0] = OPL3_ClipSample(chip->mixbuff[0]); + + for (ii = 18; ii < 33; ii++) + { + OPL3_SlotCalcFB(&chip->slot[ii]); + OPL3_EnvelopeCalc(&chip->slot[ii]); + OPL3_PhaseGenerate(&chip->slot[ii]); + OPL3_SlotGenerate(&chip->slot[ii]); + } + + chip->mixbuff[1] = 0; + for (ii = 0; ii < 18; ii++) + { + accm = 0; + for (jj = 0; jj < 4; jj++) + { + accm += *chip->channel[ii].out[jj]; + } + chip->mixbuff[1] += (Bit16s)(accm & chip->channel[ii].chb); + } + + for (ii = 33; ii < 36; ii++) + { + OPL3_SlotCalcFB(&chip->slot[ii]); + OPL3_EnvelopeCalc(&chip->slot[ii]); + OPL3_PhaseGenerate(&chip->slot[ii]); + OPL3_SlotGenerate(&chip->slot[ii]); + } + + if ((chip->timer & 0x3f) == 0x3f) + { + chip->tremolopos = (chip->tremolopos + 1) % 210; + } + if (chip->tremolopos < 105) + { + chip->tremolo = chip->tremolopos >> chip->tremoloshift; + } + else + { + chip->tremolo = (210 - chip->tremolopos) >> chip->tremoloshift; + } + + if ((chip->timer & 0x3ff) == 0x3ff) + { + chip->vibpos = (chip->vibpos + 1) & 7; + } + + chip->timer++; + + chip->eg_add = 0; + if (chip->eg_timer) + { + while (shift < 36 && ((chip->eg_timer >> shift) & 1) == 0) + { + shift++; + } + if (shift > 12) + { + chip->eg_add = 0; + } + else + { + chip->eg_add = shift + 1; + } + } + + if (chip->eg_timerrem || chip->eg_state) + { + if (chip->eg_timer == 0xfffffffff) + { + chip->eg_timer = 0; + chip->eg_timerrem = 1; + } + else + { + chip->eg_timer++; + chip->eg_timerrem = 0; + } + } + + chip->eg_state ^= 1; + + while (chip->writebuf[chip->writebuf_cur].time <= chip->writebuf_samplecnt) + { + if (!(chip->writebuf[chip->writebuf_cur].reg & 0x200)) + { + break; + } + chip->writebuf[chip->writebuf_cur].reg &= 0x1ff; + OPL3_WriteReg(chip, chip->writebuf[chip->writebuf_cur].reg, + chip->writebuf[chip->writebuf_cur].data); + chip->writebuf_cur = (chip->writebuf_cur + 1) % OPL_WRITEBUF_SIZE; + } + chip->writebuf_samplecnt++; +} + +void OPL3_GenerateResampled(opl3_chip *chip, Bit16s *buf) +{ + while (chip->samplecnt >= chip->rateratio) + { + chip->oldsamples[0] = chip->samples[0]; + chip->oldsamples[1] = chip->samples[1]; + OPL3_Generate(chip, chip->samples); + chip->samplecnt -= chip->rateratio; + } + buf[0] = (Bit16s)((chip->oldsamples[0] * (chip->rateratio - chip->samplecnt) + + chip->samples[0] * chip->samplecnt) / chip->rateratio); + buf[1] = (Bit16s)((chip->oldsamples[1] * (chip->rateratio - chip->samplecnt) + + chip->samples[1] * chip->samplecnt) / chip->rateratio); + chip->samplecnt += 1 << RSM_FRAC; +} + +void OPL3_Reset(opl3_chip *chip, Bit32u samplerate) +{ + Bit8u slotnum; + Bit8u channum; + + memset(chip, 0, sizeof(opl3_chip)); + for (slotnum = 0; slotnum < 36; slotnum++) + { + chip->slot[slotnum].chip = chip; + chip->slot[slotnum].mod = &chip->zeromod; + chip->slot[slotnum].eg_rout = 0x1ff; + chip->slot[slotnum].eg_out = 0x1ff; + chip->slot[slotnum].eg_gen = envelope_gen_num_release; + chip->slot[slotnum].trem = (Bit8u*)&chip->zeromod; + chip->slot[slotnum].slot_num = slotnum; + } + for (channum = 0; channum < 18; channum++) + { + chip->channel[channum].slots[0] = &chip->slot[ch_slot[channum]]; + chip->channel[channum].slots[1] = &chip->slot[ch_slot[channum] + 3]; + chip->slot[ch_slot[channum]].channel = &chip->channel[channum]; + chip->slot[ch_slot[channum] + 3].channel = &chip->channel[channum]; + if ((channum % 9) < 3) + { + chip->channel[channum].pair = &chip->channel[channum + 3]; + } + else if ((channum % 9) < 6) + { + chip->channel[channum].pair = &chip->channel[channum - 3]; + } + chip->channel[channum].chip = chip; + chip->channel[channum].out[0] = &chip->zeromod; + chip->channel[channum].out[1] = &chip->zeromod; + chip->channel[channum].out[2] = &chip->zeromod; + chip->channel[channum].out[3] = &chip->zeromod; + chip->channel[channum].chtype = ch_2op; + chip->channel[channum].cha = 0xffff; + chip->channel[channum].chb = 0xffff; + chip->channel[channum].ch_num = channum; + OPL3_ChannelSetupAlg(&chip->channel[channum]); + } + chip->noise = 1; + chip->rateratio = (samplerate << RSM_FRAC) / 49716; + chip->tremoloshift = 4; + chip->vibshift = 1; + + mus_opl_gain = dsda_IntConfig(dsda_config_mus_opl_gain); +} + +void OPL3_WriteReg(opl3_chip *chip, Bit16u reg, Bit8u v) +{ + Bit8u high = (reg >> 8) & 0x01; + Bit8u regm = reg & 0xff; + switch (regm & 0xf0) + { + case 0x00: + if (high) + { + switch (regm & 0x0f) + { + case 0x04: + OPL3_ChannelSet4Op(chip, v); + break; + case 0x05: + chip->newm = v & 0x01; + break; + } + } + else + { + switch (regm & 0x0f) + { + case 0x08: + chip->nts = (v >> 6) & 0x01; + break; + } + } + break; + case 0x20: + case 0x30: + if (ad_slot[regm & 0x1f] >= 0) + { + OPL3_SlotWrite20(&chip->slot[18 * high + ad_slot[regm & 0x1f]], v); + } + break; + case 0x40: + case 0x50: + if (ad_slot[regm & 0x1f] >= 0) + { + OPL3_SlotWrite40(&chip->slot[18 * high + ad_slot[regm & 0x1f]], v); + } + break; + case 0x60: + case 0x70: + if (ad_slot[regm & 0x1f] >= 0) + { + OPL3_SlotWrite60(&chip->slot[18 * high + ad_slot[regm & 0x1f]], v); + } + break; + case 0x80: + case 0x90: + if (ad_slot[regm & 0x1f] >= 0) + { + OPL3_SlotWrite80(&chip->slot[18 * high + ad_slot[regm & 0x1f]], v); + } + break; + case 0xe0: + case 0xf0: + if (ad_slot[regm & 0x1f] >= 0) + { + OPL3_SlotWriteE0(&chip->slot[18 * high + ad_slot[regm & 0x1f]], v); + } + break; + case 0xa0: + if ((regm & 0x0f) < 9) + { + OPL3_ChannelWriteA0(&chip->channel[9 * high + (regm & 0x0f)], v); + } + break; + case 0xb0: + if (regm == 0xbd && !high) + { + chip->tremoloshift = (((v >> 7) ^ 1) << 1) + 2; + chip->vibshift = ((v >> 6) & 0x01) ^ 1; + OPL3_ChannelUpdateRhythm(chip, v); + } + else if ((regm & 0x0f) < 9) + { + OPL3_ChannelWriteB0(&chip->channel[9 * high + (regm & 0x0f)], v); + if (v & 0x20) + { + OPL3_ChannelKeyOn(&chip->channel[9 * high + (regm & 0x0f)]); + } + else + { + OPL3_ChannelKeyOff(&chip->channel[9 * high + (regm & 0x0f)]); + } + } + break; + case 0xc0: + if ((regm & 0x0f) < 9) + { + OPL3_ChannelWriteC0(&chip->channel[9 * high + (regm & 0x0f)], v); + } + break; + } +} + +void OPL3_WriteRegBuffered(opl3_chip *chip, Bit16u reg, Bit8u v) +{ + Bit64u time1, time2; + + if (chip->writebuf[chip->writebuf_last].reg & 0x200) + { + OPL3_WriteReg(chip, chip->writebuf[chip->writebuf_last].reg & 0x1ff, + chip->writebuf[chip->writebuf_last].data); + + chip->writebuf_cur = (chip->writebuf_last + 1) % OPL_WRITEBUF_SIZE; + chip->writebuf_samplecnt = chip->writebuf[chip->writebuf_last].time; + } + + chip->writebuf[chip->writebuf_last].reg = reg | 0x200; + chip->writebuf[chip->writebuf_last].data = v; + time1 = chip->writebuf_lasttime + OPL_WRITEBUF_DELAY; + time2 = chip->writebuf_samplecnt; + + if (time1 < time2) + { + time1 = time2; + } + + chip->writebuf[chip->writebuf_last].time = time1; + chip->writebuf_lasttime = time1; + chip->writebuf_last = (chip->writebuf_last + 1) % OPL_WRITEBUF_SIZE; +} + +void OPL3_GenerateStream(opl3_chip *chip, Bit16s *sndptr, Bit32u numsamples) +{ + Bit32u i; + + for(i = 0; i < numsamples; i++) + { + Bit32s sample; + OPL3_GenerateResampled(chip, sndptr); + sample = sndptr[0] * mus_opl_gain / 50; + sndptr[0] = OPL3_ClipSample(sample); + sample = sndptr[1] * mus_opl_gain / 50; + sndptr[1] = OPL3_ClipSample(sample); + sndptr += 2; + } +} diff --git a/prboom2/src/MUSIC/opl3.h b/prboom2/src/MUSIC/opl3.h new file mode 100644 index 000000000..2a64037a5 --- /dev/null +++ b/prboom2/src/MUSIC/opl3.h @@ -0,0 +1,150 @@ +// +// Copyright (C) 2013-2018 Alexey Khokholov (Nuke.YKT) +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// This program 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 General Public License for more details. +// +// +// Nuked OPL3 emulator. +// Thanks: +// MAME Development Team(Jarek Burczynski, Tatsuyuki Satoh): +// Feedback and Rhythm part calculation information. +// forums.submarine.org.uk(carbon14, opl3): +// Tremolo and phase generator calculation information. +// OPLx decapsulated(Matthew Gambrell, Olli Niemitalo): +// OPL2 ROMs. +// siliconpr0n.org(John McMaster, digshadow): +// YMF262 and VRC VII decaps and die shots. +// +// version: 1.8 +// + +#ifndef OPL_OPL3_H +#define OPL_OPL3_H + +#include + +#define OPL_WRITEBUF_SIZE 1024 +#define OPL_WRITEBUF_DELAY 2 + +typedef uintptr_t Bitu; +typedef intptr_t Bits; +typedef uint64_t Bit64u; +typedef int64_t Bit64s; +typedef uint32_t Bit32u; +typedef int32_t Bit32s; +typedef uint16_t Bit16u; +typedef int16_t Bit16s; +typedef uint8_t Bit8u; +typedef int8_t Bit8s; + +typedef struct _opl3_slot opl3_slot; +typedef struct _opl3_channel opl3_channel; +typedef struct _opl3_chip opl3_chip; + +struct _opl3_slot { + opl3_channel *channel; + opl3_chip *chip; + Bit16s out; + Bit16s fbmod; + Bit16s *mod; + Bit16s prout; + Bit16s eg_rout; + Bit16s eg_out; + Bit8u eg_inc; + Bit8u eg_gen; + Bit8u eg_rate; + Bit8u eg_ksl; + Bit8u *trem; + Bit8u reg_vib; + Bit8u reg_type; + Bit8u reg_ksr; + Bit8u reg_mult; + Bit8u reg_ksl; + Bit8u reg_tl; + Bit8u reg_ar; + Bit8u reg_dr; + Bit8u reg_sl; + Bit8u reg_rr; + Bit8u reg_wf; + Bit8u key; + Bit32u pg_reset; + Bit32u pg_phase; + Bit16u pg_phase_out; + Bit8u slot_num; +}; + +struct _opl3_channel { + opl3_slot *slots[2]; + opl3_channel *pair; + opl3_chip *chip; + Bit16s *out[4]; + Bit8u chtype; + Bit16u f_num; + Bit8u block; + Bit8u fb; + Bit8u con; + Bit8u alg; + Bit8u ksv; + Bit16u cha, chb; + Bit8u ch_num; +}; + +typedef struct _opl3_writebuf { + Bit64u time; + Bit16u reg; + Bit8u data; +} opl3_writebuf; + +struct _opl3_chip { + opl3_channel channel[18]; + opl3_slot slot[36]; + Bit16u timer; + Bit64u eg_timer; + Bit8u eg_timerrem; + Bit8u eg_state; + Bit8u eg_add; + Bit8u newm; + Bit8u nts; + Bit8u rhy; + Bit8u vibpos; + Bit8u vibshift; + Bit8u tremolo; + Bit8u tremolopos; + Bit8u tremoloshift; + Bit32u noise; + Bit16s zeromod; + Bit32s mixbuff[2]; + Bit8u rm_hh_bit2; + Bit8u rm_hh_bit3; + Bit8u rm_hh_bit7; + Bit8u rm_hh_bit8; + Bit8u rm_tc_bit3; + Bit8u rm_tc_bit5; + //OPL3L + Bit32s rateratio; + Bit32s samplecnt; + Bit16s oldsamples[2]; + Bit16s samples[2]; + + Bit64u writebuf_samplecnt; + Bit32u writebuf_cur; + Bit32u writebuf_last; + Bit64u writebuf_lasttime; + opl3_writebuf writebuf[OPL_WRITEBUF_SIZE]; +}; + +void OPL3_Generate(opl3_chip *chip, Bit16s *buf); +void OPL3_GenerateResampled(opl3_chip *chip, Bit16s *buf); +void OPL3_Reset(opl3_chip *chip, Bit32u samplerate); +void OPL3_WriteReg(opl3_chip *chip, Bit16u reg, Bit8u v); +void OPL3_WriteRegBuffered(opl3_chip *chip, Bit16u reg, Bit8u v); +void OPL3_GenerateStream(opl3_chip *chip, Bit16s *sndptr, Bit32u numsamples); +#endif diff --git a/prboom2/src/dsda/configuration.c b/prboom2/src/dsda/configuration.c index fdbe95a66..812231826 100644 --- a/prboom2/src/dsda/configuration.c +++ b/prboom2/src/dsda/configuration.c @@ -756,6 +756,10 @@ dsda_config_t dsda_config[dsda_config_count] = { "mus_opl_gain", dsda_config_mus_opl_gain, dsda_config_int, 0, 1000, { 50 } }, + [dsda_config_mus_opl_opl3mode] = { + "mus_opl_opl3mode", dsda_config_mus_opl_opl3mode, + CONF_BOOL(1) + }, [dsda_config_mus_portmidi_reset_type] = { "mus_portmidi_reset_type", dsda_config_mus_portmidi_reset_type, CONF_STRING("gm") // none, gs, gm, gm2, xg diff --git a/prboom2/src/dsda/configuration.h b/prboom2/src/dsda/configuration.h index e21b68a96..a69795578 100644 --- a/prboom2/src/dsda/configuration.h +++ b/prboom2/src/dsda/configuration.h @@ -160,6 +160,7 @@ typedef enum { dsda_config_mus_fluidsynth_reverb_width, dsda_config_mus_fluidsynth_reverb_room_size, dsda_config_mus_opl_gain, + dsda_config_mus_opl_opl3mode, dsda_config_mus_portmidi_reset_type, dsda_config_mus_portmidi_reset_delay, dsda_config_mus_portmidi_filter_sysex, diff --git a/prboom2/src/m_misc.c b/prboom2/src/m_misc.c index 83221a838..62b7160a1 100644 --- a/prboom2/src/m_misc.c +++ b/prboom2/src/m_misc.c @@ -129,6 +129,7 @@ cfg_def_t cfg_defs[] = MIGRATED_SETTING(dsda_config_mus_fluidsynth_reverb_width), MIGRATED_SETTING(dsda_config_mus_fluidsynth_reverb_room_size), MIGRATED_SETTING(dsda_config_mus_opl_gain), + MIGRATED_SETTING(dsda_config_mus_opl_opl3mode), MIGRATED_SETTING(dsda_config_mus_portmidi_reset_type), MIGRATED_SETTING(dsda_config_mus_portmidi_reset_delay), MIGRATED_SETTING(dsda_config_mus_portmidi_filter_sysex), From f7bfc436dcbc1d54111040fa8d1552f2f475a139 Mon Sep 17 00:00:00 2001 From: Roman Fomin Date: Sun, 29 Oct 2023 21:30:18 +0700 Subject: [PATCH 2/5] Enable OPL3 voices, updates to oplplayer.c --- prboom2/src/MUSIC/midifile.h | 4 +- prboom2/src/MUSIC/oplplayer.c | 750 ++++++++++++++++++++++++---------- 2 files changed, 529 insertions(+), 225 deletions(-) diff --git a/prboom2/src/MUSIC/midifile.h b/prboom2/src/MUSIC/midifile.h index 2df934a73..33c0e3e55 100644 --- a/prboom2/src/MUSIC/midifile.h +++ b/prboom2/src/MUSIC/midifile.h @@ -66,7 +66,9 @@ typedef enum MIDI_CONTROLLER_DATA_ENTRY = 0x5, MIDI_CONTROLLER_MAIN_VOLUME = 0x7, - MIDI_CONTROLLER_PAN = 0xa + MIDI_CONTROLLER_PAN = 0xa, + + MIDI_CONTROLLER_ALL_NOTES_OFF = 0x7b } midi_controller_t; typedef enum diff --git a/prboom2/src/MUSIC/oplplayer.c b/prboom2/src/MUSIC/oplplayer.c index 4d63d38e7..228e942a4 100644 --- a/prboom2/src/MUSIC/oplplayer.c +++ b/prboom2/src/MUSIC/oplplayer.c @@ -29,6 +29,7 @@ #include #include "doomdef.h" +#include "doomstat.h" #include "memio.h" #include "mus2mid.h" @@ -44,10 +45,15 @@ #include "lprintf.h" +#include "dsda/configuration.h" + +static int opl_opl3mode; + // #define OPL_MIDI_DEBUG #define MAXMIDLENGTH (96 * 1024) #define GENMIDI_NUM_INSTRS 128 +#define GENMIDI_NUM_PERCUSSION 47 #define GENMIDI_HEADER "#OPL_II#" #define GENMIDI_FLAG_FIXED 0x0001 /* fixed pitch */ @@ -81,8 +87,6 @@ typedef struct genmidi_voice_t voices[2]; } PACKEDATTR genmidi_instr_t; -// Data associated with a channel of a track that is currently playing. - typedef struct { // The instrument currently used for this track. @@ -92,6 +96,11 @@ typedef struct // Volume level int volume; + int volume_base; + + // Pan + + int pan; // Pitch bend value: @@ -110,11 +119,6 @@ typedef struct // Track iterator used to read new events. midi_track_iter_t *iter; - - // Tempo control variables - - unsigned int ticks_per_beat; - unsigned int ms_per_beat; } opl_track_data_t; typedef struct opl_voice_s opl_voice_t; @@ -127,6 +131,9 @@ struct opl_voice_s // The operators used by this voice: int op1, op2; + // Array used by voice: + int array; + // Currently-loaded instrument data const genmidi_instr_t *current_instr; @@ -153,11 +160,14 @@ struct opl_voice_s unsigned int note_volume; // The current volume (register value) that has been set for this channel. - unsigned int reg_volume; + unsigned int car_volume; + unsigned int mod_volume; + + // Pan. + unsigned int reg_pan; - // Next in linked list; a voice is always either in the - // free list or the allocated list. - opl_voice_t *next; + // Priority. + unsigned int priority; }; // Operators used by the different voices. @@ -306,21 +316,40 @@ static const unsigned int volume_mapping_table[] = { 124, 124, 125, 125, 126, 126, 127, 127 }; +// DMX version to emulate for OPL emulation: +typedef enum { + opl_doom1_1_666, // Doom 1 v1.666 + opl_doom2_1_666, // Doom 2 v1.666, Hexen, Heretic + opl_doom_1_9 // Doom v1.9, Strife +} opl_driver_ver_t; + +static opl_driver_ver_t opl_drv_ver = opl_doom_1_9; static dboolean music_initialized = false; //static dboolean musicpaused = false; +static int start_music_volume; static int current_music_volume; // GENMIDI lump instrument data: -static const genmidi_instr_t *main_instrs; -static const genmidi_instr_t *percussion_instrs; +static genmidi_instr_t *main_instrs; +static genmidi_instr_t *percussion_instrs; +static char (*main_instr_names)[32]; +static char (*percussion_names)[32]; // Voices: -static opl_voice_t voices[OPL_NUM_VOICES]; -static opl_voice_t *voice_free_list; -static opl_voice_t *voice_alloced_list; +static opl_voice_t voices[OPL_NUM_VOICES * 2]; +static opl_voice_t *voice_free_list[OPL_NUM_VOICES * 2]; +static opl_voice_t *voice_alloced_list[OPL_NUM_VOICES * 2]; +static int voice_free_num; +static int voice_alloced_num; +static int opl_opl3mode; +static int num_opl_voices; + +// Data for each channel. + +static opl_channel_data_t channels[MIDI_CHANNELS_PER_TRACK]; // Track data for playing tracks: @@ -329,10 +358,16 @@ static unsigned int num_tracks = 0; static unsigned int running_tracks = 0; static dboolean song_looping; -// Configuration file variable, containing the port number for the -// adlib chip. +// Tempo control variables + +static unsigned int ticks_per_beat; +static unsigned int ms_per_beat; -int opl_io_port = 0x388; +// If true, OPL sound channels are reversed to their correct arrangement +// (as intended by the MIDI standard) rather than the backwards one +// used by DMX due to a bug. + +static dboolean opl_stereo_correct = false; // Load instrument table from GENMIDI lump: @@ -351,6 +386,9 @@ static dboolean LoadInstrumentTable(void) main_instrs = (const genmidi_instr_t *) (lump + strlen(GENMIDI_HEADER)); percussion_instrs = main_instrs + GENMIDI_NUM_INSTRS; + main_instr_names = + (char (*)[32]) (percussion_instrs + GENMIDI_NUM_PERCUSSION); + percussion_names = main_instr_names + GENMIDI_NUM_INSTRS; return true; } @@ -360,93 +398,104 @@ static dboolean LoadInstrumentTable(void) static opl_voice_t *GetFreeVoice(void) { opl_voice_t *result; + int i; // None available? - if (voice_free_list == NULL) + if (voice_free_num == 0) { return NULL; } // Remove from free list - result = voice_free_list; - voice_free_list = voice_free_list->next; + result = voice_free_list[0]; + + voice_free_num--; + + for (i = 0; i < voice_free_num; i++) + { + voice_free_list[i] = voice_free_list[i + 1]; + } // Add to allocated list - result->next = voice_alloced_list; - voice_alloced_list = result; + voice_alloced_list[voice_alloced_num++] = result; return result; } -// Remove a voice from the allocated voices list. - -static void RemoveVoiceFromAllocedList(opl_voice_t *voice) -{ - opl_voice_t **rover; +// Release a voice back to the freelist. - rover = &voice_alloced_list; +static void VoiceKeyOff(opl_voice_t *voice); - // Search the list until we find the voice, then remove it. +static void ReleaseVoice(int index) +{ + opl_voice_t *voice; + dboolean double_voice; + int i; - while (*rover != NULL) + // Doom 2 1.666 OPL crash emulation. + if (index >= voice_alloced_num) { - if (*rover == voice) - { - *rover = voice->next; - voice->next = NULL; - break; - } - rover = &(*rover)->next; + voice_alloced_num = 0; + voice_free_num = 0; + return; } -} -// Release a voice back to the freelist. + voice = voice_alloced_list[index]; -static void ReleaseVoice(opl_voice_t *voice) -{ - opl_voice_t **rover; + VoiceKeyOff(voice); voice->channel = NULL; voice->note = 0; + double_voice = voice->current_instr_voice != 0; + // Remove from alloced list. - RemoveVoiceFromAllocedList(voice); + voice_alloced_num--; + + for (i = index; i < voice_alloced_num; i++) + { + voice_alloced_list[i] = voice_alloced_list[i + 1]; + } // Search to the end of the freelist (This is how Doom behaves!) - rover = &voice_free_list; + voice_free_list[voice_free_num++] = voice; - while (*rover != NULL) + if (double_voice && opl_drv_ver < opl_doom_1_9) { - rover = &(*rover)->next; + ReleaseVoice(index); } - - *rover = voice; - voice->next = NULL; } // Load data to the specified Operator static void LoadOperatorData(int Operator, const genmidi_op_t *data, - dboolean max_level) + dboolean max_level, + unsigned int *volume) { int level; // The scale and level fields must be combined for the level register. // For the carrier wave we always set the maximum level. - level = (data->scale & 0xc0) | (data->level & 0x3f); + level = data->scale; if (max_level) { level |= 0x3f; } + else + { + level |= data->level; + } + + *volume = level; OPL_WriteRegister(OPL_REGS_LEVEL + Operator, level); OPL_WriteRegister(OPL_REGS_TREMOLO + Operator, data->tremolo); @@ -486,27 +535,31 @@ static void SetVoiceInstrument(opl_voice_t *voice, // is set in SetVoiceVolume (below). If we are not using // modulating mode, we must set both to minimum volume. - LoadOperatorData(voice->op2, &data->carrier, true); - LoadOperatorData(voice->op1, &data->modulator, !modulating); + LoadOperatorData(voice->op2 | voice->array, &data->carrier, true, + &voice->car_volume); + LoadOperatorData(voice->op1 | voice->array, &data->modulator, !modulating, + &voice->mod_volume); // Set feedback register that control the connection between the // two operators. Turn on bits in the upper nybble; I think this // is for OPL3, where it turns on channel A/B. - OPL_WriteRegister(OPL_REGS_FEEDBACK + voice->index, - data->feedback | 0x30); + OPL_WriteRegister((OPL_REGS_FEEDBACK + voice->index) | voice->array, + data->feedback | voice->reg_pan); - // Hack to force a volume update. + // Calculate voice priority. - voice->reg_volume = 999; + voice->priority = 0x0f - (data->carrier.attack >> 4) + + 0x0f - (data->carrier.sustain & 0x0f); } static void SetVoiceVolume(opl_voice_t *voice, unsigned int volume) { const genmidi_voice_t *opl_voice; + unsigned int midi_volume; unsigned int full_volume; - unsigned int op_volume; - unsigned int reg_volume; + unsigned int car_volume; + unsigned int mod_volume; voice->note_volume = volume; @@ -514,40 +567,59 @@ static void SetVoiceVolume(opl_voice_t *voice, unsigned int volume) // Multiply note volume and channel volume to get the actual volume. - full_volume = (volume_mapping_table[voice->note_volume] - * volume_mapping_table[voice->channel->volume] - * volume_mapping_table[current_music_volume]) / (127 * 127); + midi_volume = 2 * (volume_mapping_table[voice->channel->volume] + 1); - // The volume of each instrument can be controlled via GENMIDI: - - op_volume = 0x3f - opl_voice->carrier.level; + full_volume = (volume_mapping_table[voice->note_volume] * midi_volume) + >> 9; // The volume value to use in the register: - - reg_volume = (op_volume * full_volume) / 128; - reg_volume = (0x3f - reg_volume) | opl_voice->carrier.scale; + car_volume = 0x3f - full_volume; // Update the volume register(s) if necessary. - if (reg_volume != voice->reg_volume) + if (car_volume != (voice->car_volume & 0x3f)) { - voice->reg_volume = reg_volume; + voice->car_volume = car_volume | (voice->car_volume & 0xc0); - OPL_WriteRegister(OPL_REGS_LEVEL + voice->op2, reg_volume); + OPL_WriteRegister((OPL_REGS_LEVEL + voice->op2) | voice->array, + voice->car_volume); // If we are using non-modulated feedback mode, we must set the // volume for both voices. - // Note that the same register volume value is written for - // both voices, always calculated from the carrier's level - // value. - if ((opl_voice->feedback & 0x01) != 0) + if ((opl_voice->feedback & 0x01) != 0 + && opl_voice->modulator.level != 0x3f) { - OPL_WriteRegister(OPL_REGS_LEVEL + voice->op1, reg_volume); + mod_volume = opl_voice->modulator.level; + if (mod_volume < car_volume) + { + mod_volume = car_volume; + } + + mod_volume |= voice->mod_volume & 0xc0; + + if(mod_volume != voice->mod_volume) + { + voice->mod_volume = mod_volume; + OPL_WriteRegister((OPL_REGS_LEVEL + voice->op1) | voice->array, + mod_volume | + (opl_voice->modulator.scale & 0xc0)); + } } } } +static void SetVoicePan(opl_voice_t *voice, unsigned int pan) +{ + genmidi_voice_t *opl_voice; + + voice->reg_pan = pan; + opl_voice = &voice->current_instr->voices[voice->current_instr_voice];; + + OPL_WriteRegister((OPL_REGS_FEEDBACK + voice->index) | voice->array, + opl_voice->feedback | pan); +} + // Initialize the voice table and freelist static void InitVoices(void) @@ -556,26 +628,31 @@ static void InitVoices(void) // Start with an empty free list. - voice_free_list = NULL; + voice_free_num = num_opl_voices; + voice_alloced_num = 0; // Initialize each voice. - for (i=0; iindex, voice->freq >> 8); + OPL_WriteRegister((OPL_REGS_FREQ_2 + voice->index) | voice->array, + voice->freq >> 8); +} + +static opl_channel_data_t *TrackChannelForEvent(opl_track_data_t *track, + midi_event_t *event) +{ + unsigned int channel_num = event->data.channel.channel; + + // MIDI uses track #9 for percussion, but for MUS it's track #15 + // instead. Because DMX works on MUS data internally, we need to + // swap back to the MUS version of the channel number. + if (channel_num == 9) + { + channel_num = 15; + } + else if (channel_num == 15) + { + channel_num = 9; + } + + return &channels[channel_num]; } // Get the frequency that we should be using for a voice. @@ -616,44 +718,35 @@ static void KeyOffEvent(opl_track_data_t *track, midi_event_t *event) event->data.channel.param2); */ - channel = &track->channels[event->data.channel.channel]; + channel = TrackChannelForEvent(track, event); key = event->data.channel.param1; // Turn off voices being used to play this key. // If it is a double voice instrument there will be two. - for (i=0; ichannel == channel + && voice_alloced_list[i]->key == key) { - VoiceKeyOff(&voices[i]); - // Finished with this voice now. - ReleaseVoice(&voices[i]); + ReleaseVoice(i); + + i--; } } } -// Compare the priorities of channels, returning either -1, 0 or 1. - -static int CompareChannelPriorities(opl_channel_data_t *chan1, - opl_channel_data_t *chan2) -{ - // TODO ... - - return 1; -} - // When all voices are in use, we must discard an existing voice to // play a new note. Find and free an existing voice. The channel // passed to the function is the channel for the new note to be // played. -static opl_voice_t *ReplaceExistingVoice(opl_channel_data_t *channel) +static void ReplaceExistingVoice(void) { - opl_voice_t *rover; - opl_voice_t *result; + int i; + int result; // Check the allocated voices, if we find an instrument that is // of a lower priority to the new instrument, discard it. @@ -663,61 +756,74 @@ static opl_voice_t *ReplaceExistingVoice(opl_channel_data_t *channel) // than higher-numbered channels, eg. MIDI channel 1 is never // discarded for MIDI channel 2. - result = NULL; + result = 0; - for (rover = voice_alloced_list; rover != NULL; rover = rover->next) + for (i = 0; i < voice_alloced_num; i++) { - if (rover->current_instr_voice != 0 - || (rover->channel > channel - && CompareChannelPriorities(channel, rover->channel) > 0)) + if (voice_alloced_list[i]->current_instr_voice != 0 + || voice_alloced_list[i]->channel + >= voice_alloced_list[result]->channel) { - result = rover; - break; + result = i; } } - // If we didn't find a voice, find an existing voice being used to - // play a note on the same channel, and use that. + ReleaseVoice(result); +} + +// Alternate versions of ReplaceExistingVoice() used when emulating old +// versions of the DMX library used in Doom 1.666, Heretic and Hexen. - if (result == NULL) +static void ReplaceExistingVoiceDoom1(void) +{ + int i; + int result; + + result = 0; + + for (i = 0; i < voice_alloced_num; i++) { - for (rover = voice_alloced_list; rover != NULL; rover = rover->next) + if (voice_alloced_list[i]->channel + > voice_alloced_list[result]->channel) { - if (rover->channel == channel) - { - result = rover; - break; - } + result = i; } } - // Still nothing found? Give up and just use the first voice in - // the list. + ReleaseVoice(result); +} - if (result == NULL) - { - result = voice_alloced_list; - } +static void ReplaceExistingVoiceDoom2(opl_channel_data_t *channel) +{ + int i; + int result; + int priority; - // Stop playing this voice playing and release it back to the free - // list. + result = 0; - VoiceKeyOff(result); - ReleaseVoice(result); + priority = 0x8000; - // Re-allocate the voice again and return it. + for (i = 0; i < voice_alloced_num - 3; i++) + { + if (voice_alloced_list[i]->priority < priority + && voice_alloced_list[i]->channel >= channel) + { + priority = voice_alloced_list[i]->priority; + result = i; + } + } - return GetFreeVoice(); + ReleaseVoice(result); } static unsigned int FrequencyForVoice(opl_voice_t *voice) { const genmidi_voice_t *gm_voice; - unsigned int freq_index; + signed int freq_index; unsigned int octave; unsigned int sub_index; - unsigned int note; + signed int note; note = voice->note; @@ -726,16 +832,21 @@ static unsigned int FrequencyForVoice(opl_voice_t *voice) gm_voice = &voice->current_instr->voices[voice->current_instr_voice]; - if ((voice->current_instr->flags & GENMIDI_FLAG_FIXED) == 0) + if ((LittleShort(voice->current_instr->flags) & GENMIDI_FLAG_FIXED) == 0) { - note += (signed short) doom_htows(gm_voice->base_note_offset); + note += (signed short) LittleShort(gm_voice->base_note_offset); } // Avoid possible overflow due to base note offset: - if (note > 0x7f) + while (note < 0) { - note = voice->note; + note += 12; + } + + while (note > 95) + { + note -= 12; } freq_index = 64 + 32 * note + voice->channel->bend; @@ -748,6 +859,11 @@ static unsigned int FrequencyForVoice(opl_voice_t *voice) freq_index += (voice->current_instr->fine_tuning / 2) - 64; } + if (freq_index < 0) + { + freq_index = 0; + } + // The first 7 notes use the start of the table, while // consecutive notes loop around the latter part. @@ -769,14 +885,7 @@ static unsigned int FrequencyForVoice(opl_voice_t *voice) if (octave >= 7) { - if (sub_index < 5) - { - octave = 7; - } - else - { - octave = 6; - } + octave = 7; } // Calculate the resulting register value to use for the frequency. @@ -797,8 +906,10 @@ static void UpdateVoiceFrequency(opl_voice_t *voice) if (voice->freq != freq) { - OPL_WriteRegister(OPL_REGS_FREQ_1 + voice->index, freq & 0xff); - OPL_WriteRegister(OPL_REGS_FREQ_2 + voice->index, (freq >> 8) | 0x20); + OPL_WriteRegister((OPL_REGS_FREQ_1 + voice->index) | voice->array, + freq & 0xff); + OPL_WriteRegister((OPL_REGS_FREQ_2 + voice->index) | voice->array, + (freq >> 8) | 0x20); voice->freq = freq; } @@ -809,32 +920,26 @@ static void UpdateVoiceFrequency(opl_voice_t *voice) // key on event. static void VoiceKeyOn(opl_channel_data_t *channel, - const genmidi_instr_t *instrument, + genmidi_instr_t *instrument, unsigned int instrument_voice, + unsigned int note, unsigned int key, unsigned int volume) { opl_voice_t *voice; + if (!opl_opl3mode && opl_drv_ver == opl_doom1_1_666) + { + instrument_voice = 0; + } + // Find a voice to use for this new note. voice = GetFreeVoice(); - // If there are no more voices left, we must decide what to do. - // If this is the first voice of the instrument, free an existing - // voice and use that. Otherwise, if this is the second voice, - // it isn't as important; just discard it. - if (voice == NULL) { - if (instrument_voice == 0) - { - voice = ReplaceExistingVoice(channel); - } - else - { - return; - } + return; } voice->channel = channel; @@ -843,15 +948,17 @@ static void VoiceKeyOn(opl_channel_data_t *channel, // Work out the note to use. This is normally the same as // the key, unless it is a fixed pitch instrument. - if ((instrument->flags & GENMIDI_FLAG_FIXED) != 0) + if ((LittleShort(instrument->flags) & GENMIDI_FLAG_FIXED) != 0) { voice->note = instrument->fixed_note; } else { - voice->note = key; + voice->note = note; } + voice->reg_pan = channel->pan; + // Program the voice with the instrument data: SetVoiceInstrument(voice, instrument, instrument_voice); @@ -868,10 +975,10 @@ static void VoiceKeyOn(opl_channel_data_t *channel, static void KeyOnEvent(opl_track_data_t *track, midi_event_t *event) { - const genmidi_instr_t *instrument; + genmidi_instr_t *instrument; opl_channel_data_t *channel; - unsigned int key; - unsigned int volume; + unsigned int note, key, volume, voicenum; + dboolean double_voice; /* printf("note on: channel %i, %i, %i\n", @@ -880,22 +987,23 @@ static void KeyOnEvent(opl_track_data_t *track, midi_event_t *event) event->data.channel.param2); */ - if (event->data.channel.param2 == 0) - { // NSM - // i have no idea why this is the case, but it is - // note that you don't see this in any of the base doom/doom2 music - KeyOffEvent (track, event); + note = event->data.channel.param1; + key = event->data.channel.param1; + volume = event->data.channel.param2; + + // A volume of zero means key off. Some MIDI tracks, eg. the ones + // in AV.wad, use a second key on with a volume of zero to mean + // key off. + if (volume <= 0) + { + KeyOffEvent(track, event); return; } // The channel. + channel = TrackChannelForEvent(track, event); - channel = &track->channels[event->data.channel.channel]; - key = event->data.channel.param1; - volume = event->data.channel.param2; - - // Percussion channel (10) is treated differently. - + // Percussion channel is treated differently. if (event->data.channel.channel == 9) { if (key < 35 || key > 81) @@ -904,47 +1012,116 @@ static void KeyOnEvent(opl_track_data_t *track, midi_event_t *event) } instrument = &percussion_instrs[key - 35]; + + note = 60; } else { instrument = channel->instrument; } - // Find and program a voice for this instrument. If this - // is a double voice instrument, we must do this twice. + double_voice = (LittleShort(instrument->flags) & GENMIDI_FLAG_2VOICE) != 0; - VoiceKeyOn(channel, instrument, 0, key, volume); - - if ((instrument->flags & GENMIDI_FLAG_2VOICE) != 0) + switch (opl_drv_ver) { - VoiceKeyOn(channel, instrument, 1, key, volume); + case opl_doom1_1_666: + voicenum = double_voice + 1; + if (!opl_opl3mode) + { + voicenum = 1; + } + while (voice_alloced_num > num_opl_voices - voicenum) + { + ReplaceExistingVoiceDoom1(); + } + + // Find and program a voice for this instrument. If this + // is a double voice instrument, we must do this twice. + + if (double_voice) + { + VoiceKeyOn(channel, instrument, 1, note, key, volume); + } + + VoiceKeyOn(channel, instrument, 0, note, key, volume); + break; + case opl_doom2_1_666: + if (voice_alloced_num == num_opl_voices) + { + ReplaceExistingVoiceDoom2(channel); + } + if (voice_alloced_num == num_opl_voices - 1 && double_voice) + { + ReplaceExistingVoiceDoom2(channel); + } + + // Find and program a voice for this instrument. If this + // is a double voice instrument, we must do this twice. + + if (double_voice) + { + VoiceKeyOn(channel, instrument, 1, note, key, volume); + } + + VoiceKeyOn(channel, instrument, 0, note, key, volume); + break; + default: + case opl_doom_1_9: + if (voice_free_num == 0) + { + ReplaceExistingVoice(); + } + + // Find and program a voice for this instrument. If this + // is a double voice instrument, we must do this twice. + + VoiceKeyOn(channel, instrument, 0, note, key, volume); + + if (double_voice) + { + VoiceKeyOn(channel, instrument, 1, note, key, volume); + } + break; } } static void ProgramChangeEvent(opl_track_data_t *track, midi_event_t *event) { - int channel; + opl_channel_data_t *channel; int instrument; // Set the instrument used on this channel. - channel = event->data.channel.channel; + channel = TrackChannelForEvent(track, event); instrument = event->data.channel.param1; - track->channels[channel].instrument = &main_instrs[instrument]; + channel->instrument = &main_instrs[instrument]; // TODO: Look through existing voices that are turned on on this // channel, and change the instrument. } -static void SetChannelVolume(opl_channel_data_t *channel, unsigned int volume) +static void SetChannelVolume(opl_channel_data_t *channel, unsigned int volume, + dboolean clip_start) { unsigned int i; + channel->volume_base = volume; + + if (volume > current_music_volume) + { + volume = current_music_volume; + } + + if (clip_start && volume > start_music_volume) + { + volume = start_music_volume; + } + channel->volume = volume; // Update all voices that this channel is using. - for (i=0; i= 96) + { + reg_pan = 0x10; + } + else if (pan <= 48) + { + reg_pan = 0x20; + } + else + { + reg_pan = 0x30; + } + if (channel->pan != reg_pan) + { + channel->pan = reg_pan; + for (i = 0; i < num_opl_voices; i++) + { + if (voices[i].channel == channel) + { + SetVoicePan(&voices[i], reg_pan); + } + } + } + } +} + +// Handler for the MIDI_CONTROLLER_ALL_NOTES_OFF channel event. +static void AllNotesOff(opl_channel_data_t *channel, unsigned int param) +{ + int i; + + for (i = 0; i < voice_alloced_num; i++) + { + if (voice_alloced_list[i]->channel == channel) + { + // Finished with this voice now. + + ReleaseVoice(i); + + i--; + } + } +} + static void ControllerEvent(opl_track_data_t *track, midi_event_t *event) { + opl_channel_data_t *channel; unsigned int controller; unsigned int param; - opl_channel_data_t *channel; /* printf("change controller: channel %i, %i, %i\n", @@ -966,14 +1204,22 @@ static void ControllerEvent(opl_track_data_t *track, midi_event_t *event) event->data.channel.param2); */ - channel = &track->channels[event->data.channel.channel]; + channel = TrackChannelForEvent(track, event); controller = event->data.channel.param1; param = event->data.channel.param2; switch (controller) { case MIDI_CONTROLLER_MAIN_VOLUME: - SetChannelVolume(channel, param); + SetChannelVolume(channel, param, true); + break; + + case MIDI_CONTROLLER_PAN: + SetChannelPan(channel, param); + break; + + case MIDI_CONTROLLER_ALL_NOTES_OFF: + AllNotesOff(channel, param); break; default: @@ -990,28 +1236,57 @@ static void PitchBendEvent(opl_track_data_t *track, midi_event_t *event) { opl_channel_data_t *channel; unsigned int i; + opl_voice_t *voice_updated_list[OPL_NUM_VOICES * 2]; + unsigned int voice_updated_num = 0; + opl_voice_t *voice_not_updated_list[OPL_NUM_VOICES * 2]; + unsigned int voice_not_updated_num = 0; // Update the channel bend value. Only the MSB of the pitch bend // value is considered: this is what Doom does. - channel = &track->channels[event->data.channel.channel]; + channel = TrackChannelForEvent(track, event); channel->bend = event->data.channel.param2 - 64; // Update all voices for this channel. - for (i=0; ichannel == channel) + { + UpdateVoiceFrequency(voice_alloced_list[i]); + voice_updated_list[voice_updated_num++] = voice_alloced_list[i]; + } + else { - UpdateVoiceFrequency(&voices[i]); + voice_not_updated_list[voice_not_updated_num++] = + voice_alloced_list[i]; } } + + for (i = 0; i < voice_not_updated_num; i++) + { + voice_alloced_list[i] = voice_not_updated_list[i]; + } + + for (i = 0; i < voice_updated_num; i++) + { + voice_alloced_list[i + voice_not_updated_num] = voice_updated_list[i]; + } +} + +static void MetaSetTempo(unsigned int tempo) +{ + // OPL_AdjustCallbacks((float) us_per_beat / tempo); + // us_per_beat = tempo; } // Process a meta event. static void MetaEvent(opl_track_data_t *track, midi_event_t *event) { + byte *data = event->data.meta.data; + unsigned int data_len = event->data.meta.length; + switch (event->data.meta.type) { // Things we can just ignore. @@ -1027,6 +1302,13 @@ static void MetaEvent(opl_track_data_t *track, midi_event_t *event) case MIDI_META_SEQUENCER_SPECIFIC: break; + case MIDI_META_SET_TEMPO: + if (data_len == 3) + { + MetaSetTempo((data[0] << 16) | (data[1] << 8) | data[2]); + } + break; + // End of track - actually handled when we run out of events // in the track, see below. @@ -1087,6 +1369,7 @@ static void ProcessEvent(opl_track_data_t *track, midi_event_t *event) } static void ScheduleTrack(opl_track_data_t *track); +static void InitChannel(opl_channel_data_t *channel); // Restart a song from the beginning. @@ -1113,6 +1396,11 @@ static void RestartSong(void) MIDI_RestartIterator(tracks[i].iter); ScheduleTrack(&tracks[i]); } + + for (i = 0; i < MIDI_CHANNELS_PER_TRACK; ++i) + { + InitChannel(&channels[i]); + } } // Callback function invoked when another event needs to be read from @@ -1162,7 +1450,7 @@ static void ScheduleTrack(opl_track_data_t *track) // Get the number of milliseconds until the next event. nticks = MIDI_GetDeltaTime(track->iter); - ms = (nticks * track->ms_per_beat) / track->ticks_per_beat; + ms = (nticks * ms_per_beat) / ticks_per_beat; // Set a timer to be invoked when the next event is // ready to play. @@ -1172,12 +1460,18 @@ static void ScheduleTrack(opl_track_data_t *track) // Initialize a channel. -static void InitChannel(opl_track_data_t *track, opl_channel_data_t *channel) +static void InitChannel(opl_channel_data_t *channel) { // TODO: Work out sensible defaults? channel->instrument = &main_instrs[0]; - channel->volume = 127; + channel->volume = current_music_volume; + channel->volume_base = 100; + if (channel->volume > channel->volume_base) + { + channel->volume = channel->volume_base; + } + channel->pan = 0x30; channel->bend = 0; } @@ -1186,21 +1480,9 @@ static void InitChannel(opl_track_data_t *track, opl_channel_data_t *channel) static void StartTrack(const midi_file_t *file, unsigned int track_num) { opl_track_data_t *track; - unsigned int i; track = &tracks[track_num]; track->iter = MIDI_IterateTrack(file, track_num); - track->ticks_per_beat = MIDI_GetFileTimeDivision(file); - - // Default is 120 bpm. - // TODO: this is wrong - - track->ms_per_beat = 500; - - for (i=0; ichannels[i]); - } // Schedule the first event. @@ -1233,10 +1515,24 @@ static void I_OPL_PlaySong(const void *handle, int looping) running_tracks = num_tracks; song_looping = looping; - for (i=0; i Date: Sun, 29 Oct 2023 22:23:48 +0700 Subject: [PATCH 3/5] Delete dbopl.h dbopl.c --- prboom2/src/MUSIC/dbopl.c | 1562 ------------------------------------- prboom2/src/MUSIC/dbopl.h | 217 ------ 2 files changed, 1779 deletions(-) delete mode 100644 prboom2/src/MUSIC/dbopl.c delete mode 100644 prboom2/src/MUSIC/dbopl.h diff --git a/prboom2/src/MUSIC/dbopl.c b/prboom2/src/MUSIC/dbopl.c deleted file mode 100644 index 8028f68ee..000000000 --- a/prboom2/src/MUSIC/dbopl.c +++ /dev/null @@ -1,1562 +0,0 @@ -/* - * Copyright (C) 2002-2010 The DOSBox Team - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ - -// -// Chocolate Doom-related discussion: -// -// This is the DosBox OPL emulator code (src/hardware/dbopl.cpp) r3635, -// converted to C. The bulk of the work was done using the minus-minus -// script in the Chocolate Doom SVN repository, then the result tweaked -// by hand until working. -// - - -/* - DOSBox implementation of a combined Yamaha YMF262 and Yamaha YM3812 emulator. - Enabling the opl3 bit will switch the emulator to stereo opl3 output instead of regular mono opl2 - Except for the table generation it's all integer math - Can choose different types of generators, using muls and bigger tables, try different ones for slower platforms - The generation was based on the MAME implementation but tried to have it use less memory and be faster in general - MAME uses much bigger envelope tables and this will be the biggest cause of it sounding different at times - - //TODO Don't delay first operator 1 sample in opl3 mode - //TODO Maybe not use class method pointers but a regular function pointers with operator as first parameter - //TODO Fix panning for the Percussion channels, would any opl3 player use it and actually really change it though? - //TODO Check if having the same accuracy in all frequency multipliers sounds better or not - - //DUNNO Keyon in 4op, switch to 2op without keyoff. -*/ - -/* $Id: dbopl.cpp,v 1.10 2009-06-10 19:54:51 harekiet Exp $ */ - -#include -#include -#include -//#include "dosbox.h" -#include "dbopl.h" - -#ifdef _MSC_VER -#define inline __inline -#endif - -#define GCC_UNLIKELY(x) x - -#define TRUE 1 -#define FALSE 0 - -#ifndef PI -#define PI 3.14159265358979323846 -#endif - -#define OPLRATE ((double)(14318180.0 / 288.0)) -#define TREMOLO_TABLE 52 - -//Try to use most precision for frequencies -//Else try to keep different waves in synch -//#define WAVE_PRECISION 1 -#ifndef WAVE_PRECISION -//Wave bits available in the top of the 32bit range -//Original adlib uses 10.10, we use 10.22 -#define WAVE_BITS 10 -#else -//Need some extra bits at the top to have room for octaves and frequency multiplier -//We support to 8 times lower rate -//128 * 15 * 8 = 15350, 2^13.9, so need 14 bits -#define WAVE_BITS 14 -#endif -#define WAVE_SH ( 32 - WAVE_BITS ) -#define WAVE_MASK ( ( 1 << WAVE_SH ) - 1 ) - -//Use the same accuracy as the waves -#define LFO_SH ( WAVE_SH - 10 ) -//LFO is controlled by our tremolo 256 sample limit -#define LFO_MAX ( 256 << ( LFO_SH ) ) - - -//Maximum amount of attenuation bits -//Envelope goes to 511, 9 bits -#if (DBOPL_WAVE == WAVE_TABLEMUL ) -//Uses the value directly -#define ENV_BITS ( 9 ) -#else -//Add 3 bits here for more accuracy and would have to be shifted up either way -#define ENV_BITS ( 9 ) -#endif -//Limits of the envelope with those bits and when the envelope goes silent -#define ENV_MIN 0 -#define ENV_EXTRA ( ENV_BITS - 9 ) -#define ENV_MAX ( 511 << ENV_EXTRA ) -#define ENV_LIMIT ( ( 12 * 256) >> ( 3 - ENV_EXTRA ) ) -#define ENV_SILENT( _X_ ) ( (_X_) >= ENV_LIMIT ) - -//Attack/decay/release rate counter shift -#define RATE_SH 24 -#define RATE_MASK ( ( 1 << RATE_SH ) - 1 ) -//Has to fit within 16bit lookuptable -#define MUL_SH 16 - -//Check some ranges -#if ENV_EXTRA > 3 -#error Too many envelope bits -#endif - -static inline void Operator__SetState(Operator *self, Bit8u s ); -static inline Bit32u Chip__ForwardNoise(Chip *self); - -// C++'s template<> sure is useful sometimes. - -static Channel* Channel__BlockTemplate(Channel *self, Chip* chip, - Bit32u samples, Bit32s* output, - SynthMode mode ); -#define BLOCK_TEMPLATE(mode) \ - static Channel* Channel__BlockTemplate_ ## mode(Channel *self, Chip* chip, \ - Bit32u samples, Bit32s* output) \ - { \ - return Channel__BlockTemplate(self, chip, samples, output, mode); \ - } - -BLOCK_TEMPLATE(sm2AM) -BLOCK_TEMPLATE(sm2FM) -BLOCK_TEMPLATE(sm3AM) -BLOCK_TEMPLATE(sm3FM) -BLOCK_TEMPLATE(sm3FMFM) -BLOCK_TEMPLATE(sm3AMFM) -BLOCK_TEMPLATE(sm3FMAM) -BLOCK_TEMPLATE(sm3AMAM) -BLOCK_TEMPLATE(sm2Percussion) -BLOCK_TEMPLATE(sm3Percussion) - -//How much to substract from the base value for the final attenuation -static const Bit8u KslCreateTable[16] = { - //0 will always be be lower than 7 * 8 - 64, 32, 24, 19, - 16, 12, 11, 10, - 8, 6, 5, 4, - 3, 2, 1, 0, -}; - -#define M(_X_) ((Bit8u)( (_X_) * 2)) -static const Bit8u FreqCreateTable[16] = { - M(0.5), M(1 ), M(2 ), M(3 ), M(4 ), M(5 ), M(6 ), M(7 ), - M(8 ), M(9 ), M(10), M(10), M(12), M(12), M(15), M(15) -}; -#undef M - -//We're not including the highest attack rate, that gets a special value -static const Bit8u AttackSamplesTable[13] = { - 69, 55, 46, 40, - 35, 29, 23, 20, - 19, 15, 11, 10, - 9 -}; -//On a real opl these values take 8 samples to reach and are based upon larger tables -static const Bit8u EnvelopeIncreaseTable[13] = { - 4, 5, 6, 7, - 8, 10, 12, 14, - 16, 20, 24, 28, - 32, -}; - -#if ( DBOPL_WAVE == WAVE_HANDLER ) || ( DBOPL_WAVE == WAVE_TABLELOG ) -static Bit16u ExpTable[ 256 ]; -#endif - -#if ( DBOPL_WAVE == WAVE_HANDLER ) -//PI table used by WAVEHANDLER -static Bit16u SinTable[ 512 ]; -#endif - -#if ( DBOPL_WAVE > WAVE_HANDLER ) -//Layout of the waveform table in 512 entry intervals -//With overlapping waves we reduce the table to half it's size - -// | |//\\|____|WAV7|//__|/\ |____|/\/\| -// |\\//| | |WAV7| | \/| | | -// |06 |0126|17 |7 |3 |4 |4 5 |5 | - -//6 is just 0 shifted and masked - -static Bit16s WaveTable[ 8 * 512 ]; -//Distance into WaveTable the wave starts -static const Bit16u WaveBaseTable[8] = { - 0x000, 0x200, 0x200, 0x800, - 0xa00, 0xc00, 0x100, 0x400, - -}; -//Mask the counter with this -static const Bit16u WaveMaskTable[8] = { - 1023, 1023, 511, 511, - 1023, 1023, 512, 1023, -}; - -//Where to start the counter on at keyon -static const Bit16u WaveStartTable[8] = { - 512, 0, 0, 0, - 0, 512, 512, 256, -}; -#endif - -#if ( DBOPL_WAVE == WAVE_TABLEMUL ) -static Bit16u MulTable[ 384 ]; -#endif - -static Bit8u KslTable[ 8 * 16 ]; -static Bit8u TremoloTable[ TREMOLO_TABLE ]; -//Start of a channel behind the chip struct start -static Bit16u ChanOffsetTable[32]; -//Start of an operator behind the chip struct start -static Bit16u OpOffsetTable[64]; - -//The lower bits are the shift of the operator vibrato value -//The highest bit is right shifted to generate -1 or 0 for negation -//So taking the highest input value of 7 this gives 3, 7, 3, 0, -3, -7, -3, 0 -static const Bit8s VibratoTable[ 8 ] = { - 1 - 0x00, 0 - 0x00, 1 - 0x00, 30 - 0x00, - 1 - 0x80, 0 - 0x80, 1 - 0x80, 30 - 0x80 -}; - -//Shift strength for the ksl value determined by ksl strength -static const Bit8u KslShiftTable[4] = { - 31,1,2,0 -}; - -//Generate a table index and table shift value using input value from a selected rate -static void EnvelopeSelect( Bit8u val, Bit8u *index, Bit8u *shift ) { - if ( val < 13 * 4 ) { //Rate 0 - 12 - *shift = 12 - ( val >> 2 ); - *index = val & 3; - } else if ( val < 15 * 4 ) { //rate 13 - 14 - *shift = 0; - *index = val - 12 * 4; - } else { //rate 15 and up - *shift = 0; - *index = 12; - } -} - -#if ( DBOPL_WAVE == WAVE_HANDLER ) -/* - Generate the different waveforms out of the sine/exponetial table using handlers -*/ -static inline Bits MakeVolume( Bitu wave, Bitu volume ) { - Bitu total = wave + volume; - Bitu index = total & 0xff; - Bitu sig = ExpTable[ index ]; - Bitu exp = total >> 8; - - return (sig >> exp); -}; - -static Bits DB_FASTCALL WaveForm0( Bitu i, Bitu volume ) { - Bits neg = 0 - (( i >> 9) & 1);//Create ~0 or 0 - Bitu wave = SinTable[i & 511]; - return (MakeVolume( wave, volume ) ^ neg) - neg; -} -static Bits DB_FASTCALL WaveForm1( Bitu i, Bitu volume ) { - Bit32u wave = SinTable[i & 511]; - wave |= ( ( (i ^ 512 ) & 512) - 1) >> ( 32 - 12 ); - return MakeVolume( wave, volume ); -} -static Bits DB_FASTCALL WaveForm2( Bitu i, Bitu volume ) { - Bitu wave = SinTable[i & 511]; - return MakeVolume( wave, volume ); -} -static Bits DB_FASTCALL WaveForm3( Bitu i, Bitu volume ) { - Bitu wave = SinTable[i & 255]; - wave |= ( ( (i ^ 256 ) & 256) - 1) >> ( 32 - 12 ); - return MakeVolume( wave, volume ); -} -static Bits DB_FASTCALL WaveForm4( Bitu i, Bitu volume ) { - //Twice as fast - i <<= 1; - Bits neg = 0 - (( i >> 9) & 1);//Create ~0 or 0 - Bitu wave = SinTable[i & 511]; - wave |= ( ( (i ^ 512 ) & 512) - 1) >> ( 32 - 12 ); - return (MakeVolume( wave, volume ) ^ neg) - neg; -} -static Bits DB_FASTCALL WaveForm5( Bitu i, Bitu volume ) { - //Twice as fast - i <<= 1; - Bitu wave = SinTable[i & 511]; - wave |= ( ( (i ^ 512 ) & 512) - 1) >> ( 32 - 12 ); - return MakeVolume( wave, volume ); -} -static Bits DB_FASTCALL WaveForm6( Bitu i, Bitu volume ) { - Bits neg = 0 - (( i >> 9) & 1);//Create ~0 or 0 - return (MakeVolume( 0, volume ) ^ neg) - neg; -} -static Bits DB_FASTCALL WaveForm7( Bitu i, Bitu volume ) { - //Negative is reversed here - Bits neg = (( i >> 9) & 1) - 1; - Bitu wave = (i << 3); - //When negative the volume also runs backwards - wave = ((wave ^ neg) - neg) & 4095; - return (MakeVolume( wave, volume ) ^ neg) - neg; -} - -static const WaveHandler WaveHandlerTable[8] = { - WaveForm0, WaveForm1, WaveForm2, WaveForm3, - WaveForm4, WaveForm5, WaveForm6, WaveForm7 -}; - -#endif - -/* - Operator -*/ - -//We zero out when rate == 0 -static inline void Operator__UpdateAttack(Operator *self, const Chip* chip ) { - Bit8u rate = self->reg60 >> 4; - if ( rate ) { - Bit8u val = (rate << 2) + self->ksr; - self->attackAdd = chip->attackRates[ val ]; - self->rateZero &= ~(1 << ATTACK); - } else { - self->attackAdd = 0; - self->rateZero |= (1 << ATTACK); - } -} -static inline void Operator__UpdateDecay(Operator *self, const Chip* chip ) { - Bit8u rate = self->reg60 & 0xf; - if ( rate ) { - Bit8u val = (rate << 2) + self->ksr; - self->decayAdd = chip->linearRates[ val ]; - self->rateZero &= ~(1 << DECAY); - } else { - self->decayAdd = 0; - self->rateZero |= (1 << DECAY); - } -} -static inline void Operator__UpdateRelease(Operator *self, const Chip* chip ) { - Bit8u rate = self->reg80 & 0xf; - if ( rate ) { - Bit8u val = (rate << 2) + self->ksr; - self->releaseAdd = chip->linearRates[ val ]; - self->rateZero &= ~(1 << RELEASE); - if ( !(self->reg20 & MASK_SUSTAIN ) ) { - self->rateZero &= ~( 1 << SUSTAIN ); - } - } else { - self->rateZero |= (1 << RELEASE); - self->releaseAdd = 0; - if ( !(self->reg20 & MASK_SUSTAIN ) ) { - self->rateZero |= ( 1 << SUSTAIN ); - } - } -} - -static inline void Operator__UpdateAttenuation(Operator *self) { - Bit8u kslBase = (Bit8u)((self->chanData >> SHIFT_KSLBASE) & 0xff); - Bit32u tl = self->reg40 & 0x3f; - Bit8u kslShift = KslShiftTable[ self->reg40 >> 6 ]; - //Make sure the attenuation goes to the right bits - self->totalLevel = tl << ( ENV_BITS - 7 ); //Total level goes 2 bits below max - self->totalLevel += ( kslBase << ENV_EXTRA ) >> kslShift; -} - -static void Operator__UpdateFrequency(Operator *self) { - Bit32u freq = self->chanData & (( 1 << 10 ) - 1); - Bit32u block = (self->chanData >> 10) & 0xff; -#ifdef WAVE_PRECISION - block = 7 - block; - self->waveAdd = ( freq * self->freqMul ) >> block; -#else - self->waveAdd = ( freq << block ) * self->freqMul; -#endif - if ( self->reg20 & MASK_VIBRATO ) { - self->vibStrength = (Bit8u)(freq >> 7); - -#ifdef WAVE_PRECISION - self->vibrato = ( self->vibStrength * self->freqMul ) >> block; -#else - self->vibrato = ( self->vibStrength << block ) * self->freqMul; -#endif - } else { - self->vibStrength = 0; - self->vibrato = 0; - } -} - -static void Operator__UpdateRates(Operator *self, const Chip* chip ) { - //Mame seems to reverse this where enabling ksr actually lowers - //the rate, but pdf manuals says otherwise? - Bit8u newKsr = (Bit8u)((self->chanData >> SHIFT_KEYCODE) & 0xff); - if ( !( self->reg20 & MASK_KSR ) ) { - newKsr >>= 2; - } - if ( self->ksr == newKsr ) - return; - self->ksr = newKsr; - Operator__UpdateAttack( self, chip ); - Operator__UpdateDecay( self, chip ); - Operator__UpdateRelease( self, chip ); -} - -static inline Bit32s Operator__RateForward(Operator *self, Bit32u add ) { - Bit32s ret; - self->rateIndex += add; - /*Bit32s*/ ret = self->rateIndex >> RATE_SH; - self->rateIndex = self->rateIndex & RATE_MASK; - return ret; -} - -static Bits Operator__TemplateVolume(Operator *self, OperatorState yes) { - Bit32s vol = self->volume; - Bit32s change; - switch ( yes ) { - case OFF: - return ENV_MAX; - case ATTACK: - change = Operator__RateForward( self, self->attackAdd ); - if ( !change ) - return vol; - vol += ( (~vol) * change ) >> 3; - if ( vol < ENV_MIN ) { - self->volume = ENV_MIN; - self->rateIndex = 0; - Operator__SetState( self, DECAY ); - return ENV_MIN; - } - break; - case DECAY: - vol += Operator__RateForward( self, self->decayAdd ); - if ( GCC_UNLIKELY(vol >= self->sustainLevel) ) { - //Check if we didn't overshoot max attenuation, then just go off - if ( GCC_UNLIKELY(vol >= ENV_MAX) ) { - self->volume = ENV_MAX; - Operator__SetState( self, OFF ); - return ENV_MAX; - } - //Continue as sustain - self->rateIndex = 0; - Operator__SetState( self, SUSTAIN ); - } - break; - case SUSTAIN: - if ( self->reg20 & MASK_SUSTAIN ) { - return vol; - } - // fallthrough - //In sustain phase, but not sustaining, do regular release - case RELEASE: - vol += Operator__RateForward( self, self->releaseAdd );; - if ( GCC_UNLIKELY(vol >= ENV_MAX) ) { - self->volume = ENV_MAX; - Operator__SetState( self, OFF ); - return ENV_MAX; - } - break; - } - self->volume = vol; - return vol; -} - -#define TEMPLATE_VOLUME(mode) \ - static Bits Operator__TemplateVolume ## mode(Operator *self) \ - { \ - return Operator__TemplateVolume(self, mode); \ - } - -TEMPLATE_VOLUME(OFF) -TEMPLATE_VOLUME(RELEASE) -TEMPLATE_VOLUME(SUSTAIN) -TEMPLATE_VOLUME(ATTACK) -TEMPLATE_VOLUME(DECAY) - -static const VolumeHandler VolumeHandlerTable[5] = { - &Operator__TemplateVolumeOFF, - &Operator__TemplateVolumeRELEASE, - &Operator__TemplateVolumeSUSTAIN, - &Operator__TemplateVolumeDECAY, - &Operator__TemplateVolumeATTACK, -}; - -static inline Bitu Operator__ForwardVolume(Operator *self) { - return self->currentLevel + (self->volHandler)(self); -} - - -static inline Bitu Operator__ForwardWave(Operator *self) { - self->waveIndex += self->waveCurrent; - return self->waveIndex >> WAVE_SH; -} - -static void Operator__Write20(Operator *self, const Chip* chip, Bit8u val ) { - Bit8u change = (self->reg20 ^ val ); - if ( !change ) - return; - self->reg20 = val; - //Shift the tremolo bit over the entire register, saved a branch, YES! - self->tremoloMask = (Bit8s)(val) >> 7; - self->tremoloMask &= ~(( 1 << ENV_EXTRA ) -1); - //Update specific features based on changes - if ( change & MASK_KSR ) { - Operator__UpdateRates( self, chip ); - } - //With sustain enable the volume doesn't change - if ( self->reg20 & MASK_SUSTAIN || ( !self->releaseAdd ) ) { - self->rateZero |= ( 1 << SUSTAIN ); - } else { - self->rateZero &= ~( 1 << SUSTAIN ); - } - //Frequency multiplier or vibrato changed - if ( change & (0xf | MASK_VIBRATO) ) { - self->freqMul = chip->freqMul[ val & 0xf ]; - Operator__UpdateFrequency(self); - } -} - -static void Operator__Write40(Operator *self, const Chip *chip, Bit8u val ) { - if (!(self->reg40 ^ val )) - return; - self->reg40 = val; - Operator__UpdateAttenuation( self ); -} - -static void Operator__Write60(Operator *self, const Chip* chip, Bit8u val ) { - Bit8u change = self->reg60 ^ val; - self->reg60 = val; - if ( change & 0x0f ) { - Operator__UpdateDecay( self, chip ); - } - if ( change & 0xf0 ) { - Operator__UpdateAttack( self, chip ); - } -} - -static void Operator__Write80(Operator *self, const Chip* chip, Bit8u val ) { - Bit8u change = (self->reg80 ^ val ); - Bit8u sustain; - if ( !change ) - return; - self->reg80 = val; - sustain = val >> 4; - //Turn 0xf into 0x1f - sustain |= ( sustain + 1) & 0x10; - self->sustainLevel = sustain << ( ENV_BITS - 5 ); - if ( change & 0x0f ) { - Operator__UpdateRelease( self, chip ); - } -} - -static void Operator__WriteE0(Operator *self, const Chip* chip, Bit8u val ) { - Bit8u waveForm; - if ( !(self->regE0 ^ val) ) - return; - //in opl3 mode you can always selet 7 waveforms regardless of waveformselect - waveForm = val & ( ( 0x3 & chip->waveFormMask ) | (0x7 & chip->opl3Active ) ); - self->regE0 = val; -#if( DBOPL_WAVE == WAVE_HANDLER ) - self->waveHandler = WaveHandlerTable[ waveForm ]; -#else - self->waveBase = WaveTable + WaveBaseTable[ waveForm ]; - self->waveStart = WaveStartTable[ waveForm ] << WAVE_SH; - self->waveMask = WaveMaskTable[ waveForm ]; -#endif -} - -static inline void Operator__SetState(Operator *self, Bit8u s ) { - self->state = s; - self->volHandler = VolumeHandlerTable[ s ]; -} - -static inline int Operator__Silent(Operator *self) { - if ( !ENV_SILENT( self->totalLevel + self->volume ) ) - return FALSE; - if ( !(self->rateZero & ( 1 << self->state ) ) ) - return FALSE; - return TRUE; -} - -static inline void Operator__Prepare(Operator *self, const Chip* chip ) { - self->currentLevel = self->totalLevel + (chip->tremoloValue & self->tremoloMask); - self->waveCurrent = self->waveAdd; - if ( self->vibStrength >> chip->vibratoShift ) { - Bit32s add = self->vibrato >> chip->vibratoShift; - //Sign extend over the shift value - Bit32s neg = chip->vibratoSign; - //Negate the add with -1 or 0 - add = ( add ^ neg ) - neg; - self->waveCurrent += add; - } -} - -static void Operator__KeyOn(Operator *self, Bit8u mask ) { - if ( !self->keyOn ) { - //Restart the frequency generator -#if( DBOPL_WAVE > WAVE_HANDLER ) - self->waveIndex = self->waveStart; -#else - self->waveIndex = 0; -#endif - self->rateIndex = 0; - Operator__SetState( self, ATTACK ); - } - self->keyOn |= mask; -} - -static void Operator__KeyOff(Operator *self, Bit8u mask ) { - self->keyOn &= ~mask; - if ( !self->keyOn ) { - if ( self->state != OFF ) { - Operator__SetState( self, RELEASE ); - } - } -} - -static inline Bits Operator__GetWave(Operator *self, Bitu index, Bitu vol ) { -#if( DBOPL_WAVE == WAVE_HANDLER ) - return self->waveHandler( index, vol << ( 3 - ENV_EXTRA ) ); -#elif( DBOPL_WAVE == WAVE_TABLEMUL ) - return(self->waveBase[ index & self->waveMask ] * MulTable[ vol >> ENV_EXTRA ]) >> MUL_SH; -#elif( DBOPL_WAVE == WAVE_TABLELOG ) - Bit32s wave = self->waveBase[ index & self->waveMask ]; - Bit32u total = ( wave & 0x7fff ) + vol << ( 3 - ENV_EXTRA ); - Bit32s sig = ExpTable[ total & 0xff ]; - Bit32u exp = total >> 8; - Bit32s neg = wave >> 16; - return((sig ^ neg) - neg) >> exp; -#else -#error "No valid wave routine" -#endif -} - -static inline Bits Operator__GetSample(Operator *self, Bits modulation ) { - Bitu vol = Operator__ForwardVolume(self); - if ( ENV_SILENT( vol ) ) { - //Simply forward the wave - self->waveIndex += self->waveCurrent; - return 0; - } else { - Bitu index = Operator__ForwardWave(self); - index += modulation; - return Operator__GetWave( self, index, vol ); - } -} - -static void Operator__Operator(Operator *self) { - self->chanData = 0; - self->freqMul = 0; - self->waveIndex = 0; - self->waveAdd = 0; - self->waveCurrent = 0; - self->keyOn = 0; - self->ksr = 0; - self->reg20 = 0; - self->reg40 = 0; - self->reg60 = 0; - self->reg80 = 0; - self->regE0 = 0; - Operator__SetState( self, OFF ); - self->rateZero = (1 << OFF); - self->sustainLevel = ENV_MAX; - self->currentLevel = ENV_MAX; - self->totalLevel = ENV_MAX; - self->volume = ENV_MAX; - self->releaseAdd = 0; -} - -/* - Channel -*/ - -static void Channel__Channel(Channel *self) { - Operator__Operator(&self->op[0]); - Operator__Operator(&self->op[1]); - self->old[0] = self->old[1] = 0; - self->chanData = 0; - self->regB0 = 0; - self->regC0 = 0; - self->maskLeft = -1; - self->maskRight = -1; - self->feedback = 31; - self->fourMask = 0; - self->synthHandler = Channel__BlockTemplate_sm2FM; -}; - -static inline Operator* Channel__Op( Channel *self, Bitu index ) { - return &( ( self + (index >> 1) )->op[ index & 1 ]); -} - -static void Channel__SetChanData(Channel *self, const Chip* chip, Bit32u data ) { - Bit32u change = self->chanData ^ data; - self->chanData = data; - Channel__Op( self, 0 )->chanData = data; - Channel__Op( self, 1 )->chanData = data; - //Since a frequency update triggered this, always update frequency - Operator__UpdateFrequency(Channel__Op( self, 0 )); - Operator__UpdateFrequency(Channel__Op( self, 1 )); - if ( change & ( 0xff << SHIFT_KSLBASE ) ) { - Operator__UpdateAttenuation(Channel__Op( self, 0 )); - Operator__UpdateAttenuation(Channel__Op( self, 1 )); - } - if ( change & ( 0xff << SHIFT_KEYCODE ) ) { - Operator__UpdateRates(Channel__Op( self, 0 ), chip); - Operator__UpdateRates(Channel__Op( self, 1 ), chip); - } -} - -static void Channel__UpdateFrequency(Channel *self, const Chip* chip, Bit8u fourOp ) { - //Extrace the frequency bits - Bit32u data = self->chanData & 0xffff; - Bit32u kslBase = KslTable[ data >> 6 ]; - Bit32u keyCode = ( data & 0x1c00) >> 9; - if ( chip->reg08 & 0x40 ) { - keyCode |= ( data & 0x100)>>8; /* notesel == 1 */ - } else { - keyCode |= ( data & 0x200)>>9; /* notesel == 0 */ - } - //Add the keycode and ksl into the highest bits of chanData - data |= (keyCode << SHIFT_KEYCODE) | ( kslBase << SHIFT_KSLBASE ); - Channel__SetChanData( self + 0, chip, data ); - if ( fourOp & 0x3f ) { - Channel__SetChanData( self + 1, chip, data ); - } -} - -static void Channel__WriteA0(Channel *self, const Chip* chip, Bit8u val ) { - Bit32u change; - Bit8u fourOp = chip->reg104 & chip->opl3Active & self->fourMask; - //Don't handle writes to silent fourop channels - if ( fourOp > 0x80 ) - return; - change = (self->chanData ^ val ) & 0xff; - if ( change ) { - self->chanData ^= change; - Channel__UpdateFrequency( self, chip, fourOp ); - } -} - -static void Channel__WriteB0(Channel *self, const Chip* chip, Bit8u val ) { - Bitu change; - Bit8u fourOp = chip->reg104 & chip->opl3Active & self->fourMask; - //Don't handle writes to silent fourop channels - if ( fourOp > 0x80 ) - return; - change = (self->chanData ^ ( val << 8 ) ) & 0x1f00; - if ( change ) { - self->chanData ^= change; - Channel__UpdateFrequency( self, chip, fourOp ); - } - //Check for a change in the keyon/off state - if ( !(( val ^ self->regB0) & 0x20)) - return; - self->regB0 = val; - if ( val & 0x20 ) { - Operator__KeyOn( Channel__Op(self, 0), 0x1 ); - Operator__KeyOn( Channel__Op(self, 1), 0x1 ); - if ( fourOp & 0x3f ) { - Operator__KeyOn( Channel__Op(self + 1, 0), 1 ); - Operator__KeyOn( Channel__Op(self + 1, 1), 1 ); - } - } else { - Operator__KeyOff( Channel__Op(self, 0), 0x1 ); - Operator__KeyOff( Channel__Op(self, 1), 0x1 ); - if ( fourOp & 0x3f ) { - Operator__KeyOff( Channel__Op(self + 1, 0), 1 ); - Operator__KeyOff( Channel__Op(self + 1, 1), 1 ); - } - } -} - -static void Channel__WriteC0(Channel *self, const Chip* chip, Bit8u val ) { - Bit8u synth; - Bit8u change = val ^ self->regC0; - if ( !change ) - return; - self->regC0 = val; - self->feedback = ( val >> 1 ) & 7; - if ( self->feedback ) { - //We shift the input to the right 10 bit wave index value - self->feedback = 9 - self->feedback; - } else { - self->feedback = 31; - } - //Select the new synth mode - if ( chip->opl3Active ) { - //4-op mode enabled for this channel - if ( (chip->reg104 & self->fourMask) & 0x3f ) { - Channel* chan0, *chan1; - //Check if it's the 2nd channel in a 4-op - if ( !(self->fourMask & 0x80 ) ) { - chan0 = self; - chan1 = self + 1; - } else { - chan0 = self - 1; - chan1 = self; - } - - synth = ( (chan0->regC0 & 1) << 0 )| (( chan1->regC0 & 1) << 1 ); - switch ( synth ) { - case 0: - chan0->synthHandler = Channel__BlockTemplate_sm3FMFM; - break; - case 1: - chan0->synthHandler = Channel__BlockTemplate_sm3AMFM; - break; - case 2: - chan0->synthHandler = Channel__BlockTemplate_sm3FMAM ; - break; - case 3: - chan0->synthHandler = Channel__BlockTemplate_sm3AMAM ; - break; - } - //Disable updating percussion channels - } else if ((self->fourMask & 0x40) && ( chip->regBD & 0x20) ) { - - //Regular dual op, am or fm - } else if ( val & 1 ) { - self->synthHandler = Channel__BlockTemplate_sm3AM; - } else { - self->synthHandler = Channel__BlockTemplate_sm3FM; - } - self->maskLeft = ( val & 0x10 ) ? -1 : 0; - self->maskRight = ( val & 0x20 ) ? -1 : 0; - //opl2 active - } else { - //Disable updating percussion channels - if ( (self->fourMask & 0x40) && ( chip->regBD & 0x20 ) ) { - - //Regular dual op, am or fm - } else if ( val & 1 ) { - self->synthHandler = Channel__BlockTemplate_sm2AM; - } else { - self->synthHandler = Channel__BlockTemplate_sm2FM; - } - } -} - -static void Channel__ResetC0(Channel *self, const Chip* chip ) { - Bit8u val = self->regC0; - self->regC0 ^= 0xff; - Channel__WriteC0( self, chip, val ); -}; - -static inline void Channel__GeneratePercussion(Channel *self, Chip* chip, - Bit32s* output, int opl3Mode ) { - Bit32u noiseBit, c2, c5, phaseBit, hhVol, sdVol, tcVol; - Bit32s sample; - Channel* chan = self; - - //BassDrum - Bit32s mod = (Bit32u)((self->old[0] + self->old[1])) >> self->feedback; - self->old[0] = self->old[1]; - self->old[1] = Operator__GetSample( Channel__Op(self, 0), mod ); - - //When bassdrum is in AM mode first operator is ignoed - if ( chan->regC0 & 1 ) { - mod = 0; - } else { - mod = self->old[0]; - } - sample = Operator__GetSample( Channel__Op(self, 1), mod ); - - //Precalculate stuff used by other outputs - noiseBit = Chip__ForwardNoise(chip) & 0x1; - c2 = Operator__ForwardWave(Channel__Op(self, 2)); - c5 = Operator__ForwardWave(Channel__Op(self, 5)); - phaseBit = (((c2 & 0x88) ^ ((c2<<5) & 0x80)) | ((c5 ^ (c5<<2)) & 0x20)) ? 0x02 : 0x00; - - //Hi-Hat - hhVol = Operator__ForwardVolume(Channel__Op(self, 2)); - if ( !ENV_SILENT( hhVol ) ) { - Bit32u hhIndex = (phaseBit<<8) | (0x34 << ( phaseBit ^ (noiseBit << 1 ))); - sample += Operator__GetWave( Channel__Op(self, 2), hhIndex, hhVol ); - } - //Snare Drum - sdVol = Operator__ForwardVolume( Channel__Op(self, 3) ); - if ( !ENV_SILENT( sdVol ) ) { - Bit32u sdIndex = ( 0x100 + (c2 & 0x100) ) ^ ( noiseBit << 8 ); - sample += Operator__GetWave( Channel__Op(self, 3), sdIndex, sdVol ); - } - //Tom-tom - sample += Operator__GetSample( Channel__Op(self, 4), 0 ); - - //Top-Cymbal - tcVol = Operator__ForwardVolume(Channel__Op(self, 5)); - if ( !ENV_SILENT( tcVol ) ) { - Bit32u tcIndex = (1 + phaseBit) << 8; - sample += Operator__GetWave( Channel__Op(self, 5), tcIndex, tcVol ); - } - sample <<= 1; - if ( opl3Mode ) { - output[0] += sample; - output[1] += sample; - } else { - output[0] += sample; - } -} - -Channel* Channel__BlockTemplate(Channel *self, Chip* chip, - Bit32u samples, Bit32s* output, - SynthMode mode ) { - Bitu i; - - switch( mode ) { - case sm2AM: - case sm3AM: - if ( Operator__Silent(Channel__Op(self, 0)) - && Operator__Silent(Channel__Op(self, 1))) { - self->old[0] = self->old[1] = 0; - return(self + 1); - } - break; - case sm2FM: - case sm3FM: - if ( Operator__Silent(Channel__Op(self, 1))) { - self->old[0] = self->old[1] = 0; - return (self + 1); - } - break; - case sm3FMFM: - if ( Operator__Silent(Channel__Op(self, 3))) { - self->old[0] = self->old[1] = 0; - return (self + 2); - } - break; - case sm3AMFM: - if ( Operator__Silent( Channel__Op(self, 0) ) - && Operator__Silent( Channel__Op(self, 3) )) { - self->old[0] = self->old[1] = 0; - return (self + 2); - } - break; - case sm3FMAM: - if ( Operator__Silent( Channel__Op(self, 1)) - && Operator__Silent( Channel__Op(self, 3))) { - self->old[0] = self->old[1] = 0; - return (self + 2); - } - break; - case sm3AMAM: - if ( Operator__Silent( Channel__Op(self, 0) ) - && Operator__Silent( Channel__Op(self, 2) ) - && Operator__Silent( Channel__Op(self, 3) )) { - self->old[0] = self->old[1] = 0; - return (self + 2); - } - break; - - default: - abort(); - } - //Init the operators with the the current vibrato and tremolo values - Operator__Prepare( Channel__Op( self, 0 ), chip ); - Operator__Prepare( Channel__Op( self, 1 ), chip ); - if ( mode > sm4Start ) { - Operator__Prepare( Channel__Op( self, 2 ), chip ); - Operator__Prepare( Channel__Op( self, 3 ), chip ); - } - if ( mode > sm6Start ) { - Operator__Prepare( Channel__Op( self, 4 ), chip ); - Operator__Prepare( Channel__Op( self, 5 ), chip ); - } - for ( i = 0; i < samples; i++ ) { - Bit32s mod, sample, out0; - Bits next; - //Early out for percussion handlers - if ( mode == sm2Percussion ) { - Channel__GeneratePercussion( self, chip, output + i, FALSE ); - continue; //Prevent some unitialized value bitching - } else if ( mode == sm3Percussion ) { - Channel__GeneratePercussion( self, chip, output + i * 2, TRUE ); - continue; //Prevent some unitialized value bitching - } - - //Do unsigned shift so we can shift out all bits but still stay in 10 bit range otherwise - mod = (Bit32u)((self->old[0] + self->old[1])) >> self->feedback; - self->old[0] = self->old[1]; - self->old[1] = Operator__GetSample( Channel__Op(self, 0), mod ); - sample = 0; - out0 = self->old[0]; - if ( mode == sm2AM || mode == sm3AM ) { - sample = out0 + Operator__GetSample( Channel__Op(self, 1), 0 ); - } else if ( mode == sm2FM || mode == sm3FM ) { - sample = Operator__GetSample( Channel__Op(self, 1), out0 ); - } else if ( mode == sm3FMFM ) { - next = Operator__GetSample( Channel__Op(self, 1), out0 ); - next = Operator__GetSample( Channel__Op(self, 2), next ); - sample = Operator__GetSample( Channel__Op(self, 3), next ); - } else if ( mode == sm3AMFM ) { - sample = out0; - next = Operator__GetSample( Channel__Op(self, 1), 0 ); - next = Operator__GetSample( Channel__Op(self, 2), next ); - sample += Operator__GetSample( Channel__Op(self, 3), next ); - } else if ( mode == sm3FMAM ) { - sample = Operator__GetSample( Channel__Op(self, 1), out0 ); - next = Operator__GetSample( Channel__Op(self, 2), 0 ); - sample += Operator__GetSample( Channel__Op(self, 3), next ); - } else if ( mode == sm3AMAM ) { - sample = out0; - next = Operator__GetSample( Channel__Op(self, 1), 0 ); - sample += Operator__GetSample( Channel__Op(self, 2), next ); - sample += Operator__GetSample( Channel__Op(self, 3), 0 ); - } - switch( mode ) { - case sm2AM: - case sm2FM: - output[ i ] += sample; - break; - case sm3AM: - case sm3FM: - case sm3FMFM: - case sm3AMFM: - case sm3FMAM: - case sm3AMAM: - output[ i * 2 + 0 ] += sample & self->maskLeft; - output[ i * 2 + 1 ] += sample & self->maskRight; - break; - default: - abort(); - } - } - switch( mode ) { - case sm2AM: - case sm2FM: - case sm3AM: - case sm3FM: - return ( self + 1 ); - case sm3FMFM: - case sm3AMFM: - case sm3FMAM: - case sm3AMAM: - return ( self + 2 ); - case sm2Percussion: - case sm3Percussion: - return( self + 3 ); - default: - abort(); - } - return 0; -} - -/* - Chip -*/ - -void Chip__Chip(Chip *self) { - int i; - - for (i=0; i<18; ++i) { - Channel__Channel(&self->chan[i]); - } - - self->reg08 = 0; - self->reg04 = 0; - self->regBD = 0; - self->reg104 = 0; - self->opl3Active = 0; -} - -static inline Bit32u Chip__ForwardNoise(Chip *self) { - Bitu count; - self->noiseCounter += self->noiseAdd; - count = self->noiseCounter >> LFO_SH; - self->noiseCounter &= WAVE_MASK; - for ( ; count > 0; --count ) { - //Noise calculation from mame - self->noiseValue ^= ( 0x800302 ) & ( 0 - (self->noiseValue & 1 ) ); - self->noiseValue >>= 1; - } - return self->noiseValue; -} - -static inline Bit32u Chip__ForwardLFO(Chip *self, Bit32u samples ) { - Bit32u todo, count; - //Current vibrato value, runs 4x slower than tremolo - self->vibratoSign = ( VibratoTable[ self->vibratoIndex >> 2] ) >> 7; - self->vibratoShift = ( VibratoTable[ self->vibratoIndex >> 2] & 7) + self->vibratoStrength; - self->tremoloValue = TremoloTable[ self->tremoloIndex ] >> self->tremoloStrength; - - //Check hom many samples there can be done before the value changes - todo = LFO_MAX - self->lfoCounter; - count = (todo + self->lfoAdd - 1) / self->lfoAdd; - if ( count > samples ) { - count = samples; - self->lfoCounter += count * self->lfoAdd; - } else { - self->lfoCounter += count * self->lfoAdd; - self->lfoCounter &= (LFO_MAX - 1); - //Maximum of 7 vibrato value * 4 - self->vibratoIndex = ( self->vibratoIndex + 1 ) & 31; - //Clip tremolo to the the table size - if ( self->tremoloIndex + 1 < TREMOLO_TABLE ) - ++self->tremoloIndex; - else - self->tremoloIndex = 0; - } - return count; -} - - -static void Chip__WriteBD(Chip *self, Bit8u val ) { - Bit8u change = self->regBD ^ val; - if ( !change ) - return; - self->regBD = val; - //TODO could do this with shift and xor? - self->vibratoStrength = (val & 0x40) ? 0x00 : 0x01; - self->tremoloStrength = (val & 0x80) ? 0x00 : 0x02; - if ( val & 0x20 ) { - //Drum was just enabled, make sure channel 6 has the right synth - if ( change & 0x20 ) { - if ( self->opl3Active ) { - self->chan[6].synthHandler - = Channel__BlockTemplate_sm3Percussion; - } else { - self->chan[6].synthHandler - = Channel__BlockTemplate_sm2Percussion; - } - } - //Bass Drum - if ( val & 0x10 ) { - Operator__KeyOn( &self->chan[6].op[0], 0x2 ); - Operator__KeyOn( &self->chan[6].op[1], 0x2 ); - } else { - Operator__KeyOff( &self->chan[6].op[0], 0x2 ); - Operator__KeyOff( &self->chan[6].op[1], 0x2 ); - } - //Hi-Hat - if ( val & 0x1 ) { - Operator__KeyOn( &self->chan[7].op[0], 0x2 ); - } else { - Operator__KeyOff( &self->chan[7].op[0], 0x2 ); - } - //Snare - if ( val & 0x8 ) { - Operator__KeyOn( &self->chan[7].op[1], 0x2 ); - } else { - Operator__KeyOff( &self->chan[7].op[1], 0x2 ); - } - //Tom-Tom - if ( val & 0x4 ) { - Operator__KeyOn( &self->chan[8].op[0], 0x2 ); - } else { - Operator__KeyOff( &self->chan[8].op[0], 0x2 ); - } - //Top Cymbal - if ( val & 0x2 ) { - Operator__KeyOn( &self->chan[8].op[1], 0x2 ); - } else { - Operator__KeyOff( &self->chan[8].op[1], 0x2 ); - } - //Toggle keyoffs when we turn off the percussion - } else if ( change & 0x20 ) { - //Trigger a reset to setup the original synth handler - Channel__ResetC0( &self->chan[6], self ); - Operator__KeyOff( &self->chan[6].op[0], 0x2 ); - Operator__KeyOff( &self->chan[6].op[1], 0x2 ); - Operator__KeyOff( &self->chan[7].op[0], 0x2 ); - Operator__KeyOff( &self->chan[7].op[1], 0x2 ); - Operator__KeyOff( &self->chan[8].op[0], 0x2 ); - Operator__KeyOff( &self->chan[8].op[1], 0x2 ); - } -} - - -#define REGOP( _FUNC_ ) \ - index = ( ( reg >> 3) & 0x20 ) | ( reg & 0x1f ); \ - if ( OpOffsetTable[ index ] ) { \ - Operator* regOp = (Operator*)( ((char *)self ) + OpOffsetTable[ index ] ); \ - Operator__ ## _FUNC_ (regOp, self, val); \ - } - -#define REGCHAN( _FUNC_ ) \ - index = ( ( reg >> 4) & 0x10 ) | ( reg & 0xf ); \ - if ( ChanOffsetTable[ index ] ) { \ - Channel* regChan = (Channel*)( ((char *)self ) + ChanOffsetTable[ index ] ); \ - Channel__ ## _FUNC_ (regChan, self, val); \ - } - -void Chip__WriteReg(Chip *self, Bit32u reg, Bit8u val ) { - Bitu index; - switch ( (reg & 0xf0) >> 4 ) { - case 0x00 >> 4: - if ( reg == 0x01 ) { - self->waveFormMask = ( val & 0x20 ) ? 0x7 : 0x0; - } else if ( reg == 0x104 ) { - //Only detect changes in lowest 6 bits - if ( !((self->reg104 ^ val) & 0x3f) ) - return; - //Always keep the highest bit enabled, for checking > 0x80 - self->reg104 = 0x80 | ( val & 0x3f ); - } else if ( reg == 0x105 ) { - int i; - - //MAME says the real opl3 doesn't reset anything on opl3 disable/enable till the next write in another register - if ( !((self->opl3Active ^ val) & 1 ) ) - return; - self->opl3Active = ( val & 1 ) ? 0xff : 0; - //Update the 0xc0 register for all channels to signal the switch to mono/stereo handlers - for ( i = 0; i < 18;i++ ) { - Channel__ResetC0( &self->chan[i], self ); - } - } else if ( reg == 0x08 ) { - self->reg08 = val; - } - case 0x10 >> 4: - break; - case 0x20 >> 4: - case 0x30 >> 4: - REGOP( Write20 ); - break; - case 0x40 >> 4: - case 0x50 >> 4: - REGOP( Write40 ); - break; - case 0x60 >> 4: - case 0x70 >> 4: - REGOP( Write60 ); - break; - case 0x80 >> 4: - case 0x90 >> 4: - REGOP( Write80 ); - break; - case 0xa0 >> 4: - REGCHAN( WriteA0 ); - break; - case 0xb0 >> 4: - if ( reg == 0xbd ) { - Chip__WriteBD( self, val ); - } else { - REGCHAN( WriteB0 ); - } - break; - case 0xc0 >> 4: - REGCHAN( WriteC0 ); - case 0xd0 >> 4: - break; - case 0xe0 >> 4: - case 0xf0 >> 4: - REGOP( WriteE0 ); - break; - } -} - -Bit32u Chip__WriteAddr(Chip *self, Bit32u port, Bit8u val ) { - switch ( port & 3 ) { - case 0: - return val; - case 2: - if ( self->opl3Active || (val == 0x05) ) - return 0x100 | val; - else - return val; - } - return 0; -} - -void Chip__GenerateBlock2(Chip *self, Bitu total, Bit32s* output ) { - while ( total > 0 ) { - Channel *ch; - int count; - - Bit32u samples = Chip__ForwardLFO( self, total ); - memset(output, 0, sizeof(Bit32s) * samples); - count = 0; - for ( ch = self->chan; ch < self->chan + 9; ) { - count++; - ch = (ch->synthHandler)( ch, self, samples, output ); - } - total -= samples; - output += samples; - } -} - -void Chip__GenerateBlock3(Chip *self, Bitu total, Bit32s* output ) { - while ( total > 0 ) { - int count; - Channel *ch; - - Bit32u samples = Chip__ForwardLFO( self, total ); - memset(output, 0, sizeof(Bit32s) * samples *2); - count = 0; - for ( ch = self->chan; ch < self->chan + 18; ) { - count++; - ch = (ch->synthHandler)( ch, self, samples, output ); - } - total -= samples; - output += samples * 2; - } -} - -void Chip__Setup(Chip *self, Bit32u rate ) { - double original = OPLRATE; - Bit32u i; -// double original = rate; - double scale = original / (double)rate; - - //Noise counter is run at the same precision as general waves - self->noiseAdd = (Bit32u)( 0.5 + scale * ( 1 << LFO_SH ) ); - self->noiseCounter = 0; - self->noiseValue = 1; //Make sure it triggers the noise xor the first time - //The low frequency oscillation counter - //Every time his overflows vibrato and tremoloindex are increased - self->lfoAdd = (Bit32u)( 0.5 + scale * ( 1 << LFO_SH ) ); - self->lfoCounter = 0; - self->vibratoIndex = 0; - self->tremoloIndex = 0; - - //With higher octave this gets shifted up - //-1 since the freqCreateTable = *2 - { -#ifdef WAVE_PRECISION - double freqScale = ( 1 << 7 ) * scale * ( 1 << ( WAVE_SH - 1 - 10)); - for ( i = 0; i < 16; i++ ) { - self->freqMul[i] = (Bit32u)( 0.5 + freqScale * FreqCreateTable[ i ] ); - } -#else - Bit32u freqScale = (Bit32u)( 0.5 + scale * ( 1 << ( WAVE_SH - 1 - 10))); - for ( i = 0; i < 16; i++ ) { - self->freqMul[i] = freqScale * FreqCreateTable[ i ]; - } -#endif - } - - //-3 since the real envelope takes 8 steps to reach the single value we supply - for ( i = 0; i < 76; i++ ) { - Bit8u index, shift; - EnvelopeSelect( (Bit8u) i, &index, &shift ); - self->linearRates[i] = (Bit32u)( scale * (EnvelopeIncreaseTable[ index ] << ( RATE_SH + ENV_EXTRA - shift - 3 ))); - } - //Generate the best matching attack rate - for ( i = 0; i < 62; i++ ) { - Bit8u index, shift; - Bit32s original, guessAdd, bestAdd, bestDiff; - Bit32u passes; - EnvelopeSelect( (Bit8u) i, &index, &shift ); - //Original amount of samples the attack would take - original = (Bit32u)( (AttackSamplesTable[ index ] << shift) / scale); - - guessAdd = (Bit32u)( scale * (EnvelopeIncreaseTable[ index ] << ( RATE_SH - shift - 3 ))); - bestAdd = guessAdd; - bestDiff = 1 << 30; - - for ( passes = 0; passes < 16; passes ++ ) { - Bit32s diff, lDiff; - Bit32s volume = ENV_MAX; - Bit32s samples = 0; - Bit32u count = 0; - while ( volume > 0 && samples < original * 2 ) { - Bit32s change; - count += guessAdd; - change = count >> RATE_SH; - count &= RATE_MASK; - if ( GCC_UNLIKELY(change) ) { // less than 1 % - volume += ( ~volume * change ) >> 3; - } - samples++; - - } - diff = original - samples; - lDiff = labs( diff ); - //Init last on first pass - if ( lDiff < bestDiff ) { - bestDiff = lDiff; - bestAdd = guessAdd; - if ( !bestDiff ) - break; - } - //Below our target - if ( diff < 0 ) { - //Better than the last time - Bit32s mul = ((original - diff) << 12) / original; - guessAdd = ((guessAdd * mul) >> 12); - guessAdd++; - } else if ( diff > 0 ) { - Bit32s mul = ((original - diff) << 12) / original; - guessAdd = (guessAdd * mul) >> 12; - guessAdd--; - } - } - self->attackRates[i] = bestAdd; - } - for ( i = 62; i < 76; i++ ) { - //This should provide instant volume maximizing - self->attackRates[i] = 8 << RATE_SH; - } - //Setup the channels with the correct four op flags - //Channels are accessed through a table so they appear linear here - self->chan[ 0].fourMask = 0x00 | ( 1 << 0 ); - self->chan[ 1].fourMask = 0x80 | ( 1 << 0 ); - self->chan[ 2].fourMask = 0x00 | ( 1 << 1 ); - self->chan[ 3].fourMask = 0x80 | ( 1 << 1 ); - self->chan[ 4].fourMask = 0x00 | ( 1 << 2 ); - self->chan[ 5].fourMask = 0x80 | ( 1 << 2 ); - - self->chan[ 9].fourMask = 0x00 | ( 1 << 3 ); - self->chan[10].fourMask = 0x80 | ( 1 << 3 ); - self->chan[11].fourMask = 0x00 | ( 1 << 4 ); - self->chan[12].fourMask = 0x80 | ( 1 << 4 ); - self->chan[13].fourMask = 0x00 | ( 1 << 5 ); - self->chan[14].fourMask = 0x80 | ( 1 << 5 ); - - //mark the percussion channels - self->chan[ 6].fourMask = 0x40; - self->chan[ 7].fourMask = 0x40; - self->chan[ 8].fourMask = 0x40; - - //Clear Everything in opl3 mode - Chip__WriteReg( self, 0x105, 0x1 ); - for ( i = 0; i < 512; i++ ) { - if ( i == 0x105 ) - continue; - Chip__WriteReg( self, i, 0xff ); - Chip__WriteReg( self, i, 0x0 ); - } - Chip__WriteReg( self, 0x105, 0x0 ); - //Clear everything in opl2 mode - for ( i = 0; i < 255; i++ ) { - Chip__WriteReg( self, i, 0xff ); - Chip__WriteReg( self, i, 0x0 ); - } -} - -static int doneTables = FALSE; -void DBOPL_InitTables( void ) { - int i, oct; - Chip *chip = NULL; - - if ( doneTables ) - return; - doneTables = TRUE; -#if ( DBOPL_WAVE == WAVE_HANDLER ) || ( DBOPL_WAVE == WAVE_TABLELOG ) - //Exponential volume table, same as the real adlib - for ( i = 0; i < 256; i++ ) { - //Save them in reverse - ExpTable[i] = (int)( 0.5 + ( pow(2.0, ( 255 - i) * ( 1.0 /256 ) )-1) * 1024 ); - ExpTable[i] += 1024; //or remove the -1 oh well :) - //Preshift to the left once so the final volume can shift to the right - ExpTable[i] *= 2; - } -#endif -#if ( DBOPL_WAVE == WAVE_HANDLER ) - //Add 0.5 for the trunc rounding of the integer cast - //Do a PI sinetable instead of the original 0.5 PI - for ( i = 0; i < 512; i++ ) { - SinTable[i] = (Bit16s)( 0.5 - log10( sin( (i + 0.5) * (PI / 512.0) ) ) / log10(2.0)*256 ); - } -#endif -#if ( DBOPL_WAVE == WAVE_TABLEMUL ) - //Multiplication based tables - for ( i = 0; i < 384; i++ ) { - int s = i * 8; - //TODO maybe keep some of the precision errors of the original table? - double val = ( 0.5 + ( pow(2.0, -1.0 + ( 255 - s) * ( 1.0 /256 ) )) * ( 1 << MUL_SH )); - MulTable[i] = (Bit16u)(val); - } - - //Sine Wave Base - for ( i = 0; i < 512; i++ ) { - WaveTable[ 0x0200 + i ] = (Bit16s)(sin( (i + 0.5) * (PI / 512.0) ) * 4084); - WaveTable[ 0x0000 + i ] = -WaveTable[ 0x200 + i ]; - } - //Exponential wave - for ( i = 0; i < 256; i++ ) { - WaveTable[ 0x700 + i ] = (Bit16s)( 0.5 + ( pow(2.0, -1.0 + ( 255 - i * 8) * ( 1.0 /256 ) ) ) * 4085 ); - WaveTable[ 0x6ff - i ] = -WaveTable[ 0x700 + i ]; - } -#endif -#if ( DBOPL_WAVE == WAVE_TABLELOG ) - //Sine Wave Base - for ( i = 0; i < 512; i++ ) { - WaveTable[ 0x0200 + i ] = (Bit16s)( 0.5 - log10( sin( (i + 0.5) * (PI / 512.0) ) ) / log10(2.0)*256 ); - WaveTable[ 0x0000 + i ] = ((Bit16s)0x8000) | WaveTable[ 0x200 + i]; - } - //Exponential wave - for ( i = 0; i < 256; i++ ) { - WaveTable[ 0x700 + i ] = i * 8; - WaveTable[ 0x6ff - i ] = ((Bit16s)0x8000) | i * 8; - } -#endif - - // | |//\\|____|WAV7|//__|/\ |____|/\/\| - // |\\//| | |WAV7| | \/| | | - // |06 |0126|27 |7 |3 |4 |4 5 |5 | - -#if (( DBOPL_WAVE == WAVE_TABLELOG ) || ( DBOPL_WAVE == WAVE_TABLEMUL )) - for ( i = 0; i < 256; i++ ) { - //Fill silence gaps - WaveTable[ 0x400 + i ] = WaveTable[0]; - WaveTable[ 0x500 + i ] = WaveTable[0]; - WaveTable[ 0x900 + i ] = WaveTable[0]; - WaveTable[ 0xc00 + i ] = WaveTable[0]; - WaveTable[ 0xd00 + i ] = WaveTable[0]; - //Replicate sines in other pieces - WaveTable[ 0x800 + i ] = WaveTable[ 0x200 + i ]; - //double speed sines - WaveTable[ 0xa00 + i ] = WaveTable[ 0x200 + i * 2 ]; - WaveTable[ 0xb00 + i ] = WaveTable[ 0x000 + i * 2 ]; - WaveTable[ 0xe00 + i ] = WaveTable[ 0x200 + i * 2 ]; - WaveTable[ 0xf00 + i ] = WaveTable[ 0x200 + i * 2 ]; - } -#endif - - //Create the ksl table - for ( oct = 0; oct < 8; oct++ ) { - int base = oct * 8; - for ( i = 0; i < 16; i++ ) { - int val = base - KslCreateTable[i]; - if ( val < 0 ) - val = 0; - //*4 for the final range to match attenuation range - KslTable[ oct * 16 + i ] = val * 4; - } - } - //Create the Tremolo table, just increase and decrease a triangle wave - for ( i = 0; i < TREMOLO_TABLE / 2; i++ ) { - Bit8u val = i << ENV_EXTRA; - TremoloTable[i] = val; - TremoloTable[TREMOLO_TABLE - 1 - i] = val; - } - //Create a table with offsets of the channels from the start of the chip - for ( i = 0; i < 32; i++ ) { - Bitu blah; - Bitu index = i & 0xf; - if ( index >= 9 ) { - ChanOffsetTable[i] = 0; - continue; - } - //Make sure the four op channels follow eachother - if ( index < 6 ) { - index = (index % 3) * 2 + ( index / 3 ); - } - //Add back the bits for highest ones - if ( i >= 16 ) - index += 9; - blah = (Bitu) ( &(chip->chan[ index ]) ); - ChanOffsetTable[i] = (Bit16u) blah; - } - //Same for operators - for ( i = 0; i < 64; i++ ) { - Bitu chNum, opNum, blah; - Channel* chan = NULL; - if ( i % 8 >= 6 || ( (i / 8) % 4 == 3 ) ) { - OpOffsetTable[i] = 0; - continue; - } - chNum = (i / 8) * 3 + (i % 8) % 3; - //Make sure we use 16 and up for the 2nd range to match the chanoffset gap - if ( chNum >= 12 ) - chNum += 16 - 12; - opNum = ( i % 8 ) / 3; - blah = (Bitu) ( &(chan->op[opNum]) ); - OpOffsetTable[i] = (Bit16u) (ChanOffsetTable[ chNum ] + blah); - } -} diff --git a/prboom2/src/MUSIC/dbopl.h b/prboom2/src/MUSIC/dbopl.h deleted file mode 100644 index 9815e75dc..000000000 --- a/prboom2/src/MUSIC/dbopl.h +++ /dev/null @@ -1,217 +0,0 @@ -/* - * Copyright (C) 2002-2010 The DOSBox Team - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ - -//#include "inttypes.h" //NSM -#include "SDL_stdinc.h" - -#ifdef _MSC_VER - #if _MSC_VER < 1300 - #include - typedef INT_PTR intptr_t; - #endif -#endif - -//Use 8 handlers based on a small logatirmic wavetabe and an exponential table for volume -#define WAVE_HANDLER 10 -//Use a logarithmic wavetable with an exponential table for volume -#define WAVE_TABLELOG 11 -//Use a linear wavetable with a multiply table for volume -#define WAVE_TABLEMUL 12 - -//Select the type of wave generator routine -#define DBOPL_WAVE WAVE_TABLEMUL - -typedef struct _Chip Chip; -typedef struct _Operator Operator; -typedef struct _Channel Channel; - -typedef uintptr_t Bitu; -typedef intptr_t Bits; -typedef uint32_t Bit32u; -typedef int32_t Bit32s; -typedef uint16_t Bit16u; -typedef int16_t Bit16s; -typedef uint8_t Bit8u; -typedef int8_t Bit8s; - -#if (DBOPL_WAVE == WAVE_HANDLER) -typedef Bits ( DB_FASTCALL *WaveHandler) ( Bitu i, Bitu volume ); -#endif - -#define DB_FASTCALL - -typedef Bits (*VolumeHandler)(Operator *self); -typedef Channel* (*SynthHandler)(Channel *self, Chip* chip, Bit32u samples, Bit32s* output ); - -//Different synth modes that can generate blocks of data -typedef enum { - sm2AM, - sm2FM, - sm3AM, - sm3FM, - sm4Start, - sm3FMFM, - sm3AMFM, - sm3FMAM, - sm3AMAM, - sm6Start, - sm2Percussion, - sm3Percussion, -} SynthMode; - -//Shifts for the values contained in chandata variable -enum { - SHIFT_KSLBASE = 16, - SHIFT_KEYCODE = 24, -}; - -//Masks for operator 20 values -enum { - MASK_KSR = 0x10, - MASK_SUSTAIN = 0x20, - MASK_VIBRATO = 0x40, - MASK_TREMOLO = 0x80, -}; - -typedef enum { - OFF, - RELEASE, - SUSTAIN, - DECAY, - ATTACK, -} OperatorState; - -struct _Operator { - VolumeHandler volHandler; - -#if (DBOPL_WAVE == WAVE_HANDLER) - WaveHandler waveHandler; //Routine that generate a wave -#else - Bit16s* waveBase; - Bit32u waveMask; - Bit32u waveStart; -#endif - Bit32u waveIndex; //WAVE_BITS shifted counter of the frequency index - Bit32u waveAdd; //The base frequency without vibrato - Bit32u waveCurrent; //waveAdd + vibratao - - Bit32u chanData; //Frequency/octave and derived data coming from whatever channel controls this - Bit32u freqMul; //Scale channel frequency with this, TODO maybe remove? - Bit32u vibrato; //Scaled up vibrato strength - Bit32s sustainLevel; //When stopping at sustain level stop here - Bit32s totalLevel; //totalLevel is added to every generated volume - Bit32u currentLevel; //totalLevel + tremolo - Bit32s volume; //The currently active volume - - Bit32u attackAdd; //Timers for the different states of the envelope - Bit32u decayAdd; - Bit32u releaseAdd; - Bit32u rateIndex; //Current position of the evenlope - - Bit8u rateZero; //Bits for the different states of the envelope having no changes - Bit8u keyOn; //Bitmask of different values that can generate keyon - //Registers, also used to check for changes - Bit8u reg20, reg40, reg60, reg80, regE0; - //Active part of the envelope we're in - Bit8u state; - //0xff when tremolo is enabled - Bit8u tremoloMask; - //Strength of the vibrato - Bit8u vibStrength; - //Keep track of the calculated KSR so we can check for changes - Bit8u ksr; -}; - -struct _Channel { - Operator op[2]; - SynthHandler synthHandler; - Bit32u chanData; //Frequency/octave and derived values - Bit32s old[2]; //Old data for feedback - - Bit8u feedback; //Feedback shift - Bit8u regB0; //Register values to check for changes - Bit8u regC0; - //This should correspond with reg104, bit 6 indicates a Percussion channel, bit 7 indicates a silent channel - Bit8u fourMask; - Bit8s maskLeft; //Sign extended values for both channel's panning - Bit8s maskRight; - -}; - -struct _Chip { - //This is used as the base counter for vibrato and tremolo - Bit32u lfoCounter; - Bit32u lfoAdd; - - - Bit32u noiseCounter; - Bit32u noiseAdd; - Bit32u noiseValue; - - //Frequency scales for the different multiplications - Bit32u freqMul[16]; - //Rates for decay and release for rate of this chip - Bit32u linearRates[76]; - //Best match attack rates for the rate of this chip - Bit32u attackRates[76]; - - //18 channels with 2 operators each - Channel chan[18]; - - Bit8u reg104; - Bit8u reg08; - Bit8u reg04; - Bit8u regBD; - Bit8u vibratoIndex; - Bit8u tremoloIndex; - Bit8s vibratoSign; - Bit8u vibratoShift; - Bit8u tremoloValue; - Bit8u vibratoStrength; - Bit8u tremoloStrength; - //Mask for allowed wave forms - Bit8u waveFormMask; - //0 or -1 when enabled - Bit8s opl3Active; - -}; - -/* -struct Handler : public Adlib::Handler { - DBOPL::Chip chip; - virtual Bit32u WriteAddr( Bit32u port, Bit8u val ); - virtual void WriteReg( Bit32u addr, Bit8u val ); - virtual void Generate( MixerChannel* chan, Bitu samples ); - virtual void Init( Bitu rate ); -}; -*/ - -#ifdef __cplusplus -extern "C" void Chip__Setup(Chip *self, Bit32u rate ); -extern "C" void DBOPL_InitTables( void ); -extern "C" void Chip__Chip(Chip *self); -extern "C" void Chip__WriteReg(Chip *self, Bit32u reg, Bit8u val ); -extern "C" void Chip__GenerateBlock2(Chip *self, Bitu total, Bit32s* output ); -#else -void Chip__Setup(Chip *self, Bit32u rate ); -void DBOPL_InitTables( void ); -void Chip__Chip(Chip *self); -void Chip__WriteReg(Chip *self, Bit32u reg, Bit8u val ); -void Chip__GenerateBlock2(Chip *self, Bitu total, Bit32s* output ); - -#endif From 1ded4d0114e72d0b66389c9ca2363b77783cb659 Mon Sep 17 00:00:00 2001 From: Roman Fomin Date: Sun, 29 Oct 2023 23:06:05 +0700 Subject: [PATCH 4/5] Update to use us instead of ms, adjust tempo --- prboom2/src/MUSIC/opl.c | 34 +++++++++++++++++++++------------- prboom2/src/MUSIC/opl.h | 4 +++- prboom2/src/MUSIC/opl_queue.c | 21 +++++++++++++++++---- prboom2/src/MUSIC/opl_queue.h | 6 ++++-- prboom2/src/MUSIC/oplplayer.c | 14 +++++++------- 5 files changed, 52 insertions(+), 27 deletions(-) diff --git a/prboom2/src/MUSIC/opl.c b/prboom2/src/MUSIC/opl.c index 9d10e5ef1..adcc4948e 100644 --- a/prboom2/src/MUSIC/opl.c +++ b/prboom2/src/MUSIC/opl.c @@ -45,21 +45,21 @@ typedef struct unsigned int rate; // Number of times the timer is advanced per sec. unsigned int enabled; // Non-zero if timer is enabled. unsigned int value; // Last value that was set. - unsigned int expire_time; // Calculated time that timer will expire. + uint64_t expire_time; // Calculated time that timer will expire. } opl_timer_t; // Queue of callbacks waiting to be invoked. static opl_callback_queue_t *callback_queue; -// Current time, in number of samples since startup: -static unsigned int current_time; +// Current time, in us since startup: +static uint64_t current_time; // If non-zero, playback is currently paused. static int opl_paused; -// Time offset (in samples) due to the fact that callbacks +// Time offset (in us) due to the fact that callbacks // were previously paused. -static unsigned int pause_offset; +static uint64_t pause_offset; // OPL software emulator structure. static opl3_chip opl_chip; @@ -108,12 +108,12 @@ void OPL_Shutdown(void) } } -void OPL_SetCallback(unsigned int ms, +void OPL_SetCallback(uint64_t us, opl_callback_t callback, void *data) { OPL_Queue_Push(callback_queue, callback, data, - current_time - pause_offset + (ms * opl_sample_rate) / 1000); + current_time - pause_offset + us); } void OPL_ClearCallbacks(void) @@ -131,7 +131,7 @@ static void OPLTimer_CalculateEndTime(opl_timer_t *timer) { tics = 0x100 - timer->value; timer->expire_time = current_time - + (tics * opl_sample_rate) / timer->rate; + + ((uint64_t) tics * OPL_SECOND) / timer->rate; } } @@ -185,13 +185,15 @@ static void OPL_AdvanceTime(unsigned int nsamples) { opl_callback_t callback; void *callback_data; + uint64_t us; // Advance time. - current_time += nsamples; + us = ((uint64_t) nsamples * OPL_SECOND) / opl_sample_rate; + current_time += us; if (opl_paused) { - pause_offset += nsamples; + pause_offset += us; } // Are there callbacks to invoke now? Keep invoking them @@ -209,6 +211,11 @@ static void OPL_AdvanceTime(unsigned int nsamples) } } +void OPL_AdjustCallbacks(float factor) +{ + OPL_Queue_AdjustCallbacks(callback_queue, current_time, factor); +} + void OPL_Render_Samples (void *dest, unsigned buffer_len) { unsigned int filled = 0; @@ -218,8 +225,8 @@ void OPL_Render_Samples (void *dest, unsigned buffer_len) // full. while (filled < buffer_len) { - unsigned int next_callback_time; - unsigned int nsamples; + uint64_t next_callback_time; + uint64_t nsamples; // Work out the time until the next callback waiting in // the callback queue must be invoked. We can then fill the @@ -232,7 +239,8 @@ void OPL_Render_Samples (void *dest, unsigned buffer_len) { next_callback_time = OPL_Queue_Peek(callback_queue) + pause_offset; - nsamples = next_callback_time - current_time; + nsamples = (next_callback_time - current_time) * opl_sample_rate; + nsamples = (nsamples + OPL_SECOND - 1) / OPL_SECOND; if (nsamples > buffer_len - filled) { diff --git a/prboom2/src/MUSIC/opl.h b/prboom2/src/MUSIC/opl.h index 39f70192f..507524298 100644 --- a/prboom2/src/MUSIC/opl.h +++ b/prboom2/src/MUSIC/opl.h @@ -121,8 +121,10 @@ extern unsigned int opl_sample_rate; void OPL_Render_Samples (void *dest, unsigned nsamp); -void OPL_SetCallback(unsigned int ms, opl_callback_t callback, void *data); +void OPL_SetCallback(uint64_t us, opl_callback_t callback, void *data); void OPL_ClearCallbacks(void); +void OPL_AdjustCallbacks(float tempo); + #endif diff --git a/prboom2/src/MUSIC/opl_queue.c b/prboom2/src/MUSIC/opl_queue.c index fb26a3de5..50a4eb8a8 100644 --- a/prboom2/src/MUSIC/opl_queue.c +++ b/prboom2/src/MUSIC/opl_queue.c @@ -36,13 +36,13 @@ typedef struct { opl_callback_t callback; void *data; - unsigned int time; + uint64_t time; } opl_queue_entry_t; struct opl_callback_queue_s { opl_queue_entry_t entries[MAX_OPL_QUEUE]; - int num_entries; + unsigned int num_entries; }; opl_callback_queue_t *OPL_Queue_Create(void) @@ -72,7 +72,7 @@ void OPL_Queue_Clear(opl_callback_queue_t *queue) void OPL_Queue_Push(opl_callback_queue_t *queue, opl_callback_t callback, void *data, - unsigned int time) + uint64_t time) { int entry_id; int parent_id; @@ -197,7 +197,7 @@ int OPL_Queue_Pop(opl_callback_queue_t *queue, return 1; } -unsigned int OPL_Queue_Peek(opl_callback_queue_t *queue) +uint64_t OPL_Queue_Peek(opl_callback_queue_t *queue) { if (queue->num_entries > 0) { @@ -209,6 +209,19 @@ unsigned int OPL_Queue_Peek(opl_callback_queue_t *queue) } } +void OPL_Queue_AdjustCallbacks(opl_callback_queue_t *queue, + uint64_t time, float factor) +{ + int64_t offset; + int i; + + for (i = 0; i < queue->num_entries; ++i) + { + offset = queue->entries[i].time - time; + queue->entries[i].time = time + (uint64_t) (offset / factor); + } +} + #ifdef TEST #include diff --git a/prboom2/src/MUSIC/opl_queue.h b/prboom2/src/MUSIC/opl_queue.h index 44d79576a..36bb3a802 100644 --- a/prboom2/src/MUSIC/opl_queue.h +++ b/prboom2/src/MUSIC/opl_queue.h @@ -36,9 +36,11 @@ void OPL_Queue_Clear(opl_callback_queue_t *queue); void OPL_Queue_Destroy(opl_callback_queue_t *queue); void OPL_Queue_Push(opl_callback_queue_t *queue, opl_callback_t callback, void *data, - unsigned int time); + uint64_t time); int OPL_Queue_Pop(opl_callback_queue_t *queue, opl_callback_t *callback, void **data); -unsigned int OPL_Queue_Peek(opl_callback_queue_t *queue); +uint64_t OPL_Queue_Peek(opl_callback_queue_t *queue); +void OPL_Queue_AdjustCallbacks(opl_callback_queue_t *queue, + uint64_t time, float factor); #endif /* #ifndef OPL_QUEUE_H */ diff --git a/prboom2/src/MUSIC/oplplayer.c b/prboom2/src/MUSIC/oplplayer.c index 228e942a4..2f56bfbb0 100644 --- a/prboom2/src/MUSIC/oplplayer.c +++ b/prboom2/src/MUSIC/oplplayer.c @@ -361,7 +361,7 @@ static dboolean song_looping; // Tempo control variables static unsigned int ticks_per_beat; -static unsigned int ms_per_beat; +static unsigned int us_per_beat; // If true, OPL sound channels are reversed to their correct arrangement // (as intended by the MIDI standard) rather than the backwards one @@ -1276,8 +1276,8 @@ static void PitchBendEvent(opl_track_data_t *track, midi_event_t *event) static void MetaSetTempo(unsigned int tempo) { - // OPL_AdjustCallbacks((float) us_per_beat / tempo); - // us_per_beat = tempo; + OPL_AdjustCallbacks((float) us_per_beat / tempo); + us_per_beat = tempo; } // Process a meta event. @@ -1445,17 +1445,17 @@ static void TrackTimerCallback(void *arg) static void ScheduleTrack(opl_track_data_t *track) { unsigned int nticks; - unsigned int ms; + uint64_t us; // Get the number of milliseconds until the next event. nticks = MIDI_GetDeltaTime(track->iter); - ms = (nticks * ms_per_beat) / ticks_per_beat; + us = ((uint64_t) nticks * us_per_beat) / ticks_per_beat; // Set a timer to be invoked when the next event is // ready to play. - OPL_SetCallback(ms, TrackTimerCallback, track); + OPL_SetCallback(us, TrackTimerCallback, track); } // Initialize a channel. @@ -1520,7 +1520,7 @@ static void I_OPL_PlaySong(const void *handle, int looping) // Default is 120 bpm. // TODO: this is wrong - ms_per_beat = 500; + us_per_beat = 500 * 1000; start_music_volume = current_music_volume = snd_MusicVolume * 127 / 15; From 88ab2cb438ac25c7104cfff9c9cde2012093ffcb Mon Sep 17 00:00:00 2001 From: Roman Fomin Date: Sun, 29 Oct 2023 23:58:36 +0700 Subject: [PATCH 5/5] Rename OPL2 to just OPL --- prboom2/src/MUSIC/oplplayer.c | 2 +- prboom2/src/SDL/i_sound.c | 14 +++++++------- prboom2/src/i_sound.h | 2 +- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/prboom2/src/MUSIC/oplplayer.c b/prboom2/src/MUSIC/oplplayer.c index 2f56bfbb0..366d84748 100644 --- a/prboom2/src/MUSIC/oplplayer.c +++ b/prboom2/src/MUSIC/oplplayer.c @@ -1722,7 +1722,7 @@ int I_OPL_InitMusic(int samplerate) const char *I_OPL_SynthName (void) { - return "opl2 synth player"; + return "opl synth player"; } void I_OPL_RenderSamples (void *dest, unsigned nsamp) diff --git a/prboom2/src/SDL/i_sound.c b/prboom2/src/SDL/i_sound.c index 08e18f1d3..c151afaa4 100644 --- a/prboom2/src/SDL/i_sound.c +++ b/prboom2/src/SDL/i_sound.c @@ -898,7 +898,7 @@ static int music_player_was_init[NUM_MUS_PLAYERS]; #define PLAYER_MAD "mad mp3 player" #define PLAYER_DUMB "dumb tracker player" #define PLAYER_FLUIDSYNTH "fluidsynth midi player" -#define PLAYER_OPL2 "opl2 synth player" +#define PLAYER_OPL "opl synth player" #define PLAYER_PORTMIDI "portmidi midi player" // order in which players are to be tried @@ -908,12 +908,12 @@ char music_player_order[NUM_MUS_PLAYERS][200] = PLAYER_MAD, PLAYER_DUMB, PLAYER_FLUIDSYNTH, - PLAYER_OPL2, + PLAYER_OPL, PLAYER_PORTMIDI, }; const char *midiplayers[midi_player_last + 1] = { - "fluidsynth", "opl2", "portmidi", NULL }; + "fluidsynth", "opl", "portmidi", NULL }; static int current_player = -1; static const void *music_handle = NULL; @@ -1339,12 +1339,12 @@ void M_ChangeMIDIPlayer(void) if (!strcasecmp(snd_midiplayer, midiplayers[midi_player_fluidsynth])) { strcpy(music_player_order[3], PLAYER_FLUIDSYNTH); - strcpy(music_player_order[4], PLAYER_OPL2); + strcpy(music_player_order[4], PLAYER_OPL); strcpy(music_player_order[5], PLAYER_PORTMIDI); } - else if (!strcasecmp(snd_midiplayer, midiplayers[midi_player_opl2])) + else if (!strcasecmp(snd_midiplayer, midiplayers[midi_player_opl])) { - strcpy(music_player_order[3], PLAYER_OPL2); + strcpy(music_player_order[3], PLAYER_OPL); strcpy(music_player_order[4], PLAYER_FLUIDSYNTH); strcpy(music_player_order[5], PLAYER_PORTMIDI); } @@ -1352,7 +1352,7 @@ void M_ChangeMIDIPlayer(void) { strcpy(music_player_order[3], PLAYER_PORTMIDI); strcpy(music_player_order[4], PLAYER_FLUIDSYNTH); - strcpy(music_player_order[5], PLAYER_OPL2); + strcpy(music_player_order[5], PLAYER_OPL); } S_StopMusic(); diff --git a/prboom2/src/i_sound.h b/prboom2/src/i_sound.h index b8ef15fa5..8f6f02e0c 100644 --- a/prboom2/src/i_sound.h +++ b/prboom2/src/i_sound.h @@ -124,7 +124,7 @@ extern int snd_samplerate; typedef enum { midi_player_fluidsynth, - midi_player_opl2, + midi_player_opl, midi_player_portmidi, midi_player_last