Initial commit
This commit is contained in:
1544
audio/softsynth/opl/dbopl.cpp
Normal file
1544
audio/softsynth/opl/dbopl.cpp
Normal file
File diff suppressed because it is too large
Load Diff
283
audio/softsynth/opl/dbopl.h
Normal file
283
audio/softsynth/opl/dbopl.h
Normal file
@@ -0,0 +1,283 @@
|
||||
/*
|
||||
* Copyright (C) 2002-2011 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.
|
||||
*/
|
||||
|
||||
// Last synch with DOSBox SVN trunk r3752
|
||||
|
||||
#ifndef AUDIO_SOFTSYNTH_OPL_DBOPL_H
|
||||
#define AUDIO_SOFTSYNTH_OPL_DBOPL_H
|
||||
|
||||
#include "common/scummsys.h"
|
||||
|
||||
#ifndef DISABLE_DOSBOX_OPL
|
||||
|
||||
namespace OPL {
|
||||
namespace DOSBox {
|
||||
|
||||
//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
|
||||
|
||||
namespace DBOPL {
|
||||
|
||||
// Type aliases for the DBOPL code
|
||||
typedef int Bits;
|
||||
typedef uint Bitu;
|
||||
|
||||
typedef int8 Bit8s;
|
||||
typedef uint8 Bit8u;
|
||||
|
||||
typedef int16 Bit16s;
|
||||
typedef uint16 Bit16u;
|
||||
|
||||
typedef int32 Bit32s;
|
||||
typedef uint32 Bit32u;
|
||||
|
||||
#define DB_FASTCALL
|
||||
#define GCC_UNLIKELY(x) (x)
|
||||
#define INLINE inline
|
||||
// -------------------------------
|
||||
|
||||
struct Chip;
|
||||
struct Operator;
|
||||
struct Channel;
|
||||
|
||||
#if (DBOPL_WAVE == WAVE_HANDLER)
|
||||
typedef Bits ( DB_FASTCALL *WaveHandler) ( Bitu i, Bitu volume );
|
||||
#endif
|
||||
|
||||
typedef Bits ( DBOPL::Operator::*VolumeHandler) ( );
|
||||
typedef Channel* ( DBOPL::Channel::*SynthHandler) ( 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
|
||||
};
|
||||
|
||||
struct Operator {
|
||||
public:
|
||||
//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
|
||||
} State;
|
||||
|
||||
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;
|
||||
private:
|
||||
void SetState( Bit8u s );
|
||||
void UpdateAttack( const Chip* chip );
|
||||
void UpdateRelease( const Chip* chip );
|
||||
void UpdateDecay( const Chip* chip );
|
||||
public:
|
||||
void UpdateAttenuation();
|
||||
void UpdateRates( const Chip* chip );
|
||||
void UpdateFrequency( );
|
||||
|
||||
void Write20( const Chip* chip, Bit8u val );
|
||||
void Write40( const Chip* chip, Bit8u val );
|
||||
void Write60( const Chip* chip, Bit8u val );
|
||||
void Write80( const Chip* chip, Bit8u val );
|
||||
void WriteE0( const Chip* chip, Bit8u val );
|
||||
|
||||
bool Silent() const;
|
||||
void Prepare( const Chip* chip );
|
||||
|
||||
void KeyOn( Bit8u mask);
|
||||
void KeyOff( Bit8u mask);
|
||||
|
||||
template< State state>
|
||||
Bits TemplateVolume( );
|
||||
|
||||
Bit32s RateForward( Bit32u add );
|
||||
Bitu ForwardWave();
|
||||
Bitu ForwardVolume();
|
||||
|
||||
Bits GetSample( Bits modulation );
|
||||
Bits GetWave( Bitu index, Bitu vol );
|
||||
public:
|
||||
Operator();
|
||||
};
|
||||
|
||||
struct Channel {
|
||||
Operator op[2];
|
||||
inline Operator* Op( Bitu index ) {
|
||||
return &( ( this + (index >> 1) )->op[ index & 1 ]);
|
||||
}
|
||||
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;
|
||||
|
||||
//Forward the channel data to the operators of the channel
|
||||
void SetChanData( const Chip* chip, Bit32u data );
|
||||
//Change in the chandata, check for new values and if we have to forward to operators
|
||||
void UpdateFrequency( const Chip* chip, Bit8u fourOp );
|
||||
void WriteA0( const Chip* chip, Bit8u val );
|
||||
void WriteB0( const Chip* chip, Bit8u val );
|
||||
void WriteC0( const Chip* chip, Bit8u val );
|
||||
void ResetC0( const Chip* chip );
|
||||
|
||||
//call this for the first channel
|
||||
template< bool opl3Mode >
|
||||
void GeneratePercussion( Chip* chip, Bit32s* output );
|
||||
|
||||
//Generate blocks of data in specific modes
|
||||
template<SynthMode mode>
|
||||
Channel* BlockTemplate( Chip* chip, Bit32u samples, Bit32s* output );
|
||||
Channel();
|
||||
};
|
||||
|
||||
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;
|
||||
|
||||
//Return the maximum amount of samples before and LFO change
|
||||
Bit32u ForwardLFO( Bit32u samples );
|
||||
Bit32u ForwardNoise();
|
||||
|
||||
void WriteBD( Bit8u val );
|
||||
void WriteReg(Bit32u reg, Bit8u val );
|
||||
|
||||
Bit32u WriteAddr( Bit32u port, Bit8u val );
|
||||
|
||||
void GenerateBlock2( Bitu samples, Bit32s* output );
|
||||
void GenerateBlock3( Bitu samples, Bit32s* output );
|
||||
|
||||
void Generate( Bit32u samples );
|
||||
void Setup( Bit32u r );
|
||||
|
||||
Chip();
|
||||
};
|
||||
|
||||
void InitTables();
|
||||
|
||||
} //Namespace
|
||||
} // End of namespace DOSBox
|
||||
} // End of namespace OPL
|
||||
|
||||
#endif // !DISABLE_DOSBOX_OPL
|
||||
|
||||
#endif
|
||||
356
audio/softsynth/opl/dosbox.cpp
Normal file
356
audio/softsynth/opl/dosbox.cpp
Normal file
@@ -0,0 +1,356 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* Based on AdLib emulation code of DOSBox
|
||||
* Copyright (C) 2002-2009 The DOSBox Team
|
||||
* Licensed under GPLv2+
|
||||
* http://www.dosbox.com
|
||||
*/
|
||||
|
||||
#ifndef DISABLE_DOSBOX_OPL
|
||||
|
||||
#include "dosbox.h"
|
||||
#include "dbopl.h"
|
||||
|
||||
#include "audio/mixer.h"
|
||||
#include "common/system.h"
|
||||
#include "common/scummsys.h"
|
||||
#include "common/util.h"
|
||||
|
||||
#include <math.h>
|
||||
#include <string.h>
|
||||
|
||||
namespace OPL {
|
||||
namespace DOSBox {
|
||||
|
||||
Timer::Timer() {
|
||||
masked = false;
|
||||
overflow = false;
|
||||
enabled = false;
|
||||
counter = 0;
|
||||
delay = 0;
|
||||
}
|
||||
|
||||
void Timer::update(double time) {
|
||||
if (!enabled || !delay)
|
||||
return;
|
||||
double deltaStart = time - startTime;
|
||||
// Only set the overflow flag when not masked
|
||||
if (deltaStart >= 0 && !masked)
|
||||
overflow = 1;
|
||||
}
|
||||
|
||||
void Timer::reset(double time) {
|
||||
overflow = false;
|
||||
if (!delay || !enabled)
|
||||
return;
|
||||
double delta = (time - startTime);
|
||||
double rem = fmod(delta, delay);
|
||||
double next = delay - rem;
|
||||
startTime = time + next;
|
||||
}
|
||||
|
||||
void Timer::stop() {
|
||||
enabled = false;
|
||||
}
|
||||
|
||||
void Timer::start(double time, int scale) {
|
||||
//Don't enable again
|
||||
if (enabled)
|
||||
return;
|
||||
enabled = true;
|
||||
delay = 0.001 * (256 - counter) * scale;
|
||||
startTime = time + delay;
|
||||
}
|
||||
|
||||
bool Chip::write(uint32 reg, uint8 val) {
|
||||
switch (reg) {
|
||||
case 0x02:
|
||||
timer[0].counter = val;
|
||||
return true;
|
||||
case 0x03:
|
||||
timer[1].counter = val;
|
||||
return true;
|
||||
case 0x04:
|
||||
{
|
||||
double time = g_system->getMillis() / 1000.0;
|
||||
|
||||
if (val & 0x80) {
|
||||
timer[0].reset(time);
|
||||
timer[1].reset(time);
|
||||
} else {
|
||||
timer[0].update(time);
|
||||
timer[1].update(time);
|
||||
|
||||
if (val & 0x1)
|
||||
timer[0].start(time, 80);
|
||||
else
|
||||
timer[0].stop();
|
||||
|
||||
timer[0].masked = (val & 0x40) > 0;
|
||||
|
||||
if (timer[0].masked)
|
||||
timer[0].overflow = false;
|
||||
|
||||
if (val & 0x2)
|
||||
timer[1].start(time, 320);
|
||||
else
|
||||
timer[1].stop();
|
||||
|
||||
timer[1].masked = (val & 0x20) > 0;
|
||||
|
||||
if (timer[1].masked)
|
||||
timer[1].overflow = false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
uint8 Chip::read() {
|
||||
double time = g_system->getMillis() / 1000.0;
|
||||
|
||||
timer[0].update(time);
|
||||
timer[1].update(time);
|
||||
|
||||
uint8 ret = 0;
|
||||
// Overflow won't be set if a channel is masked
|
||||
if (timer[0].overflow) {
|
||||
ret |= 0x40;
|
||||
ret |= 0x80;
|
||||
}
|
||||
if (timer[1].overflow) {
|
||||
ret |= 0x20;
|
||||
ret |= 0x80;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
OPL::OPL(Config::OplType type) : _type(type), _rate(0), _emulator(nullptr) {
|
||||
}
|
||||
|
||||
OPL::~OPL() {
|
||||
stop();
|
||||
free();
|
||||
}
|
||||
|
||||
void OPL::free() {
|
||||
delete _emulator;
|
||||
_emulator = nullptr;
|
||||
}
|
||||
|
||||
bool OPL::init() {
|
||||
free();
|
||||
|
||||
memset(&_reg, 0, sizeof(_reg));
|
||||
ARRAYCLEAR(_chip);
|
||||
|
||||
_emulator = new DBOPL::Chip();
|
||||
if (!_emulator)
|
||||
return false;
|
||||
|
||||
DBOPL::InitTables();
|
||||
_rate = g_system->getMixer()->getOutputRate();
|
||||
_emulator->Setup(_rate);
|
||||
|
||||
if (_type == Config::kDualOpl2) {
|
||||
// Setup opl3 mode in the hander
|
||||
_emulator->WriteReg(0x105, 1);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void OPL::reset() {
|
||||
init();
|
||||
}
|
||||
|
||||
void OPL::write(int port, int val) {
|
||||
if (port&1) {
|
||||
switch (_type) {
|
||||
case Config::kOpl2:
|
||||
case Config::kOpl3:
|
||||
if (!_chip[0].write(_reg.normal, val))
|
||||
_emulator->WriteReg(_reg.normal, val);
|
||||
break;
|
||||
case Config::kDualOpl2:
|
||||
// Not a 0x??8 port, then write to a specific port
|
||||
if (!(port & 0x8)) {
|
||||
byte index = (port & 2) >> 1;
|
||||
dualWrite(index, _reg.dual[index], val);
|
||||
} else {
|
||||
//Write to both ports
|
||||
dualWrite(0, _reg.dual[0], val);
|
||||
dualWrite(1, _reg.dual[1], val);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
// Ask the handler to write the address
|
||||
// Make sure to clip them in the right range
|
||||
switch (_type) {
|
||||
case Config::kOpl2:
|
||||
_reg.normal = _emulator->WriteAddr(port, val) & 0xff;
|
||||
break;
|
||||
case Config::kOpl3:
|
||||
_reg.normal = _emulator->WriteAddr(port, val) & 0x1ff;
|
||||
break;
|
||||
case Config::kDualOpl2:
|
||||
// Not a 0x?88 port, when write to a specific side
|
||||
if (!(port & 0x8)) {
|
||||
byte index = (port & 2) >> 1;
|
||||
_reg.dual[index] = val & 0xff;
|
||||
} else {
|
||||
_reg.dual[0] = val & 0xff;
|
||||
_reg.dual[1] = val & 0xff;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void OPL::writeReg(int r, int v) {
|
||||
int tempReg = 0;
|
||||
switch (_type) {
|
||||
case Config::kOpl2:
|
||||
case Config::kDualOpl2:
|
||||
case Config::kOpl3:
|
||||
// We can't use _handler->writeReg here directly, since it would miss timer changes.
|
||||
|
||||
// Backup old setup register
|
||||
tempReg = _reg.normal;
|
||||
|
||||
// We directly allow writing to secondary OPL3 registers by using
|
||||
// register values >= 0x100.
|
||||
if (_type == Config::kOpl3 && r >= 0x100) {
|
||||
// We need to set the register we want to write to via port 0x222,
|
||||
// since we want to write to the secondary register set.
|
||||
write(0x222, r);
|
||||
// Do the real writing to the register
|
||||
write(0x223, v);
|
||||
} else {
|
||||
// We need to set the register we want to write to via port 0x388
|
||||
write(0x388, r);
|
||||
// Do the real writing to the register
|
||||
write(0x389, v);
|
||||
}
|
||||
|
||||
// Restore the old register
|
||||
if (_type == Config::kOpl3 && tempReg >= 0x100) {
|
||||
write(0x222, tempReg & ~0x100);
|
||||
} else {
|
||||
write(0x388, tempReg);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
};
|
||||
}
|
||||
|
||||
void OPL::dualWrite(uint8 index, uint8 reg, uint8 val) {
|
||||
// Make sure you don't use opl3 features
|
||||
// Don't allow write to disable opl3
|
||||
if (reg == 5)
|
||||
return;
|
||||
|
||||
// Only allow 4 waveforms
|
||||
if (reg >= 0xE0 && reg <= 0xE8)
|
||||
val &= 3;
|
||||
|
||||
// Write to the timer?
|
||||
if (_chip[index].write(reg, val))
|
||||
return;
|
||||
|
||||
// Enabling panning
|
||||
if (reg >= 0xC0 && reg <= 0xC8) {
|
||||
val &= 15;
|
||||
val |= index ? 0xA0 : 0x50;
|
||||
}
|
||||
|
||||
uint32 fullReg = reg + (index ? 0x100 : 0);
|
||||
_emulator->WriteReg(fullReg, val);
|
||||
}
|
||||
|
||||
void OPL::generateSamples(int16 *buffer, int length) {
|
||||
const uint bufferLength = 512;
|
||||
int32 tempBuffer[bufferLength * 2];
|
||||
|
||||
if (isStereo()) {
|
||||
// For stereo OPL cards, we divide the sample count by 2,
|
||||
// to match stereo AudioStream behavior.
|
||||
length >>= 1;
|
||||
if (_emulator->opl3Active) {
|
||||
// DUAL_OPL2 or OPL3 in OPL3 mode (stereo)
|
||||
while (length > 0) {
|
||||
const uint readSamples = MIN<uint>(length, bufferLength);
|
||||
const uint readSamples2 = (readSamples << 1);
|
||||
|
||||
_emulator->GenerateBlock3(readSamples, tempBuffer);
|
||||
|
||||
for (uint i = 0; i < readSamples2; ++i)
|
||||
buffer[i] = tempBuffer[i];
|
||||
|
||||
buffer += readSamples2;
|
||||
length -= readSamples;
|
||||
}
|
||||
} else {
|
||||
// OPL3 (stereo) in OPL2 compatibility mode (mono)
|
||||
while (length > 0) {
|
||||
const uint readSamples = MIN<uint>(length, bufferLength);
|
||||
const uint readSamples2 = (readSamples << 1);
|
||||
|
||||
_emulator->GenerateBlock2(readSamples, tempBuffer);
|
||||
|
||||
for (uint i = 0, j = 0; i < readSamples; ++i, j += 2)
|
||||
buffer[j] = buffer[j + 1] = tempBuffer[i];
|
||||
|
||||
buffer += readSamples2;
|
||||
length -= readSamples;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// OPL2
|
||||
while (length > 0) {
|
||||
const uint readSamples = MIN<uint>(length, bufferLength << 1);
|
||||
|
||||
_emulator->GenerateBlock2(readSamples, tempBuffer);
|
||||
|
||||
for (uint i = 0; i < readSamples; ++i)
|
||||
buffer[i] = tempBuffer[i];
|
||||
|
||||
buffer += readSamples;
|
||||
length -= readSamples;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // End of namespace DOSBox
|
||||
} // End of namespace OPL
|
||||
|
||||
#endif // !DISABLE_DOSBOX_ADLIB
|
||||
107
audio/softsynth/opl/dosbox.h
Normal file
107
audio/softsynth/opl/dosbox.h
Normal file
@@ -0,0 +1,107 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* Based on OPL emulation code of DOSBox
|
||||
* Copyright (C) 2002-2009 The DOSBox Team
|
||||
* Licensed under GPLv2+
|
||||
* http://www.dosbox.com
|
||||
*/
|
||||
|
||||
#ifndef AUDIO_SOFTSYNTH_OPL_DOSBOX_H
|
||||
#define AUDIO_SOFTSYNTH_OPL_DOSBOX_H
|
||||
|
||||
#ifndef DISABLE_DOSBOX_OPL
|
||||
|
||||
#include "audio/fmopl.h"
|
||||
|
||||
namespace OPL {
|
||||
namespace DOSBox {
|
||||
|
||||
struct Timer {
|
||||
double startTime;
|
||||
double delay;
|
||||
bool enabled, overflow, masked;
|
||||
uint8 counter;
|
||||
|
||||
Timer();
|
||||
|
||||
//Call update before making any further changes
|
||||
void update(double time);
|
||||
|
||||
//On a reset make sure the start is in sync with the next cycle
|
||||
void reset(double time);
|
||||
|
||||
void stop();
|
||||
|
||||
void start(double time, int scale);
|
||||
};
|
||||
|
||||
struct Chip {
|
||||
//Last selected register
|
||||
Timer timer[2];
|
||||
//Check for it being a write to the timer
|
||||
bool write(uint32 addr, uint8 val);
|
||||
//Read the current timer state, will use current double
|
||||
uint8 read();
|
||||
};
|
||||
|
||||
namespace DBOPL {
|
||||
struct Chip;
|
||||
} // end of namespace DBOPL
|
||||
|
||||
class OPL : public ::OPL::OPL, public Audio::EmulatedChip {
|
||||
private:
|
||||
Config::OplType _type;
|
||||
uint _rate;
|
||||
|
||||
DBOPL::Chip *_emulator;
|
||||
::OPL::DOSBox::Chip _chip[2];
|
||||
union {
|
||||
uint16 normal;
|
||||
uint8 dual[2];
|
||||
} _reg;
|
||||
|
||||
void free();
|
||||
void dualWrite(uint8 index, uint8 reg, uint8 val);
|
||||
public:
|
||||
OPL(Config::OplType type);
|
||||
~OPL();
|
||||
|
||||
bool init();
|
||||
void reset();
|
||||
|
||||
void write(int a, int v);
|
||||
|
||||
void writeReg(int r, int v);
|
||||
|
||||
bool isStereo() const { return _type != Config::kOpl2; }
|
||||
|
||||
protected:
|
||||
void generateSamples(int16 *buffer, int length);
|
||||
};
|
||||
|
||||
} // End of namespace DOSBox
|
||||
} // End of namespace OPL
|
||||
|
||||
#endif // !DISABLE_DOSBOX_OPL
|
||||
|
||||
#endif
|
||||
1255
audio/softsynth/opl/mame.cpp
Normal file
1255
audio/softsynth/opl/mame.cpp
Normal file
File diff suppressed because it is too large
Load Diff
199
audio/softsynth/opl/mame.h
Normal file
199
audio/softsynth/opl/mame.h
Normal file
@@ -0,0 +1,199 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* LGPL licensed version of MAMEs fmopl (V0.37a modified) by
|
||||
* Tatsuyuki Satoh. Included from LGPL'ed AdPlug.
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#ifndef AUDIO_SOFTSYNTH_OPL_MAME_H
|
||||
#define AUDIO_SOFTSYNTH_OPL_MAME_H
|
||||
|
||||
#include "common/scummsys.h"
|
||||
#include "common/random.h"
|
||||
|
||||
#include "audio/fmopl.h"
|
||||
|
||||
namespace OPL {
|
||||
namespace MAME {
|
||||
|
||||
enum {
|
||||
FMOPL_ENV_BITS_HQ = 16,
|
||||
FMOPL_ENV_BITS_MQ = 8,
|
||||
FMOPL_ENV_BITS_LQ = 8,
|
||||
FMOPL_EG_ENT_HQ = 4096,
|
||||
FMOPL_EG_ENT_MQ = 1024,
|
||||
FMOPL_EG_ENT_LQ = 128
|
||||
};
|
||||
|
||||
|
||||
typedef void (*OPL_TIMERHANDLER)(int channel,double interval_Sec);
|
||||
typedef void (*OPL_IRQHANDLER)(int param,int irq);
|
||||
typedef void (*OPL_UPDATEHANDLER)(int param,int min_interval_us);
|
||||
|
||||
#define OPL_TYPE_WAVESEL 0x01 /* waveform select */
|
||||
|
||||
/* Saving is necessary for member of the 'R' mark for suspend/resume */
|
||||
/* ---------- OPL one of slot ---------- */
|
||||
typedef struct fm_opl_slot {
|
||||
int TL; /* total level :TL << 8 */
|
||||
int TLL; /* adjusted now TL */
|
||||
uint8 KSR; /* key scale rate :(shift down bit) */
|
||||
int *AR; /* attack rate :&AR_TABLE[AR<<2] */
|
||||
int *DR; /* decay rate :&DR_TABLE[DR<<2] */
|
||||
int SL; /* sustain level :SL_TABLE[SL] */
|
||||
int *RR; /* release rate :&DR_TABLE[RR<<2] */
|
||||
uint8 ksl; /* keyscale level :(shift down bits) */
|
||||
uint8 ksr; /* key scale rate :kcode>>KSR */
|
||||
uint mul; /* multiple :ML_TABLE[ML] */
|
||||
uint Cnt; /* frequency count */
|
||||
uint Incr; /* frequency step */
|
||||
|
||||
/* envelope generator state */
|
||||
uint8 eg_typ;/* envelope type flag */
|
||||
uint8 evm; /* envelope phase */
|
||||
int evc; /* envelope counter */
|
||||
int eve; /* envelope counter end point */
|
||||
int evs; /* envelope counter step */
|
||||
int evsa; /* envelope step for AR :AR[ksr] */
|
||||
int evsd; /* envelope step for DR :DR[ksr] */
|
||||
int evsr; /* envelope step for RR :RR[ksr] */
|
||||
|
||||
/* LFO */
|
||||
uint8 ams; /* ams flag */
|
||||
uint8 vib; /* vibrate flag */
|
||||
/* wave selector */
|
||||
int **wavetable;
|
||||
} OPL_SLOT;
|
||||
|
||||
/* ---------- OPL one of channel ---------- */
|
||||
typedef struct fm_opl_channel {
|
||||
OPL_SLOT SLOT[2];
|
||||
uint8 CON; /* connection type */
|
||||
uint8 FB; /* feed back :(shift down bit)*/
|
||||
int *connect1; /* slot1 output pointer */
|
||||
int *connect2; /* slot2 output pointer */
|
||||
int op1_out[2]; /* slot1 output for selfeedback */
|
||||
|
||||
/* phase generator state */
|
||||
uint block_fnum; /* block+fnum */
|
||||
uint8 kcode; /* key code : KeyScaleCode */
|
||||
uint fc; /* Freq. Increment base */
|
||||
uint ksl_base; /* KeyScaleLevel Base step */
|
||||
uint8 keyon; /* key on/off flag */
|
||||
} OPL_CH;
|
||||
|
||||
/* OPL state */
|
||||
typedef struct fm_opl_f {
|
||||
uint8 type; /* chip type */
|
||||
int clock; /* master clock (Hz) */
|
||||
int rate; /* sampling rate (Hz) */
|
||||
double freqbase; /* frequency base */
|
||||
double TimerBase; /* Timer base time (==sampling time) */
|
||||
uint8 address; /* address register */
|
||||
uint8 status; /* status flag */
|
||||
uint8 statusmask; /* status mask */
|
||||
uint mode; /* Reg.08 : CSM , notesel,etc. */
|
||||
|
||||
/* Timer */
|
||||
int T[2]; /* timer counter */
|
||||
uint8 st[2]; /* timer enable */
|
||||
|
||||
/* FM channel slots */
|
||||
OPL_CH *P_CH; /* pointer of CH */
|
||||
int max_ch; /* maximum channel */
|
||||
|
||||
/* Rhythm sention */
|
||||
uint8 rhythm; /* Rhythm mode , key flag */
|
||||
|
||||
/* time tables */
|
||||
int AR_TABLE[76]; /* atttack rate tables */
|
||||
int DR_TABLE[76]; /* decay rate tables */
|
||||
uint FN_TABLE[1024];/* fnumber -> increment counter */
|
||||
|
||||
/* LFO */
|
||||
int *ams_table;
|
||||
int *vib_table;
|
||||
int amsCnt;
|
||||
int amsIncr;
|
||||
int vibCnt;
|
||||
int vibIncr;
|
||||
|
||||
/* wave selector enable flag */
|
||||
uint8 wavesel;
|
||||
|
||||
/* external event callback handler */
|
||||
OPL_TIMERHANDLER TimerHandler; /* TIMER handler */
|
||||
int TimerParam; /* TIMER parameter */
|
||||
OPL_IRQHANDLER IRQHandler; /* IRQ handler */
|
||||
int IRQParam; /* IRQ parameter */
|
||||
OPL_UPDATEHANDLER UpdateHandler; /* stream update handler */
|
||||
int UpdateParam; /* stream update parameter */
|
||||
|
||||
Common::RandomSource *rnd;
|
||||
} FM_OPL;
|
||||
|
||||
/* ---------- Generic interface section ---------- */
|
||||
#define OPL_TYPE_YM3526 (0)
|
||||
#define OPL_TYPE_YM3812 (OPL_TYPE_WAVESEL)
|
||||
|
||||
void OPLBuildTables(int ENV_BITS_PARAM, int EG_ENT_PARAM);
|
||||
|
||||
FM_OPL *OPLCreate(int type, int clock, int rate);
|
||||
void OPLDestroy(FM_OPL *OPL);
|
||||
void OPLSetTimerHandler(FM_OPL *OPL, OPL_TIMERHANDLER TimerHandler, int channelOffset);
|
||||
void OPLSetIRQHandler(FM_OPL *OPL, OPL_IRQHANDLER IRQHandler, int param);
|
||||
void OPLSetUpdateHandler(FM_OPL *OPL, OPL_UPDATEHANDLER UpdateHandler, int param);
|
||||
|
||||
void OPLResetChip(FM_OPL *OPL);
|
||||
int OPLWrite(FM_OPL *OPL, int a, int v);
|
||||
unsigned char OPLRead(FM_OPL *OPL, int a);
|
||||
int OPLTimerOver(FM_OPL *OPL, int c);
|
||||
void OPLWriteReg(FM_OPL *OPL, int r, int v);
|
||||
void YM3812UpdateOne(FM_OPL *OPL, int16 *buffer, int length);
|
||||
|
||||
// Factory method
|
||||
FM_OPL *makeAdLibOPL(int rate);
|
||||
|
||||
// OPL API implementation
|
||||
class OPL : public ::OPL::OPL, public Audio::EmulatedChip {
|
||||
private:
|
||||
FM_OPL *_opl;
|
||||
public:
|
||||
OPL() : _opl(0) {}
|
||||
~OPL();
|
||||
|
||||
bool init();
|
||||
void reset();
|
||||
|
||||
void write(int a, int v);
|
||||
|
||||
void writeReg(int r, int v);
|
||||
|
||||
bool isStereo() const { return false; }
|
||||
|
||||
protected:
|
||||
void generateSamples(int16 *buffer, int length);
|
||||
};
|
||||
|
||||
} // End of namespace MAME
|
||||
} // End of namespace OPL
|
||||
|
||||
#endif
|
||||
1636
audio/softsynth/opl/nuked.cpp
Normal file
1636
audio/softsynth/opl/nuked.cpp
Normal file
File diff suppressed because it is too large
Load Diff
197
audio/softsynth/opl/nuked.h
Normal file
197
audio/softsynth/opl/nuked.h
Normal file
@@ -0,0 +1,197 @@
|
||||
//
|
||||
// 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 AUDIO_SOFTSYNTH_OPL_NUKED_H
|
||||
#define AUDIO_SOFTSYNTH_OPL_NUKED_H
|
||||
|
||||
#include "common/scummsys.h"
|
||||
#include "audio/fmopl.h"
|
||||
|
||||
#ifndef DISABLE_NUKED_OPL
|
||||
|
||||
#ifndef OPL_ENABLE_STEREOEXT
|
||||
#define OPL_ENABLE_STEREOEXT 0
|
||||
#endif
|
||||
|
||||
#define OPL_WRITEBUF_SIZE 1024
|
||||
#define OPL_WRITEBUF_DELAY 2
|
||||
|
||||
namespace OPL {
|
||||
namespace NUKED {
|
||||
|
||||
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;
|
||||
int16_t out;
|
||||
int16_t fbmod;
|
||||
int16_t *mod;
|
||||
int16_t prout;
|
||||
uint16_t eg_rout;
|
||||
uint16_t eg_out;
|
||||
uint8_t eg_inc;
|
||||
uint8_t eg_gen;
|
||||
uint8_t eg_rate;
|
||||
uint8_t eg_ksl;
|
||||
uint8_t *trem;
|
||||
uint8_t reg_vib;
|
||||
uint8_t reg_type;
|
||||
uint8_t reg_ksr;
|
||||
uint8_t reg_mult;
|
||||
uint8_t reg_ksl;
|
||||
uint8_t reg_tl;
|
||||
uint8_t reg_ar;
|
||||
uint8_t reg_dr;
|
||||
uint8_t reg_sl;
|
||||
uint8_t reg_rr;
|
||||
uint8_t reg_wf;
|
||||
uint8_t key;
|
||||
uint32_t pg_reset;
|
||||
uint32_t pg_phase;
|
||||
uint16_t pg_phase_out;
|
||||
uint8_t slot_num;
|
||||
};
|
||||
|
||||
struct _opl3_channel {
|
||||
opl3_slot *slotz[2];/*Don't use "slots" keyword to avoid conflict with Qt applications*/
|
||||
opl3_channel *pair;
|
||||
opl3_chip *chip;
|
||||
int16_t *out[4];
|
||||
|
||||
#if OPL_ENABLE_STEREOEXT
|
||||
int32_t leftpan;
|
||||
int32_t rightpan;
|
||||
#endif
|
||||
|
||||
uint8_t chtype;
|
||||
uint16_t f_num;
|
||||
uint8_t block;
|
||||
uint8_t fb;
|
||||
uint8_t con;
|
||||
uint8_t alg;
|
||||
uint8_t ksv;
|
||||
uint16_t cha, chb;
|
||||
uint16_t chc, chd;
|
||||
uint8_t ch_num;
|
||||
};
|
||||
|
||||
typedef struct _opl3_writebuf {
|
||||
uint64_t time;
|
||||
uint16_t reg;
|
||||
uint8_t data;
|
||||
} opl3_writebuf;
|
||||
|
||||
struct _opl3_chip {
|
||||
opl3_channel channel[18];
|
||||
opl3_slot slot[36];
|
||||
uint16_t timer;
|
||||
uint64_t eg_timer;
|
||||
uint8_t eg_timerrem;
|
||||
uint8_t eg_state;
|
||||
uint8_t eg_add;
|
||||
uint8_t eg_timer_lo;
|
||||
uint8_t newm;
|
||||
uint8_t nts;
|
||||
uint8_t rhy;
|
||||
uint8_t vibpos;
|
||||
uint8_t vibshift;
|
||||
uint8_t tremolo;
|
||||
uint8_t tremolopos;
|
||||
uint8_t tremoloshift;
|
||||
uint32_t noise;
|
||||
int16_t zeromod;
|
||||
int32_t mixbuff[4];
|
||||
uint8_t rm_hh_bit2;
|
||||
uint8_t rm_hh_bit3;
|
||||
uint8_t rm_hh_bit7;
|
||||
uint8_t rm_hh_bit8;
|
||||
uint8_t rm_tc_bit3;
|
||||
uint8_t rm_tc_bit5;
|
||||
|
||||
#if OPL_ENABLE_STEREOEXT
|
||||
uint8_t stereoext;
|
||||
#endif
|
||||
|
||||
/* OPL3L */
|
||||
int32_t rateratio;
|
||||
int32_t samplecnt;
|
||||
int16_t oldsamples[4];
|
||||
int16_t samples[4];
|
||||
|
||||
uint64_t writebuf_samplecnt;
|
||||
uint32_t writebuf_cur;
|
||||
uint32_t writebuf_last;
|
||||
uint64_t writebuf_lasttime;
|
||||
opl3_writebuf writebuf[OPL_WRITEBUF_SIZE];
|
||||
};
|
||||
|
||||
void OPL3_Generate(opl3_chip *chip, int16_t *buf);
|
||||
void OPL3_GenerateResampled(opl3_chip *chip, int16_t *buf);
|
||||
void OPL3_Reset(opl3_chip *chip, uint32_t samplerate);
|
||||
void OPL3_WriteReg(opl3_chip *chip, uint16_t reg, uint8_t v);
|
||||
void OPL3_WriteRegBuffered(opl3_chip *chip, uint16_t reg, uint8_t v);
|
||||
void OPL3_GenerateStream(opl3_chip *chip, int16_t *sndptr, uint32_t numsamples);
|
||||
|
||||
void OPL3_Generate4Ch(opl3_chip *chip, int16_t *buf4);
|
||||
void OPL3_Generate4ChResampled(opl3_chip *chip, int16_t *buf4);
|
||||
void OPL3_Generate4ChStream(opl3_chip *chip, int16_t *sndptr1, int16_t *sndptr2, uint32_t numsamples);
|
||||
|
||||
class OPL : public ::OPL::OPL, public Audio::EmulatedChip {
|
||||
private:
|
||||
Config::OplType _type;
|
||||
uint _rate;
|
||||
opl3_chip chip;
|
||||
uint address[2];
|
||||
void dualWrite(uint8 index, uint8 reg, uint8 val);
|
||||
|
||||
public:
|
||||
OPL(Config::OplType type);
|
||||
~OPL();
|
||||
|
||||
bool init();
|
||||
void reset();
|
||||
|
||||
void write(int a, int v);
|
||||
|
||||
void writeReg(int r, int v);
|
||||
|
||||
bool isStereo() const { return true; }
|
||||
|
||||
protected:
|
||||
void generateSamples(int16 *buffer, int length);
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif // !DISABLE_NUKED_OPL
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user