mirror of
https://github.com/QB64-Phoenix-Edition/QB64pe.git
synced 2024-09-20 03:14:45 +00:00
Merge branch 'main' into main
This commit is contained in:
commit
31bfc191e8
5 changed files with 282 additions and 194 deletions
|
@ -13,7 +13,7 @@ MINIAUDIO_REAL_OBJS := $(patsubst %.cpp,$(PATH_INTERNAL_C)/parts/audio/%.o,$(MIN
|
||||||
MINIAUDIO_STUB_OBJS := $(patsubst %.cpp,$(PATH_INTERNAL_C)/parts/audio/%.o,$(MINIAUDIO_STUB_SRCS))
|
MINIAUDIO_STUB_OBJS := $(patsubst %.cpp,$(PATH_INTERNAL_C)/parts/audio/%.o,$(MINIAUDIO_STUB_SRCS))
|
||||||
|
|
||||||
ifdef DEP_AUDIO_MINIAUDIO
|
ifdef DEP_AUDIO_MINIAUDIO
|
||||||
MINIAUDIO_OBJS := $(MINIAUDIO_REAL_OBJS) $(MA_VTABLES_OBJS) $(HIVELY_OBJS) $(LIBXMP_LIB)
|
MINIAUDIO_OBJS := $(MINIAUDIO_REAL_OBJS) $(MA_VTABLES_OBJS) $(HIVELY_OBJS) $(OPAL_OBJS) $(LIBXMP_LIB)
|
||||||
|
|
||||||
ifdef DEP_AUDIO_DECODE_MIDI
|
ifdef DEP_AUDIO_DECODE_MIDI
|
||||||
MINIAUDIO_OBJS += $(MIDI_MA_VTABLES_OBJS)
|
MINIAUDIO_OBJS += $(MIDI_MA_VTABLES_OBJS)
|
||||||
|
|
|
@ -44,6 +44,10 @@ HIVELY_SRCS := hvl_replay.c
|
||||||
|
|
||||||
HIVELY_OBJS += $(patsubst %.c,$(PATH_INTERNAL_C)/parts/audio/extras/hivelytracker/%.o,$(HIVELY_SRCS))
|
HIVELY_OBJS += $(patsubst %.c,$(PATH_INTERNAL_C)/parts/audio/extras/hivelytracker/%.o,$(HIVELY_SRCS))
|
||||||
|
|
||||||
|
OPAL_SRCS := opal.cpp
|
||||||
|
|
||||||
|
OPAL_OBJS += $(patsubst %.cpp,$(PATH_INTERNAL_C)/parts/audio/extras/radv2/%.o,$(OPAL_SRCS))
|
||||||
|
|
||||||
$(PATH_INTERNAL_C)/parts/audio/extras/hivelytracker/%.o: $(PATH_INTERNAL_C)/parts/audio/extras/hivelytracker/%.c
|
$(PATH_INTERNAL_C)/parts/audio/extras/hivelytracker/%.o: $(PATH_INTERNAL_C)/parts/audio/extras/hivelytracker/%.c
|
||||||
$(CC) -O2 $(CFLAGS) -Wall $< -c -o $@
|
$(CC) -O2 $(CFLAGS) -Wall $< -c -o $@
|
||||||
|
|
||||||
|
@ -88,5 +92,5 @@ $(PATH_INTERNAL_TEMP)/soundfont.o: $(PATH_INTERNAL_TEMP)/soundfont.sf2
|
||||||
endif
|
endif
|
||||||
endif
|
endif
|
||||||
|
|
||||||
CLEAN_LIST += $(LIBXMP_LIB) $(LIBXMP_OBJS) $(HIVELY_OBJS) $(MA_VTABLES_OBJS) $(MIDI_MA_VTABLES_OBJS) $(MIDI_MA_VTABLES_STUB_OBJS)
|
CLEAN_LIST += $(LIBXMP_LIB) $(LIBXMP_OBJS) $(HIVELY_OBJS) $(OPAL_OBJS) $(MA_VTABLES_OBJS) $(MIDI_MA_VTABLES_OBJS) $(MIDI_MA_VTABLES_STUB_OBJS)
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,3 @@
|
||||||
// This is the Opal OPL3 emulator from Reality Adlib Tracker v2.0a (http://www.3eality.com/productions/reality-adlib-tracker).
|
|
||||||
// It was released by Shayde/Reality into the public domain.
|
|
||||||
// Minor modifications to silence some warnings and fix a bug in the envelope generator have been applied.
|
|
||||||
// Additional fixes by JP Cimalando.
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
||||||
The Opal OPL3 emulator.
|
The Opal OPL3 emulator.
|
||||||
|
@ -19,183 +14,14 @@
|
||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <cstdint>
|
// This is the Opal OPL3 emulator from Reality Adlib Tracker v2.0a (http://www.3eality.com/productions/reality-adlib-tracker).
|
||||||
|
// It was released by Shayde/Reality into the public domain.
|
||||||
|
// Minor modifications to silence some warnings and fix a bug in the envelope generator have been applied.
|
||||||
|
// Additional fixes by JP Cimalando.
|
||||||
|
// Soft panning support by Wohlstand.
|
||||||
|
|
||||||
//==================================================================================================
|
#include "opal.h"
|
||||||
// Opal class.
|
|
||||||
//==================================================================================================
|
|
||||||
class Opal {
|
|
||||||
|
|
||||||
class Channel;
|
|
||||||
|
|
||||||
// Various constants
|
|
||||||
enum {
|
|
||||||
OPL3SampleRate = 49716,
|
|
||||||
NumChannels = 18,
|
|
||||||
NumOperators = 36,
|
|
||||||
|
|
||||||
EnvOff = -1,
|
|
||||||
EnvAtt,
|
|
||||||
EnvDec,
|
|
||||||
EnvSus,
|
|
||||||
EnvRel
|
|
||||||
};
|
|
||||||
|
|
||||||
// A single FM operator
|
|
||||||
class Operator {
|
|
||||||
|
|
||||||
public:
|
|
||||||
Operator();
|
|
||||||
void SetMaster(Opal *opal) { Master = opal; }
|
|
||||||
void SetChannel(Channel *chan) { Chan = chan; }
|
|
||||||
|
|
||||||
int16_t Output(uint16_t keyscalenum, uint32_t phase_step, int16_t vibrato, int16_t mod = 0, int16_t fbshift = 0);
|
|
||||||
|
|
||||||
void SetKeyOn(bool on);
|
|
||||||
void SetTremoloEnable(bool on);
|
|
||||||
void SetVibratoEnable(bool on);
|
|
||||||
void SetSustainMode(bool on);
|
|
||||||
void SetEnvelopeScaling(bool on);
|
|
||||||
void SetFrequencyMultiplier(uint16_t scale);
|
|
||||||
void SetKeyScale(uint16_t scale);
|
|
||||||
void SetOutputLevel(uint16_t level);
|
|
||||||
void SetAttackRate(uint16_t rate);
|
|
||||||
void SetDecayRate(uint16_t rate);
|
|
||||||
void SetSustainLevel(uint16_t level);
|
|
||||||
void SetReleaseRate(uint16_t rate);
|
|
||||||
void SetWaveform(uint16_t wave);
|
|
||||||
|
|
||||||
void ComputeRates();
|
|
||||||
void ComputeKeyScaleLevel();
|
|
||||||
|
|
||||||
protected:
|
|
||||||
Opal *Master; // Master object
|
|
||||||
Channel *Chan; // Owning channel
|
|
||||||
uint32_t Phase; // The current offset in the selected waveform
|
|
||||||
uint16_t Waveform; // The waveform id this operator is using
|
|
||||||
uint16_t FreqMultTimes2; // Frequency multiplier * 2
|
|
||||||
int EnvelopeStage; // Which stage the envelope is at (see Env* enums above)
|
|
||||||
int16_t EnvelopeLevel; // 0 - $1FF, 0 being the loudest
|
|
||||||
uint16_t OutputLevel; // 0 - $FF
|
|
||||||
uint16_t AttackRate;
|
|
||||||
uint16_t DecayRate;
|
|
||||||
uint16_t SustainLevel;
|
|
||||||
uint16_t ReleaseRate;
|
|
||||||
uint16_t AttackShift;
|
|
||||||
uint16_t AttackMask;
|
|
||||||
uint16_t AttackAdd;
|
|
||||||
const uint16_t *AttackTab;
|
|
||||||
uint16_t DecayShift;
|
|
||||||
uint16_t DecayMask;
|
|
||||||
uint16_t DecayAdd;
|
|
||||||
const uint16_t *DecayTab;
|
|
||||||
uint16_t ReleaseShift;
|
|
||||||
uint16_t ReleaseMask;
|
|
||||||
uint16_t ReleaseAdd;
|
|
||||||
const uint16_t *ReleaseTab;
|
|
||||||
uint16_t KeyScaleShift;
|
|
||||||
uint16_t KeyScaleLevel;
|
|
||||||
int16_t Out[2];
|
|
||||||
bool KeyOn;
|
|
||||||
bool KeyScaleRate; // Affects envelope rate scaling
|
|
||||||
bool SustainMode; // Whether to sustain during the sustain phase, or release instead
|
|
||||||
bool TremoloEnable;
|
|
||||||
bool VibratoEnable;
|
|
||||||
};
|
|
||||||
|
|
||||||
// A single channel, which can contain two or more operators
|
|
||||||
class Channel {
|
|
||||||
|
|
||||||
public:
|
|
||||||
Channel();
|
|
||||||
void SetMaster(Opal *opal) { Master = opal; }
|
|
||||||
void SetOperators(Operator *a, Operator *b, Operator *c, Operator *d) {
|
|
||||||
Op[0] = a;
|
|
||||||
Op[1] = b;
|
|
||||||
Op[2] = c;
|
|
||||||
Op[3] = d;
|
|
||||||
if (a)
|
|
||||||
a->SetChannel(this);
|
|
||||||
if (b)
|
|
||||||
b->SetChannel(this);
|
|
||||||
if (c)
|
|
||||||
c->SetChannel(this);
|
|
||||||
if (d)
|
|
||||||
d->SetChannel(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Output(int16_t &left, int16_t &right);
|
|
||||||
void SetEnable(bool on) { Enable = on; }
|
|
||||||
void SetChannelPair(Channel *pair) { ChannelPair = pair; }
|
|
||||||
|
|
||||||
void SetFrequencyLow(uint16_t freq);
|
|
||||||
void SetFrequencyHigh(uint16_t freq);
|
|
||||||
void SetKeyOn(bool on);
|
|
||||||
void SetOctave(uint16_t oct);
|
|
||||||
void SetLeftEnable(bool on);
|
|
||||||
void SetRightEnable(bool on);
|
|
||||||
void SetFeedback(uint16_t val);
|
|
||||||
void SetModulationType(uint16_t type);
|
|
||||||
|
|
||||||
uint16_t GetFreq() const { return Freq; }
|
|
||||||
uint16_t GetOctave() const { return Octave; }
|
|
||||||
uint16_t GetKeyScaleNumber() const { return KeyScaleNumber; }
|
|
||||||
uint16_t GetModulationType() const { return ModulationType; }
|
|
||||||
Channel *GetChannelPair() const { return ChannelPair; }
|
|
||||||
|
|
||||||
void ComputeKeyScaleNumber();
|
|
||||||
|
|
||||||
protected:
|
|
||||||
void ComputePhaseStep();
|
|
||||||
|
|
||||||
Operator *Op[4];
|
|
||||||
|
|
||||||
Opal *Master; // Master object
|
|
||||||
uint16_t Freq; // Frequency; actually it's a phase stepping value
|
|
||||||
uint16_t Octave; // Also known as "block" in Yamaha parlance
|
|
||||||
uint32_t PhaseStep;
|
|
||||||
uint16_t KeyScaleNumber;
|
|
||||||
uint16_t FeedbackShift;
|
|
||||||
uint16_t ModulationType;
|
|
||||||
Channel *ChannelPair;
|
|
||||||
bool Enable;
|
|
||||||
bool LeftEnable, RightEnable;
|
|
||||||
};
|
|
||||||
|
|
||||||
public:
|
|
||||||
Opal(int sample_rate);
|
|
||||||
Opal(const Opal &) = delete;
|
|
||||||
Opal(Opal &&) = delete;
|
|
||||||
~Opal();
|
|
||||||
|
|
||||||
void SetSampleRate(int sample_rate);
|
|
||||||
void Port(uint16_t reg_num, uint8_t val);
|
|
||||||
void Sample(int16_t *left, int16_t *right);
|
|
||||||
|
|
||||||
protected:
|
|
||||||
void Init(int sample_rate);
|
|
||||||
void Output(int16_t &left, int16_t &right);
|
|
||||||
|
|
||||||
int32_t SampleRate;
|
|
||||||
int32_t SampleAccum;
|
|
||||||
int16_t LastOutput[2], CurrOutput[2];
|
|
||||||
Channel Chan[NumChannels];
|
|
||||||
Operator Op[NumOperators];
|
|
||||||
// uint16_t ExpTable[256];
|
|
||||||
// uint16_t LogSinTable[256];
|
|
||||||
uint16_t Clock;
|
|
||||||
uint16_t TremoloClock;
|
|
||||||
uint16_t TremoloLevel;
|
|
||||||
uint16_t VibratoTick;
|
|
||||||
uint16_t VibratoClock;
|
|
||||||
bool NoteSel;
|
|
||||||
bool TremoloDepth;
|
|
||||||
bool VibratoDepth;
|
|
||||||
|
|
||||||
static const uint16_t RateTables[4][8];
|
|
||||||
static const uint16_t ExpTable[256];
|
|
||||||
static const uint16_t LogSinTable[256];
|
|
||||||
};
|
|
||||||
// clang-format off
|
// clang-format off
|
||||||
//--------------------------------------------------------------------------------------------------
|
//--------------------------------------------------------------------------------------------------
|
||||||
const uint16_t Opal::RateTables[4][8] = {
|
const uint16_t Opal::RateTables[4][8] = {
|
||||||
|
@ -242,6 +68,28 @@ const uint16_t Opal::LogSinTable[0x100] = {
|
||||||
7, 7, 6, 6, 5, 5, 5, 4, 4, 4, 3, 3, 3, 2, 2, 2,
|
7, 7, 6, 6, 5, 5, 5, 4, 4, 4, 3, 3, 3, 2, 2, 2,
|
||||||
2, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0,
|
2, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
};
|
};
|
||||||
|
//--------------------------------------------------------------------------------------------------
|
||||||
|
const uint16_t Opal::PanLawTable[128] =
|
||||||
|
{
|
||||||
|
65535, 65529, 65514, 65489, 65454, 65409, 65354, 65289,
|
||||||
|
65214, 65129, 65034, 64929, 64814, 64689, 64554, 64410,
|
||||||
|
64255, 64091, 63917, 63733, 63540, 63336, 63123, 62901,
|
||||||
|
62668, 62426, 62175, 61914, 61644, 61364, 61075, 60776,
|
||||||
|
60468, 60151, 59825, 59489, 59145, 58791, 58428, 58057,
|
||||||
|
57676, 57287, 56889, 56482, 56067, 55643, 55211, 54770,
|
||||||
|
54320, 53863, 53397, 52923, 52441, 51951, 51453, 50947,
|
||||||
|
50433, 49912, 49383, 48846, 48302, 47750, 47191,
|
||||||
|
46340, // Center left
|
||||||
|
46340, // Center right
|
||||||
|
45472, 44885, 44291, 43690, 43083, 42469, 41848, 41221,
|
||||||
|
40588, 39948, 39303, 38651, 37994, 37330, 36661, 35986,
|
||||||
|
35306, 34621, 33930, 33234, 32533, 31827, 31116, 30400,
|
||||||
|
29680, 28955, 28225, 27492, 26754, 26012, 25266, 24516,
|
||||||
|
23762, 23005, 22244, 21480, 20713, 19942, 19169, 18392,
|
||||||
|
17613, 16831, 16046, 15259, 14469, 13678, 12884, 12088,
|
||||||
|
11291, 10492, 9691, 8888, 8085, 7280, 6473, 5666,
|
||||||
|
4858, 4050, 3240, 2431, 1620, 810, 0
|
||||||
|
};
|
||||||
// clang-format on
|
// clang-format on
|
||||||
|
|
||||||
//==================================================================================================
|
//==================================================================================================
|
||||||
|
@ -339,6 +187,13 @@ void Opal::Init(int sample_rate) {
|
||||||
for (int i = 0; i < NumOperators; i++)
|
for (int i = 0; i < NumOperators; i++)
|
||||||
Op[i].ComputeRates();
|
Op[i].ComputeRates();
|
||||||
|
|
||||||
|
// Initialise channel panning at center.
|
||||||
|
for (int i = 0; i < NumChannels; i++) {
|
||||||
|
Chan[i].SetPan(64);
|
||||||
|
Chan[i].SetLeftEnable(true);
|
||||||
|
Chan[i].SetRightEnable(true);
|
||||||
|
}
|
||||||
|
|
||||||
SetSampleRate(sample_rate);
|
SetSampleRate(sample_rate);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -392,7 +247,11 @@ void Opal::Port(uint16_t reg_num, uint8_t val) {
|
||||||
|
|
||||||
// The 4-op channels are 0, 1, 2, 9, 10, 11
|
// The 4-op channels are 0, 1, 2, 9, 10, 11
|
||||||
uint16_t chan = static_cast<uint16_t>(i < 3 ? i : i + 6);
|
uint16_t chan = static_cast<uint16_t>(i < 3 ? i : i + 6);
|
||||||
|
// cppcheck false-positive
|
||||||
|
// cppcheck-suppress arrayIndexOutOfBounds
|
||||||
Channel *primary = &Chan[chan];
|
Channel *primary = &Chan[chan];
|
||||||
|
// cppcheck false-positive
|
||||||
|
// cppcheck-suppress arrayIndexOutOfBounds
|
||||||
Channel *secondary = &Chan[chan + 3];
|
Channel *secondary = &Chan[chan + 3];
|
||||||
|
|
||||||
if (val & mask) {
|
if (val & mask) {
|
||||||
|
@ -439,7 +298,7 @@ void Opal::Port(uint16_t reg_num, uint8_t val) {
|
||||||
|
|
||||||
Channel &chan = Chan[chan_num];
|
Channel &chan = Chan[chan_num];
|
||||||
|
|
||||||
// Registers Ax and Bx affect both channels
|
// Registers Ax and Cx affect both channels
|
||||||
Channel *chans[2] = {&chan, chan.GetChannelPair()};
|
Channel *chans[2] = {&chan, chan.GetChannelPair()};
|
||||||
int numchans = chans[1] ? 2 : 1;
|
int numchans = chans[1] ? 2 : 1;
|
||||||
|
|
||||||
|
@ -533,6 +392,16 @@ void Opal::Port(uint16_t reg_num, uint8_t val) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//==================================================================================================
|
||||||
|
// Set panning on the channel designated by the register number.
|
||||||
|
// This is extended functionality.
|
||||||
|
//==================================================================================================
|
||||||
|
void Opal::Pan(uint16_t reg_num, uint8_t pan) {
|
||||||
|
uint8_t high = (reg_num >> 8) & 1;
|
||||||
|
uint8_t regm = reg_num & 0xff;
|
||||||
|
Chan[9 * high + (regm & 0x0f)].SetPan(pan);
|
||||||
|
}
|
||||||
|
|
||||||
//==================================================================================================
|
//==================================================================================================
|
||||||
// Generate sample. Every time you call this you will get two signed 16-bit samples (one for each
|
// Generate sample. Every time you call this you will get two signed 16-bit samples (one for each
|
||||||
// stereo channel) which will sound correct when played back at the sample rate given when the
|
// stereo channel) which will sound correct when played back at the sample rate given when the
|
||||||
|
@ -553,8 +422,8 @@ void Opal::Sample(int16_t *left, int16_t *right) {
|
||||||
|
|
||||||
// Mix with the partial accumulation
|
// Mix with the partial accumulation
|
||||||
int32_t omblend = SampleRate - SampleAccum;
|
int32_t omblend = SampleRate - SampleAccum;
|
||||||
*left = static_cast<uint16_t>((LastOutput[0] * omblend + CurrOutput[0] * SampleAccum) / SampleRate);
|
*left = static_cast<int16_t>((LastOutput[0] * omblend + CurrOutput[0] * SampleAccum) / SampleRate);
|
||||||
*right = static_cast<uint16_t>((LastOutput[1] * omblend + CurrOutput[1] * SampleAccum) / SampleRate);
|
*right = static_cast<int16_t>((LastOutput[1] * omblend + CurrOutput[1] * SampleAccum) / SampleRate);
|
||||||
|
|
||||||
SampleAccum += OPL3SampleRate;
|
SampleAccum += OPL3SampleRate;
|
||||||
}
|
}
|
||||||
|
@ -582,14 +451,14 @@ void Opal::Output(int16_t &left, int16_t &right) {
|
||||||
else if (leftmix > 0x7FFF)
|
else if (leftmix > 0x7FFF)
|
||||||
left = 0x7FFF;
|
left = 0x7FFF;
|
||||||
else
|
else
|
||||||
left = static_cast<uint16_t>(leftmix);
|
left = static_cast<int16_t>(leftmix);
|
||||||
|
|
||||||
if (rightmix < -0x8000)
|
if (rightmix < -0x8000)
|
||||||
right = -0x8000;
|
right = -0x8000;
|
||||||
else if (rightmix > 0x7FFF)
|
else if (rightmix > 0x7FFF)
|
||||||
right = 0x7FFF;
|
right = 0x7FFF;
|
||||||
else
|
else
|
||||||
right = static_cast<uint16_t>(rightmix);
|
right = static_cast<int16_t>(rightmix);
|
||||||
|
|
||||||
Clock++;
|
Clock++;
|
||||||
|
|
||||||
|
@ -725,6 +594,9 @@ void Opal::Channel::Output(int16_t &left, int16_t &right) {
|
||||||
|
|
||||||
left = LeftEnable ? out : 0;
|
left = LeftEnable ? out : 0;
|
||||||
right = RightEnable ? out : 0;
|
right = RightEnable ? out : 0;
|
||||||
|
|
||||||
|
left = left * LeftPan / 65536;
|
||||||
|
right = right * RightPan / 65536;
|
||||||
}
|
}
|
||||||
|
|
||||||
//==================================================================================================
|
//==================================================================================================
|
||||||
|
@ -774,6 +646,15 @@ void Opal::Channel::SetLeftEnable(bool on) { LeftEnable = on; }
|
||||||
//==================================================================================================
|
//==================================================================================================
|
||||||
void Opal::Channel::SetRightEnable(bool on) { RightEnable = on; }
|
void Opal::Channel::SetRightEnable(bool on) { RightEnable = on; }
|
||||||
|
|
||||||
|
//==================================================================================================
|
||||||
|
// Pan the channel to the position given.
|
||||||
|
//==================================================================================================
|
||||||
|
void Opal::Channel::SetPan(uint8_t pan) {
|
||||||
|
pan &= 127;
|
||||||
|
LeftPan = PanLawTable[pan];
|
||||||
|
RightPan = PanLawTable[127 - pan];
|
||||||
|
}
|
||||||
|
|
||||||
//==================================================================================================
|
//==================================================================================================
|
||||||
// Set the channel feedback amount.
|
// Set the channel feedback amount.
|
||||||
//==================================================================================================
|
//==================================================================================================
|
||||||
|
@ -1117,8 +998,8 @@ void Opal::Operator::SetFrequencyMultiplier(uint16_t scale) {
|
||||||
//==================================================================================================
|
//==================================================================================================
|
||||||
void Opal::Operator::SetKeyScale(uint16_t scale) {
|
void Opal::Operator::SetKeyScale(uint16_t scale) {
|
||||||
|
|
||||||
static constexpr uint8_t kslShift[4] = {8, 1, 2, 0};
|
static const uint8_t KeyScaleShiftTable[4] = {8, 1, 2, 0};
|
||||||
KeyScaleShift = kslShift[scale];
|
KeyScaleShift = KeyScaleShiftTable[scale & 3];
|
||||||
ComputeKeyScaleLevel();
|
ComputeKeyScaleLevel();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
205
internal/c/parts/audio/extras/radv2/opal.h
Normal file
205
internal/c/parts/audio/extras/radv2/opal.h
Normal file
|
@ -0,0 +1,205 @@
|
||||||
|
/*
|
||||||
|
|
||||||
|
The Opal OPL3 emulator (header).
|
||||||
|
|
||||||
|
Note: this is not a complete emulator, just enough for Reality Adlib Tracker tunes.
|
||||||
|
|
||||||
|
Missing features compared to a real OPL3:
|
||||||
|
|
||||||
|
- Timers/interrupts
|
||||||
|
- OPL3 enable bit (it defaults to always on)
|
||||||
|
- CSW mode
|
||||||
|
- Test register
|
||||||
|
- Percussion mode
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
// This is the Opal OPL3 emulator from Reality Adlib Tracker v2.0a (http://www.3eality.com/productions/reality-adlib-tracker).
|
||||||
|
// It was released by Shayde/Reality into the public domain.
|
||||||
|
// Minor modifications to silence some warnings and fix a bug in the envelope generator have been applied.
|
||||||
|
// Additional fixes by JP Cimalando.
|
||||||
|
// Soft panning support by Wohlstand.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
//==================================================================================================
|
||||||
|
// Opal class.
|
||||||
|
//==================================================================================================
|
||||||
|
class Opal {
|
||||||
|
|
||||||
|
class Channel;
|
||||||
|
|
||||||
|
// Various constants
|
||||||
|
enum {
|
||||||
|
OPL3SampleRate = 49716,
|
||||||
|
NumChannels = 18,
|
||||||
|
NumOperators = 36,
|
||||||
|
|
||||||
|
EnvOff = -1,
|
||||||
|
EnvAtt,
|
||||||
|
EnvDec,
|
||||||
|
EnvSus,
|
||||||
|
EnvRel
|
||||||
|
};
|
||||||
|
|
||||||
|
// A single FM operator
|
||||||
|
class Operator {
|
||||||
|
|
||||||
|
public:
|
||||||
|
Operator();
|
||||||
|
void SetMaster(Opal *opal) { Master = opal; }
|
||||||
|
void SetChannel(Channel *chan) { Chan = chan; }
|
||||||
|
|
||||||
|
int16_t Output(uint16_t keyscalenum, uint32_t phase_step, int16_t vibrato, int16_t mod = 0, int16_t fbshift = 0);
|
||||||
|
|
||||||
|
void SetKeyOn(bool on);
|
||||||
|
void SetTremoloEnable(bool on);
|
||||||
|
void SetVibratoEnable(bool on);
|
||||||
|
void SetSustainMode(bool on);
|
||||||
|
void SetEnvelopeScaling(bool on);
|
||||||
|
void SetFrequencyMultiplier(uint16_t scale);
|
||||||
|
void SetKeyScale(uint16_t scale);
|
||||||
|
void SetOutputLevel(uint16_t level);
|
||||||
|
void SetAttackRate(uint16_t rate);
|
||||||
|
void SetDecayRate(uint16_t rate);
|
||||||
|
void SetSustainLevel(uint16_t level);
|
||||||
|
void SetReleaseRate(uint16_t rate);
|
||||||
|
void SetWaveform(uint16_t wave);
|
||||||
|
|
||||||
|
void ComputeRates();
|
||||||
|
void ComputeKeyScaleLevel();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
Opal *Master; // Master object
|
||||||
|
Channel *Chan; // Owning channel
|
||||||
|
uint32_t Phase; // The current offset in the selected waveform
|
||||||
|
uint16_t Waveform; // The waveform id this operator is using
|
||||||
|
uint16_t FreqMultTimes2; // Frequency multiplier * 2
|
||||||
|
int EnvelopeStage; // Which stage the envelope is at (see Env* enums above)
|
||||||
|
int16_t EnvelopeLevel; // 0 - $1FF, 0 being the loudest
|
||||||
|
uint16_t OutputLevel; // 0 - $FF
|
||||||
|
uint16_t AttackRate;
|
||||||
|
uint16_t DecayRate;
|
||||||
|
uint16_t SustainLevel;
|
||||||
|
uint16_t ReleaseRate;
|
||||||
|
uint16_t AttackShift;
|
||||||
|
uint16_t AttackMask;
|
||||||
|
uint16_t AttackAdd;
|
||||||
|
const uint16_t *AttackTab;
|
||||||
|
uint16_t DecayShift;
|
||||||
|
uint16_t DecayMask;
|
||||||
|
uint16_t DecayAdd;
|
||||||
|
const uint16_t *DecayTab;
|
||||||
|
uint16_t ReleaseShift;
|
||||||
|
uint16_t ReleaseMask;
|
||||||
|
uint16_t ReleaseAdd;
|
||||||
|
const uint16_t *ReleaseTab;
|
||||||
|
uint16_t KeyScaleShift;
|
||||||
|
uint16_t KeyScaleLevel;
|
||||||
|
int16_t Out[2];
|
||||||
|
bool KeyOn;
|
||||||
|
bool KeyScaleRate; // Affects envelope rate scaling
|
||||||
|
bool SustainMode; // Whether to sustain during the sustain phase, or release instead
|
||||||
|
bool TremoloEnable;
|
||||||
|
bool VibratoEnable;
|
||||||
|
};
|
||||||
|
|
||||||
|
// A single channel, which can contain two or more operators
|
||||||
|
class Channel {
|
||||||
|
|
||||||
|
public:
|
||||||
|
Channel();
|
||||||
|
void SetMaster(Opal *opal) { Master = opal; }
|
||||||
|
void SetOperators(Operator *a, Operator *b, Operator *c, Operator *d) {
|
||||||
|
Op[0] = a;
|
||||||
|
Op[1] = b;
|
||||||
|
Op[2] = c;
|
||||||
|
Op[3] = d;
|
||||||
|
if (a)
|
||||||
|
a->SetChannel(this);
|
||||||
|
if (b)
|
||||||
|
b->SetChannel(this);
|
||||||
|
if (c)
|
||||||
|
c->SetChannel(this);
|
||||||
|
if (d)
|
||||||
|
d->SetChannel(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Output(int16_t &left, int16_t &right);
|
||||||
|
void SetEnable(bool on) { Enable = on; }
|
||||||
|
void SetChannelPair(Channel *pair) { ChannelPair = pair; }
|
||||||
|
|
||||||
|
void SetFrequencyLow(uint16_t freq);
|
||||||
|
void SetFrequencyHigh(uint16_t freq);
|
||||||
|
void SetKeyOn(bool on);
|
||||||
|
void SetOctave(uint16_t oct);
|
||||||
|
void SetLeftEnable(bool on);
|
||||||
|
void SetRightEnable(bool on);
|
||||||
|
void SetPan(uint8_t pan);
|
||||||
|
void SetFeedback(uint16_t val);
|
||||||
|
void SetModulationType(uint16_t type);
|
||||||
|
|
||||||
|
uint16_t GetFreq() const { return Freq; }
|
||||||
|
uint16_t GetOctave() const { return Octave; }
|
||||||
|
uint16_t GetKeyScaleNumber() const { return KeyScaleNumber; }
|
||||||
|
uint16_t GetModulationType() const { return ModulationType; }
|
||||||
|
Channel *GetChannelPair() const { return ChannelPair; }
|
||||||
|
|
||||||
|
void ComputeKeyScaleNumber();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void ComputePhaseStep();
|
||||||
|
|
||||||
|
Operator *Op[4];
|
||||||
|
|
||||||
|
Opal *Master; // Master object
|
||||||
|
uint16_t Freq; // Frequency; actually it's a phase stepping value
|
||||||
|
uint16_t Octave; // Also known as "block" in Yamaha parlance
|
||||||
|
uint32_t PhaseStep;
|
||||||
|
uint16_t KeyScaleNumber;
|
||||||
|
uint16_t FeedbackShift;
|
||||||
|
uint16_t ModulationType;
|
||||||
|
Channel *ChannelPair;
|
||||||
|
bool Enable;
|
||||||
|
bool LeftEnable, RightEnable;
|
||||||
|
uint16_t LeftPan, RightPan;
|
||||||
|
};
|
||||||
|
|
||||||
|
public:
|
||||||
|
Opal(int sample_rate);
|
||||||
|
Opal(const Opal &) = delete;
|
||||||
|
Opal(Opal &&) = delete;
|
||||||
|
~Opal();
|
||||||
|
|
||||||
|
void SetSampleRate(int sample_rate);
|
||||||
|
void Port(uint16_t reg_num, uint8_t val);
|
||||||
|
void Pan(uint16_t reg_num, uint8_t pan);
|
||||||
|
void Sample(int16_t *left, int16_t *right);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void Init(int sample_rate);
|
||||||
|
void Output(int16_t &left, int16_t &right);
|
||||||
|
|
||||||
|
int32_t SampleRate;
|
||||||
|
int32_t SampleAccum;
|
||||||
|
int16_t LastOutput[2], CurrOutput[2];
|
||||||
|
Channel Chan[NumChannels];
|
||||||
|
Operator Op[NumOperators];
|
||||||
|
// uint16_t ExpTable[256];
|
||||||
|
// uint16_t LogSinTable[256];
|
||||||
|
uint16_t Clock;
|
||||||
|
uint16_t TremoloClock;
|
||||||
|
uint16_t TremoloLevel;
|
||||||
|
uint16_t VibratoTick;
|
||||||
|
uint16_t VibratoClock;
|
||||||
|
bool NoteSel;
|
||||||
|
bool TremoloDepth;
|
||||||
|
bool VibratoDepth;
|
||||||
|
|
||||||
|
static const uint16_t RateTables[4][8];
|
||||||
|
static const uint16_t ExpTable[256];
|
||||||
|
static const uint16_t LogSinTable[256];
|
||||||
|
static const uint16_t PanLawTable[128];
|
||||||
|
};
|
|
@ -19,7 +19,7 @@
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
#include "radv2/opal.cpp"
|
#include "radv2/opal.h"
|
||||||
#define RAD_DETECT_REPEATS 1
|
#define RAD_DETECT_REPEATS 1
|
||||||
#include "radv2/player20.cpp"
|
#include "radv2/player20.cpp"
|
||||||
#include "radv2/validate20.cpp"
|
#include "radv2/validate20.cpp"
|
||||||
|
@ -372,8 +372,7 @@ static ma_result ma_radv2_init(ma_read_proc onRead, ma_seek_proc onSeek, ma_tell
|
||||||
|
|
||||||
// Initialize the player
|
// Initialize the player
|
||||||
// We'll use a lambda here and pass the pRadv2 pointer using 'arg'
|
// We'll use a lambda here and pass the pRadv2 pointer using 'arg'
|
||||||
pRadv2->player->Init(
|
pRadv2->player->Init(pRadv2->tune, [](void *arg, uint16_t reg, uint8_t data) { ((ma_radv2 *)arg)->adlib->Port(reg, data); }, pRadv2);
|
||||||
pRadv2->tune, [](void *arg, uint16_t reg, uint8_t data) { ((ma_radv2 *)arg)->adlib->Port(reg, data); }, pRadv2);
|
|
||||||
|
|
||||||
// Get the playback rate
|
// Get the playback rate
|
||||||
if (pRadv2->player->GetHertz() < 0) {
|
if (pRadv2->player->GetHertz() < 0) {
|
||||||
|
@ -485,8 +484,7 @@ static ma_result ma_radv2_init_file(const char *pFilePath, const ma_decoding_bac
|
||||||
|
|
||||||
// Initialize the player
|
// Initialize the player
|
||||||
// We'll use a lambda here and pass the pRadv2 pointer using 'arg'
|
// We'll use a lambda here and pass the pRadv2 pointer using 'arg'
|
||||||
pRadv2->player->Init(
|
pRadv2->player->Init(pRadv2->tune, [](void *arg, uint16_t reg, uint8_t data) { ((ma_radv2 *)arg)->adlib->Port(reg, data); }, pRadv2);
|
||||||
pRadv2->tune, [](void *arg, uint16_t reg, uint8_t data) { ((ma_radv2 *)arg)->adlib->Port(reg, data); }, pRadv2);
|
|
||||||
|
|
||||||
// Get the playback rate
|
// Get the playback rate
|
||||||
if (pRadv2->player->GetHertz() < 0) {
|
if (pRadv2->player->GetHertz() < 0) {
|
||||||
|
|
Loading…
Reference in a new issue