opl: Nuked OPL3 v1.7

Reformat the OPL3 code.
Remove the DBOPL core.
The OPL3 chip write delay implementation.
This commit is contained in:
nukeykt 2016-02-28 21:53:03 +09:00
parent e25368ca6b
commit eb3cd65176
14 changed files with 795 additions and 2455 deletions

View file

@ -158,10 +158,6 @@
Filter="cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx"
UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}"
>
<File
RelativePath="..\opl\dbopl.c"
>
</File>
<File
RelativePath="..\opl\ioperm_sys.c"
>
@ -194,6 +190,10 @@
RelativePath="..\opl\opl_win32.c"
>
</File>
<File
RelativePath="..\opl\opl3.c"
>
</File>
</Filter>
<Filter
Name="Header Files"
@ -224,6 +224,10 @@
RelativePath="..\opl\opl_timer.h"
>
</File>
<File
RelativePath="..\opl\opl3.h"
>
</File>
</Filter>
<Filter
Name="Resource Files"

View file

@ -15,6 +15,5 @@ libopl_a_SOURCES = \
opl_timer.c opl_timer.h \
opl_win32.c \
ioperm_sys.c ioperm_sys.h \
dbopl.c dbopl.h \
opl3.c opl3.h

File diff suppressed because it is too large Load diff

View file

@ -1,195 +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.
*/
#include <inttypes.h>
#include "opl.h"
//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;
#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 );
};
*/
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 );
void Chip__GenerateBlock3(Chip *self, Bitu total, Bit32s* output );
// haleyjd 09/09/10: Not standard C.
#ifdef _MSC_VER
#define inline __inline
#endif

View file

@ -57,7 +57,6 @@ static opl_driver_t *driver = NULL;
static int init_stage_reg_writes = 1;
unsigned int opl_sample_rate = 22050;
opl_core_t opl_emu = OPL_CORE_NUKED;
//
// Init/shutdown code.
@ -180,10 +179,9 @@ void OPL_Shutdown(void)
// Set the sample rate used for software OPL emulation.
void OPL_SetSampleRateAndCore(unsigned int rate, opl_core_t core)
void OPL_SetSampleRate(unsigned int rate)
{
opl_sample_rate = rate;
opl_emu = core;
}
void OPL_WritePort(opl_port_t port, unsigned int value)

View file

@ -21,15 +21,6 @@
#include <inttypes.h>
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;
typedef void (*opl_callback_t)(void *data);
// Result from OPL_Init(), indicating what type of OPL chip was detected,
@ -48,12 +39,6 @@ typedef enum
OPL_REGISTER_PORT_OPL3 = 2
} opl_port_t;
typedef enum
{
OPL_CORE_NUKED,
OPL_CORE_DBOPL
} opl_core_t;
#define OPL_NUM_OPERATORS 21
#define OPL_NUM_VOICES 9
@ -96,9 +81,9 @@ opl_init_result_t OPL_Init(unsigned int port_base);
void OPL_Shutdown(void);
// Set the sample rate and the core used for software emulation.
// Set the sample rate used for software emulation.
void OPL_SetSampleRateAndCore(unsigned int rate, opl_core_t core);
void OPL_SetSampleRate(unsigned int rate);
// Write to one of the OPL I/O ports:

1156
opl/opl3.c

File diff suppressed because it is too large Load diff

View file

@ -21,21 +21,35 @@
// OPLx decapsulated(Matthew Gambrell, Olli Niemitalo):
// OPL2 ROMs.
//
// version: 1.6.2
// version: 1.7
//
#ifndef OPL_OPL3_H
#define OPL_OPL3_H
#include "opl.h"
#include <inttypes.h>
typedef struct _opl_slot opl_slot;
typedef struct _opl_channel opl_channel;
typedef struct _opl_chip opl_chip;
#define OPL_WRITEBUF_SIZE 1024
#define OPL_WRITEBUF_DELAY 2
struct _opl_slot {
opl_channel *channel;
opl_chip *chip;
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;
@ -63,10 +77,10 @@ struct _opl_slot {
Bit32u timer;
};
struct _opl_channel {
opl_slot *slots[2];
opl_channel *pair;
opl_chip *chip;
struct _opl3_channel {
opl3_slot *slots[2];
opl3_channel *pair;
opl3_chip *chip;
Bit16s *out[4];
Bit8u chtype;
Bit16u f_num;
@ -78,9 +92,15 @@ struct _opl_channel {
Bit16u cha, chb;
};
struct _opl_chip {
opl_channel channel[18];
opl_slot slot[36];
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;
Bit8u newm;
Bit8u nts;
@ -98,10 +118,18 @@ struct _opl_chip {
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 chip_reset(opl_chip *chip, Bit32u samplerate);
void chip_write(opl_chip *chip, Bit16u reg, Bit8u v);
void chip_update(opl_chip *chip, Bit16s* sndptr, Bit32u numsamples);
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

View file

@ -50,21 +50,9 @@ typedef struct
opl_adjust_callbacks_func adjust_callbacks_func;
} opl_driver_t;
typedef void (*opl_core_init_func)(unsigned int rate);
typedef void (*opl_core_write_func)(unsigned int reg, unsigned int data);
typedef void (*opl_core_fill_buffer_func)(int16_t *buffer, unsigned int nsamples);
typedef struct
{
opl_core_init_func init_func;
opl_core_write_func write_func;
opl_core_fill_buffer_func fill_buffer_func;
} opl_sdl_core_t;
// Sample rate to use when doing software emulation.
extern unsigned int opl_sample_rate;
extern opl_core_t opl_emu;
#endif /* #ifndef OPL_INTERNAL_H */

View file

@ -25,7 +25,6 @@
#include "SDL.h"
#include "SDL_mixer.h"
#include "dbopl.h"
#include "opl3.h"
#include "opl.h"
@ -71,8 +70,7 @@ static uint64_t pause_offset;
// OPL software emulator structure.
static Chip opl_dbopl_chip;
static opl_chip opl_nuked_chip;
static opl3_chip opl_chip;
static int opl_opl3mode;
// Temporary mixing buffer used by the mixing callback.
@ -94,88 +92,6 @@ static int sdl_was_initialized = 0;
static int mixing_freq, mixing_channels;
static Uint16 mixing_format;
// OPL cores.
opl_sdl_core_t *opl_sdl_core = NULL;
static void OPL_Nuked_Init(unsigned int rate)
{
chip_reset(&opl_nuked_chip, rate);
}
static void OPL_Nuked_Write(unsigned int reg, unsigned int data)
{
chip_write(&opl_nuked_chip, reg, data);
}
static void OPL_Nuked_Fill_Buffer(int16_t *buffer, unsigned int nsamples)
{
chip_update(&opl_nuked_chip, (Bit16s*)buffer, nsamples);
}
opl_sdl_core_t opl_nuked_core =
{
OPL_Nuked_Init,
OPL_Nuked_Write,
OPL_Nuked_Fill_Buffer
};
static void OPL_DBOPL_Init(unsigned int rate)
{
DBOPL_InitTables();
Chip__Chip(&opl_dbopl_chip);
Chip__Setup(&opl_dbopl_chip, mixing_freq);
}
static void OPL_DBOPL_Write(unsigned int reg, unsigned int data)
{
Chip__WriteReg(&opl_dbopl_chip, reg, data);
}
static void OPL_DBOPL_Fill_Buffer(int16_t *buffer, unsigned int nsamples)
{
unsigned int i;
// This seems like a reasonable assumption. mix_buffer is
// 1 second long, which should always be much longer than the
// SDL mix buffer.
assert(nsamples < mixing_freq);
if (opl_opl3mode)
{
Chip__GenerateBlock3(&opl_dbopl_chip, nsamples, mix_buffer);
// Mix into the destination buffer, doubling up into stereo.
for (i=0; i<nsamples; ++i)
{
buffer[i * 2] = (int16_t) mix_buffer[i * 2];
buffer[i * 2 + 1] = (int16_t) mix_buffer[i * 2 + 1];
}
}
else
{
Chip__GenerateBlock2(&opl_dbopl_chip, nsamples, mix_buffer);
// Mix into the destination buffer, doubling up into stereo.
for (i=0; i<nsamples; ++i)
{
buffer[i * 2] = (int16_t) mix_buffer[i];
buffer[i * 2 + 1] = (int16_t) mix_buffer[i];
}
}
}
opl_sdl_core_t opl_dbopl_core =
{
OPL_DBOPL_Init,
OPL_DBOPL_Write,
OPL_DBOPL_Fill_Buffer
};
static int SDLIsInitialized(void)
{
int freq, channels;
@ -236,6 +152,21 @@ static void AdvanceTime(unsigned int nsamples)
SDL_UnlockMutex(callback_queue_mutex);
}
// Call the OPL emulator code to fill the specified buffer.
static void FillBuffer(int16_t *buffer, unsigned int nsamples)
{
unsigned int i;
// This seems like a reasonable assumption. mix_buffer is
// 1 second long, which should always be much longer than the
// SDL mix buffer.
assert(nsamples < mixing_freq);
OPL3_GenerateStream(&opl_chip, buffer, nsamples);
}
// Callback function to fill a new sound buffer:
@ -287,7 +218,7 @@ static void OPL_Mix_Callback(void *udata,
// Add emulator output to buffer.
opl_sdl_core->fill_buffer_func(buffer + filled * 2, nsamples);
FillBuffer(buffer + filled * 2, nsamples);
filled += nsamples;
// Invoke callbacks for this point in time.
@ -417,18 +348,7 @@ static int OPL_SDL_Init(unsigned int port_base)
// Create the emulator structure:
switch (opl_emu)
{
case OPL_CORE_NUKED:
default:
opl_sdl_core = &opl_nuked_core;
break;
case OPL_CORE_DBOPL:
opl_sdl_core = &opl_dbopl_core;
break;
}
opl_sdl_core->init_func(mixing_freq);
OPL3_Reset(&opl_chip, mixing_freq);
opl_opl3mode = 0;
callback_mutex = SDL_CreateMutex();
@ -520,7 +440,7 @@ static void WriteRegister(unsigned int reg_num, unsigned int value)
opl_opl3mode = value & 0x01;
default:
opl_sdl_core->write_func(reg_num, value);
OPL3_WriteRegBuffered(&opl_chip, reg_num, value);
break;
}
}

View file

@ -353,7 +353,6 @@ static unsigned int last_perc_count;
char *snd_dmxoption = "";
int opl_io_port = 0x388;
int opl_core = OPL_CORE_NUKED;
// If true, OPL sound channels are reversed to their correct arrangement
// (as intended by the MIDI standard) rather than the backwards one
@ -1761,7 +1760,7 @@ static boolean I_OPL_InitMusic(void)
char *dmxoption;
opl_init_result_t chip_type;
OPL_SetSampleRateAndCore(snd_samplerate, (opl_core_t)opl_core);
OPL_SetSampleRate(snd_samplerate);
chip_type = OPL_Init(opl_io_port);
if (chip_type == OPL_INIT_NONE)

View file

@ -73,7 +73,6 @@ extern music_module_t music_opl_module;
extern opl_driver_ver_t opl_drv_ver;
extern int opl_io_port;
extern int opl_core;
// For native music module:
@ -454,7 +453,6 @@ void I_BindSoundVariables(void)
M_BindIntVariable("snd_samplerate", &snd_samplerate);
M_BindIntVariable("snd_cachesize", &snd_cachesize);
M_BindIntVariable("opl_io_port", &opl_io_port);
M_BindIntVariable("opl_core", &opl_core);
M_BindIntVariable("snd_pitchshift", &snd_pitchshift);
M_BindStringVariable("timidity_cfg_path", &timidity_cfg_path);

View file

@ -838,13 +838,6 @@ static default_t extra_defaults_list[] =
CONFIG_VARIABLE_INT_HEX(opl_io_port),
//!
// OPL emulator. A value of zero enables Nuked OPL core. A value of 1
// enables DOSBox DBOPL core.
//
CONFIG_VARIABLE_INT(opl_core),
//!
// @game doom heretic strife
//

View file

@ -74,19 +74,6 @@ static char *opltype_strings[] =
"OPL3"
};
typedef enum
{
OPLCORE_NUKED,
OPLMODE_DBOPL,
NUM_OPLCORES,
} oplcore_t;
static char *oplcore_strings[] =
{
"Nuked OPL",
"DOSBox DBOPL"
};
static char *cfg_extension[] = { "cfg", NULL };
// Config file variables:
@ -127,7 +114,6 @@ static int snd_mport = 0;
static int snd_sfxmode;
static int snd_musicmode;
static int snd_oplmode;
static int opl_core = OPLCORE_NUKED;
static void UpdateSndDevices(TXT_UNCAST_ARG(widget), TXT_UNCAST_ARG(data))
{
@ -209,8 +195,6 @@ static void UpdateExtraTable(TXT_UNCAST_ARG(widget),
TXT_AddWidgets(extra_table,
TXT_NewLabel("OPL type"),
OPLTypeSelector(),
TXT_NewLabel("OPL emulator"),
TXT_NewDropdownList(&opl_core, oplcore_strings, 2),
NULL);
break;
@ -400,7 +384,6 @@ void BindSoundVariables(void)
M_BindIntVariable("snd_cachesize", &snd_cachesize);
M_BindIntVariable("opl_io_port", &opl_io_port);
M_BindIntVariable("opl_core", &opl_core);
M_BindIntVariable("snd_pitchshift", &snd_pitchshift);