mirror of
https://github.com/QB64-Phoenix-Edition/QB64pe.git
synced 2024-09-16 15:34:03 +00:00
Merge pull request #329 from a740g/audio-fixes-improvements
RAD v1 support
This commit is contained in:
commit
3046d97845
4 changed files with 1547 additions and 1080 deletions
|
@ -1,3 +1,8 @@
|
||||||
|
// 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.
|
||||||
|
@ -14,11 +19,7 @@
|
||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
#include <stdint.h>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//==================================================================================================
|
//==================================================================================================
|
||||||
// Opal class.
|
// Opal class.
|
||||||
|
@ -140,6 +141,7 @@ class Opal {
|
||||||
uint16_t GetOctave() const { return Octave; }
|
uint16_t GetOctave() const { return Octave; }
|
||||||
uint16_t GetKeyScaleNumber() const { return KeyScaleNumber; }
|
uint16_t GetKeyScaleNumber() const { return KeyScaleNumber; }
|
||||||
uint16_t GetModulationType() const { return ModulationType; }
|
uint16_t GetModulationType() const { return ModulationType; }
|
||||||
|
Channel *GetChannelPair() const { return ChannelPair; }
|
||||||
|
|
||||||
void ComputeKeyScaleNumber();
|
void ComputeKeyScaleNumber();
|
||||||
|
|
||||||
|
@ -162,6 +164,8 @@ class Opal {
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Opal(int sample_rate);
|
Opal(int sample_rate);
|
||||||
|
Opal(const Opal &) = delete;
|
||||||
|
Opal(Opal &&) = delete;
|
||||||
~Opal();
|
~Opal();
|
||||||
|
|
||||||
void SetSampleRate(int sample_rate);
|
void SetSampleRate(int sample_rate);
|
||||||
|
@ -192,6 +196,7 @@ class Opal {
|
||||||
static const uint16_t ExpTable[256];
|
static const uint16_t ExpTable[256];
|
||||||
static const uint16_t LogSinTable[256];
|
static const uint16_t LogSinTable[256];
|
||||||
};
|
};
|
||||||
|
// clang-format off
|
||||||
//--------------------------------------------------------------------------------------------------
|
//--------------------------------------------------------------------------------------------------
|
||||||
const uint16_t Opal::RateTables[4][8] = {
|
const uint16_t Opal::RateTables[4][8] = {
|
||||||
{ 1, 0, 1, 0, 1, 0, 1, 0 },
|
{ 1, 0, 1, 0, 1, 0, 1, 0 },
|
||||||
|
@ -237,8 +242,7 @@ 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,
|
||||||
};
|
};
|
||||||
|
// clang-format on
|
||||||
|
|
||||||
|
|
||||||
//==================================================================================================
|
//==================================================================================================
|
||||||
// This is the temporary code for generating the above tables. Maths and data from this nice
|
// This is the temporary code for generating the above tables. Maths and data from this nice
|
||||||
|
@ -279,20 +283,12 @@ void GenerateTables() {
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//==================================================================================================
|
//==================================================================================================
|
||||||
// Constructor/destructor.
|
// Constructor/destructor.
|
||||||
//==================================================================================================
|
//==================================================================================================
|
||||||
Opal::Opal(int sample_rate) {
|
Opal::Opal(int sample_rate) { Init(sample_rate); }
|
||||||
|
|
||||||
Init(sample_rate);
|
|
||||||
}
|
|
||||||
//--------------------------------------------------------------------------------------------------
|
//--------------------------------------------------------------------------------------------------
|
||||||
Opal::~Opal() {
|
Opal::~Opal() {}
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//==================================================================================================
|
//==================================================================================================
|
||||||
// Initialise the emulation.
|
// Initialise the emulation.
|
||||||
|
@ -301,6 +297,7 @@ void Opal::Init(int sample_rate) {
|
||||||
|
|
||||||
Clock = 0;
|
Clock = 0;
|
||||||
TremoloClock = 0;
|
TremoloClock = 0;
|
||||||
|
TremoloLevel = 0;
|
||||||
VibratoTick = 0;
|
VibratoTick = 0;
|
||||||
VibratoClock = 0;
|
VibratoClock = 0;
|
||||||
NoteSel = false;
|
NoteSel = false;
|
||||||
|
@ -345,8 +342,6 @@ void Opal::Init(int sample_rate) {
|
||||||
SetSampleRate(sample_rate);
|
SetSampleRate(sample_rate);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//==================================================================================================
|
//==================================================================================================
|
||||||
// Change the sample rate.
|
// Change the sample rate.
|
||||||
//==================================================================================================
|
//==================================================================================================
|
||||||
|
@ -362,26 +357,26 @@ void Opal::SetSampleRate(int sample_rate) {
|
||||||
CurrOutput[0] = CurrOutput[1] = 0;
|
CurrOutput[0] = CurrOutput[1] = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//==================================================================================================
|
//==================================================================================================
|
||||||
// Write a value to an OPL3 register.
|
// Write a value to an OPL3 register.
|
||||||
//==================================================================================================
|
//==================================================================================================
|
||||||
void Opal::Port(uint16_t reg_num, uint8_t val) {
|
void Opal::Port(uint16_t reg_num, uint8_t val) {
|
||||||
|
|
||||||
const int op_lookup[] = {
|
// clang-format off
|
||||||
|
static constexpr int8_t op_lookup[] = {
|
||||||
// 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F
|
// 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F
|
||||||
0, 1, 2, 3, 4, 5, -1, -1, 6, 7, 8, 9, 10, 11, -1, -1,
|
0, 1, 2, 3, 4, 5, -1, -1, 6, 7, 8, 9, 10, 11, -1, -1,
|
||||||
// 10 11 12 13 14 15 16 17 18 19 1A 1B 1C 1D 1E 1F
|
// 10 11 12 13 14 15 16 17 18 19 1A 1B 1C 1D 1E 1F
|
||||||
12, 13, 14, 15, 16, 17, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
12, 13, 14, 15, 16, 17, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||||
};
|
};
|
||||||
|
// clang-format on
|
||||||
|
|
||||||
uint16_t type = reg_num & 0xE0;
|
uint16_t type = reg_num & 0xE0;
|
||||||
|
|
||||||
// Is it BD, the one-off register stuck in the middle of the register array?
|
// Is it BD, the one-off register stuck in the middle of the register array?
|
||||||
if (reg_num == 0xBD) {
|
if (reg_num == 0xBD) {
|
||||||
TremoloDepth = (val & 0x80);
|
TremoloDepth = (val & 0x80) != 0;
|
||||||
VibratoDepth = (val & 0x40);
|
VibratoDepth = (val & 0x40) != 0;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -396,7 +391,7 @@ void Opal::Port(uint16_t reg_num, uint8_t val) {
|
||||||
for (int i = 0; i < 6; i++, mask <<= 1) {
|
for (int i = 0; i < 6; i++, mask <<= 1) {
|
||||||
|
|
||||||
// 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 = i < 3 ? i : i + 6;
|
uint16_t chan = static_cast<uint16_t>(i < 3 ? i : i + 6);
|
||||||
Channel *primary = &Chan[chan];
|
Channel *primary = &Chan[chan];
|
||||||
Channel *secondary = &Chan[chan + 3];
|
Channel *secondary = &Chan[chan + 3];
|
||||||
|
|
||||||
|
@ -421,7 +416,7 @@ void Opal::Port(uint16_t reg_num, uint8_t val) {
|
||||||
// CSW / Note-sel
|
// CSW / Note-sel
|
||||||
} else if (reg_num == 0x08) {
|
} else if (reg_num == 0x08) {
|
||||||
|
|
||||||
NoteSel = (val & 0x40);
|
NoteSel = (val & 0x40) != 0;
|
||||||
|
|
||||||
// Get the channels to recompute the Key Scale No. as this varies based on NoteSel
|
// Get the channels to recompute the Key Scale No. as this varies based on NoteSel
|
||||||
for (int i = 0; i < NumChannels; i++)
|
for (int i = 0; i < NumChannels; i++)
|
||||||
|
@ -444,27 +439,35 @@ 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
|
||||||
|
Channel *chans[2] = {&chan, chan.GetChannelPair()};
|
||||||
|
int numchans = chans[1] ? 2 : 1;
|
||||||
|
|
||||||
// Do specific registers
|
// Do specific registers
|
||||||
switch (reg_num & 0xF0) {
|
switch (reg_num & 0xF0) {
|
||||||
|
|
||||||
// Frequency low
|
// Frequency low
|
||||||
case 0xA0: {
|
case 0xA0: {
|
||||||
chan.SetFrequencyLow(val);
|
for (int i = 0; i < numchans; i++) {
|
||||||
|
chans[i]->SetFrequencyLow(val);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Key-on / Octave / Frequency High
|
// Key-on / Octave / Frequency High
|
||||||
case 0xB0: {
|
case 0xB0: {
|
||||||
chan.SetKeyOn(val & 0x20);
|
for (int i = 0; i < numchans; i++) {
|
||||||
chan.SetOctave(val >> 2 & 7);
|
chans[i]->SetKeyOn((val & 0x20) != 0);
|
||||||
chan.SetFrequencyHigh(val & 3);
|
chans[i]->SetOctave(val >> 2 & 7);
|
||||||
|
chans[i]->SetFrequencyHigh(val & 3);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Right Stereo Channel Enable / Left Stereo Channel Enable / Feedback Factor / Modulation Type
|
// Right Stereo Channel Enable / Left Stereo Channel Enable / Feedback Factor / Modulation Type
|
||||||
case 0xC0: {
|
case 0xC0: {
|
||||||
chan.SetRightEnable(val & 0x20);
|
chan.SetRightEnable((val & 0x20) != 0);
|
||||||
chan.SetLeftEnable(val & 0x10);
|
chan.SetLeftEnable((val & 0x10) != 0);
|
||||||
chan.SetFeedback(val >> 1 & 7);
|
chan.SetFeedback(val >> 1 & 7);
|
||||||
chan.SetModulationType(val & 1);
|
chan.SetModulationType(val & 1);
|
||||||
break;
|
break;
|
||||||
|
@ -492,10 +495,10 @@ void Opal::Port(uint16_t reg_num, uint8_t val) {
|
||||||
|
|
||||||
// Tremolo Enable / Vibrato Enable / Sustain Mode / Envelope Scaling / Frequency Multiplier
|
// Tremolo Enable / Vibrato Enable / Sustain Mode / Envelope Scaling / Frequency Multiplier
|
||||||
case 0x20: {
|
case 0x20: {
|
||||||
op.SetTremoloEnable(val & 0x80);
|
op.SetTremoloEnable((val & 0x80) != 0);
|
||||||
op.SetVibratoEnable(val & 0x40);
|
op.SetVibratoEnable((val & 0x40) != 0);
|
||||||
op.SetSustainMode(val & 0x20);
|
op.SetSustainMode((val & 0x20) != 0);
|
||||||
op.SetEnvelopeScaling(val & 0x10);
|
op.SetEnvelopeScaling((val & 0x10) != 0);
|
||||||
op.SetFrequencyMultiplier(val & 15);
|
op.SetFrequencyMultiplier(val & 15);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -530,8 +533,6 @@ void Opal::Port(uint16_t reg_num, uint8_t val) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//==================================================================================================
|
//==================================================================================================
|
||||||
// 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
|
||||||
|
@ -552,14 +553,12 @@ 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 = (LastOutput[0] * omblend + CurrOutput[0] * SampleAccum) / SampleRate;
|
*left = static_cast<uint16_t>((LastOutput[0] * omblend + CurrOutput[0] * SampleAccum) / SampleRate);
|
||||||
*right = (LastOutput[1] * omblend + CurrOutput[1] * SampleAccum) / SampleRate;
|
*right = static_cast<uint16_t>((LastOutput[1] * omblend + CurrOutput[1] * SampleAccum) / SampleRate);
|
||||||
|
|
||||||
SampleAccum += OPL3SampleRate;
|
SampleAccum += OPL3SampleRate;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//==================================================================================================
|
//==================================================================================================
|
||||||
// Produce final output from the chip. This is at the OPL3 sample-rate.
|
// Produce final output from the chip. This is at the OPL3 sample-rate.
|
||||||
//==================================================================================================
|
//==================================================================================================
|
||||||
|
@ -583,14 +582,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 = leftmix;
|
left = static_cast<uint16_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 = rightmix;
|
right = static_cast<uint16_t>(rightmix);
|
||||||
|
|
||||||
Clock++;
|
Clock++;
|
||||||
|
|
||||||
|
@ -612,8 +611,6 @@ void Opal::Output(int16_t &left, int16_t &right) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//==================================================================================================
|
//==================================================================================================
|
||||||
// Channel constructor.
|
// Channel constructor.
|
||||||
//==================================================================================================
|
//==================================================================================================
|
||||||
|
@ -628,10 +625,10 @@ Opal::Channel::Channel() {
|
||||||
ModulationType = 0;
|
ModulationType = 0;
|
||||||
ChannelPair = 0;
|
ChannelPair = 0;
|
||||||
Enable = true;
|
Enable = true;
|
||||||
|
LeftEnable = true;
|
||||||
|
RightEnable = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//==================================================================================================
|
//==================================================================================================
|
||||||
// Produce output from channel.
|
// Produce output from channel.
|
||||||
//==================================================================================================
|
//==================================================================================================
|
||||||
|
@ -655,12 +652,13 @@ void Opal::Channel::Output(int16_t &left, int16_t &right) {
|
||||||
else {
|
else {
|
||||||
if (clk & 1)
|
if (clk & 1)
|
||||||
vibrato >>= 1; // Odd positions are half the magnitude
|
vibrato >>= 1; // Odd positions are half the magnitude
|
||||||
|
|
||||||
|
vibrato <<= Octave;
|
||||||
|
|
||||||
if (clk & 4)
|
if (clk & 4)
|
||||||
vibrato = -vibrato; // The second half positions are negative
|
vibrato = -vibrato; // The second half positions are negative
|
||||||
}
|
}
|
||||||
|
|
||||||
vibrato <<= Octave;
|
|
||||||
|
|
||||||
// Combine individual operator outputs
|
// Combine individual operator outputs
|
||||||
int16_t out, acc;
|
int16_t out, acc;
|
||||||
|
|
||||||
|
@ -729,8 +727,6 @@ void Opal::Channel::Output(int16_t &left, int16_t &right) {
|
||||||
right = RightEnable ? out : 0;
|
right = RightEnable ? out : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//==================================================================================================
|
//==================================================================================================
|
||||||
// Set phase step for operators using this channel.
|
// Set phase step for operators using this channel.
|
||||||
//==================================================================================================
|
//==================================================================================================
|
||||||
|
@ -749,8 +745,6 @@ void Opal::Channel::SetFrequencyHigh(uint16_t freq) {
|
||||||
ComputeKeyScaleNumber();
|
ComputeKeyScaleNumber();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//==================================================================================================
|
//==================================================================================================
|
||||||
// Set the octave of the channel (0 to 7).
|
// Set the octave of the channel (0 to 7).
|
||||||
//==================================================================================================
|
//==================================================================================================
|
||||||
|
@ -761,8 +755,6 @@ void Opal::Channel::SetOctave(uint16_t oct) {
|
||||||
ComputeKeyScaleNumber();
|
ComputeKeyScaleNumber();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//==================================================================================================
|
//==================================================================================================
|
||||||
// Keys the channel on/off.
|
// Keys the channel on/off.
|
||||||
//==================================================================================================
|
//==================================================================================================
|
||||||
|
@ -772,58 +764,31 @@ void Opal::Channel::SetKeyOn(bool on) {
|
||||||
Op[1]->SetKeyOn(on);
|
Op[1]->SetKeyOn(on);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//==================================================================================================
|
//==================================================================================================
|
||||||
// Enable left stereo channel.
|
// Enable left stereo channel.
|
||||||
//==================================================================================================
|
//==================================================================================================
|
||||||
void Opal::Channel::SetLeftEnable(bool on) {
|
void Opal::Channel::SetLeftEnable(bool on) { LeftEnable = on; }
|
||||||
|
|
||||||
LeftEnable = on;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//==================================================================================================
|
//==================================================================================================
|
||||||
// Enable right stereo channel.
|
// Enable right stereo channel.
|
||||||
//==================================================================================================
|
//==================================================================================================
|
||||||
void Opal::Channel::SetRightEnable(bool on) {
|
void Opal::Channel::SetRightEnable(bool on) { RightEnable = on; }
|
||||||
|
|
||||||
RightEnable = on;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//==================================================================================================
|
//==================================================================================================
|
||||||
// Set the channel feedback amount.
|
// Set the channel feedback amount.
|
||||||
//==================================================================================================
|
//==================================================================================================
|
||||||
void Opal::Channel::SetFeedback(uint16_t val) {
|
void Opal::Channel::SetFeedback(uint16_t val) { FeedbackShift = val ? 9 - val : 0; }
|
||||||
|
|
||||||
FeedbackShift = val ? 9 - val : 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//==================================================================================================
|
//==================================================================================================
|
||||||
// Set frequency modulation/additive modulation
|
// Set frequency modulation/additive modulation
|
||||||
//==================================================================================================
|
//==================================================================================================
|
||||||
void Opal::Channel::SetModulationType(uint16_t type) {
|
void Opal::Channel::SetModulationType(uint16_t type) { ModulationType = type; }
|
||||||
|
|
||||||
ModulationType = type;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//==================================================================================================
|
//==================================================================================================
|
||||||
// Compute the stepping factor for the operator waveform phase based on the frequency and octave
|
// Compute the stepping factor for the operator waveform phase based on the frequency and octave
|
||||||
// values of the channel.
|
// values of the channel.
|
||||||
//==================================================================================================
|
//==================================================================================================
|
||||||
void Opal::Channel::ComputePhaseStep() {
|
void Opal::Channel::ComputePhaseStep() { PhaseStep = uint32_t(Freq) << Octave; }
|
||||||
|
|
||||||
PhaseStep = uint32_t(Freq) << Octave;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//==================================================================================================
|
//==================================================================================================
|
||||||
// Compute the key scale number and key scale levels.
|
// Compute the key scale number and key scale levels.
|
||||||
|
@ -848,8 +813,6 @@ void Opal::Channel::ComputeKeyScaleNumber() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//==================================================================================================
|
//==================================================================================================
|
||||||
// Operator constructor.
|
// Operator constructor.
|
||||||
//==================================================================================================
|
//==================================================================================================
|
||||||
|
@ -876,12 +839,10 @@ Opal::Operator::Operator() {
|
||||||
VibratoEnable = false;
|
VibratoEnable = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//==================================================================================================
|
//==================================================================================================
|
||||||
// Produce output from operator.
|
// Produce output from operator.
|
||||||
//==================================================================================================
|
//==================================================================================================
|
||||||
int16_t Opal::Operator::Output(uint16_t keyscalenum, uint32_t phase_step, int16_t vibrato, int16_t mod, int16_t fbshift) {
|
int16_t Opal::Operator::Output(uint16_t /*keyscalenum*/, uint32_t phase_step, int16_t vibrato, int16_t mod, int16_t fbshift) {
|
||||||
|
|
||||||
// Advance wave phase
|
// Advance wave phase
|
||||||
if (VibratoEnable)
|
if (VibratoEnable)
|
||||||
|
@ -894,11 +855,11 @@ int16_t Opal::Operator::Output(uint16_t keyscalenum, uint32_t phase_step, int16_
|
||||||
|
|
||||||
// Attack stage
|
// Attack stage
|
||||||
case EnvAtt: {
|
case EnvAtt: {
|
||||||
if (AttackRate == 0)
|
|
||||||
break;
|
|
||||||
if (AttackMask && (Master->Clock & AttackMask))
|
|
||||||
break;
|
|
||||||
uint16_t add = ((AttackAdd >> AttackTab[Master->Clock >> AttackShift & 7]) * ~EnvelopeLevel) >> 3;
|
uint16_t add = ((AttackAdd >> AttackTab[Master->Clock >> AttackShift & 7]) * ~EnvelopeLevel) >> 3;
|
||||||
|
if (AttackRate == 0)
|
||||||
|
add = 0;
|
||||||
|
if (AttackMask && (Master->Clock & AttackMask))
|
||||||
|
add = 0;
|
||||||
EnvelopeLevel += add;
|
EnvelopeLevel += add;
|
||||||
if (EnvelopeLevel <= 0) {
|
if (EnvelopeLevel <= 0) {
|
||||||
EnvelopeLevel = 0;
|
EnvelopeLevel = 0;
|
||||||
|
@ -909,11 +870,11 @@ int16_t Opal::Operator::Output(uint16_t keyscalenum, uint32_t phase_step, int16_
|
||||||
|
|
||||||
// Decay stage
|
// Decay stage
|
||||||
case EnvDec: {
|
case EnvDec: {
|
||||||
if (DecayRate == 0)
|
|
||||||
break;
|
|
||||||
if (DecayMask && (Master->Clock & DecayMask))
|
|
||||||
break;
|
|
||||||
uint16_t add = DecayAdd >> DecayTab[Master->Clock >> DecayShift & 7];
|
uint16_t add = DecayAdd >> DecayTab[Master->Clock >> DecayShift & 7];
|
||||||
|
if (DecayRate == 0)
|
||||||
|
add = 0;
|
||||||
|
if (DecayMask && (Master->Clock & DecayMask))
|
||||||
|
add = 0;
|
||||||
EnvelopeLevel += add;
|
EnvelopeLevel += add;
|
||||||
if (EnvelopeLevel >= SustainLevel) {
|
if (EnvelopeLevel >= SustainLevel) {
|
||||||
EnvelopeLevel = SustainLevel;
|
EnvelopeLevel = SustainLevel;
|
||||||
|
@ -929,15 +890,16 @@ int16_t Opal::Operator::Output(uint16_t keyscalenum, uint32_t phase_step, int16_
|
||||||
break;
|
break;
|
||||||
|
|
||||||
// Note: fall-through!
|
// Note: fall-through!
|
||||||
|
[[fallthrough]];
|
||||||
}
|
}
|
||||||
|
|
||||||
// Release stage
|
// Release stage
|
||||||
case EnvRel: {
|
case EnvRel: {
|
||||||
if (ReleaseRate == 0)
|
|
||||||
break;
|
|
||||||
if (ReleaseMask && (Master->Clock & ReleaseMask))
|
|
||||||
break;
|
|
||||||
uint16_t add = ReleaseAdd >> ReleaseTab[Master->Clock >> ReleaseShift & 7];
|
uint16_t add = ReleaseAdd >> ReleaseTab[Master->Clock >> ReleaseShift & 7];
|
||||||
|
if (ReleaseRate == 0)
|
||||||
|
add = 0;
|
||||||
|
if (ReleaseMask && (Master->Clock & ReleaseMask))
|
||||||
|
add = 0;
|
||||||
EnvelopeLevel += add;
|
EnvelopeLevel += add;
|
||||||
if (EnvelopeLevel >= 0x1FF) {
|
if (EnvelopeLevel >= 0x1FF) {
|
||||||
EnvelopeLevel = 0x1FF;
|
EnvelopeLevel = 0x1FF;
|
||||||
|
@ -958,7 +920,7 @@ int16_t Opal::Operator::Output(uint16_t keyscalenum, uint32_t phase_step, int16_
|
||||||
if (fbshift)
|
if (fbshift)
|
||||||
mod += (Out[0] + Out[1]) >> fbshift;
|
mod += (Out[0] + Out[1]) >> fbshift;
|
||||||
|
|
||||||
uint16_t phase = (Phase >> 10) + mod;
|
uint16_t phase = static_cast<uint16_t>(Phase >> 10) + mod;
|
||||||
uint16_t offset = phase & 0xFF;
|
uint16_t offset = phase & 0xFF;
|
||||||
uint16_t logsin;
|
uint16_t logsin;
|
||||||
bool negate = false;
|
bool negate = false;
|
||||||
|
@ -972,7 +934,7 @@ int16_t Opal::Operator::Output(uint16_t keyscalenum, uint32_t phase_step, int16_
|
||||||
if (phase & 0x100)
|
if (phase & 0x100)
|
||||||
offset ^= 0xFF;
|
offset ^= 0xFF;
|
||||||
logsin = Master->LogSinTable[offset];
|
logsin = Master->LogSinTable[offset];
|
||||||
negate = (phase & 0x200);
|
negate = (phase & 0x200) != 0;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
//------------------------------------
|
//------------------------------------
|
||||||
|
@ -1017,7 +979,7 @@ int16_t Opal::Operator::Output(uint16_t keyscalenum, uint32_t phase_step, int16_
|
||||||
offset ^= 0xFF;
|
offset ^= 0xFF;
|
||||||
|
|
||||||
offset = (offset + offset) & 0xFF;
|
offset = (offset + offset) & 0xFF;
|
||||||
negate = (phase & 0x100);
|
negate = (phase & 0x100) != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
logsin = Master->LogSinTable[offset];
|
logsin = Master->LogSinTable[offset];
|
||||||
|
@ -1045,7 +1007,7 @@ int16_t Opal::Operator::Output(uint16_t keyscalenum, uint32_t phase_step, int16_
|
||||||
//------------------------------------
|
//------------------------------------
|
||||||
case 6:
|
case 6:
|
||||||
logsin = 0;
|
logsin = 0;
|
||||||
negate = (phase & 0x200);
|
negate = (phase & 0x200) != 0;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
//------------------------------------
|
//------------------------------------
|
||||||
|
@ -1070,8 +1032,7 @@ int16_t Opal::Operator::Output(uint16_t keyscalenum, uint32_t phase_step, int16_
|
||||||
// position given by the 8 LSB's of the input. The value + 1024 (the hidden bit) is then the
|
// position given by the 8 LSB's of the input. The value + 1024 (the hidden bit) is then the
|
||||||
// significand of the floating point output and the yet unused MSB's of the input are the
|
// significand of the floating point output and the yet unused MSB's of the input are the
|
||||||
// exponent of the floating point output."
|
// exponent of the floating point output."
|
||||||
int16_t v = Master->ExpTable[mix & 0xFF] + 1024;
|
int16_t v = (Master->ExpTable[mix & 0xFF] + 1024u) >> (mix >> 8u);
|
||||||
v >>= mix >> 8;
|
|
||||||
v += v;
|
v += v;
|
||||||
if (negate)
|
if (negate)
|
||||||
v = ~v;
|
v = ~v;
|
||||||
|
@ -1083,8 +1044,6 @@ int16_t Opal::Operator::Output(uint16_t keyscalenum, uint32_t phase_step, int16_
|
||||||
return v;
|
return v;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//==================================================================================================
|
//==================================================================================================
|
||||||
// Trigger operator.
|
// Trigger operator.
|
||||||
//==================================================================================================
|
//==================================================================================================
|
||||||
|
@ -1114,38 +1073,21 @@ void Opal::Operator::SetKeyOn(bool on) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//==================================================================================================
|
//==================================================================================================
|
||||||
// Enable amplitude vibrato.
|
// Enable amplitude vibrato.
|
||||||
//==================================================================================================
|
//==================================================================================================
|
||||||
void Opal::Operator::SetTremoloEnable(bool on) {
|
void Opal::Operator::SetTremoloEnable(bool on) { TremoloEnable = on; }
|
||||||
|
|
||||||
TremoloEnable = on;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//==================================================================================================
|
//==================================================================================================
|
||||||
// Enable frequency vibrato.
|
// Enable frequency vibrato.
|
||||||
//==================================================================================================
|
//==================================================================================================
|
||||||
void Opal::Operator::SetVibratoEnable(bool on) {
|
void Opal::Operator::SetVibratoEnable(bool on) { VibratoEnable = on; }
|
||||||
|
|
||||||
VibratoEnable = on;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//==================================================================================================
|
//==================================================================================================
|
||||||
// Sets whether we release or sustain during the sustain phase of the envelope. 'true' is to
|
// Sets whether we release or sustain during the sustain phase of the envelope. 'true' is to
|
||||||
// sustain, otherwise release.
|
// sustain, otherwise release.
|
||||||
//==================================================================================================
|
//==================================================================================================
|
||||||
void Opal::Operator::SetSustainMode(bool on) {
|
void Opal::Operator::SetSustainMode(bool on) { SustainMode = on; }
|
||||||
|
|
||||||
SustainMode = on;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//==================================================================================================
|
//==================================================================================================
|
||||||
// Key scale rate. Sets how much the Key Scaling Number affects the envelope rates.
|
// Key scale rate. Sets how much the Key Scaling Number affects the envelope rates.
|
||||||
|
@ -1156,8 +1098,6 @@ void Opal::Operator::SetEnvelopeScaling(bool on) {
|
||||||
ComputeRates();
|
ComputeRates();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//==================================================================================================
|
//==================================================================================================
|
||||||
// Multiplies the phase frequency.
|
// Multiplies the phase frequency.
|
||||||
//==================================================================================================
|
//==================================================================================================
|
||||||
|
@ -1172,34 +1112,20 @@ void Opal::Operator::SetFrequencyMultiplier(uint16_t scale) {
|
||||||
FreqMultTimes2 = mul_times_2[scale & 15];
|
FreqMultTimes2 = mul_times_2[scale & 15];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//==================================================================================================
|
//==================================================================================================
|
||||||
// Attenuates output level towards higher pitch.
|
// Attenuates output level towards higher pitch.
|
||||||
//==================================================================================================
|
//==================================================================================================
|
||||||
void Opal::Operator::SetKeyScale(uint16_t scale) {
|
void Opal::Operator::SetKeyScale(uint16_t scale) {
|
||||||
|
|
||||||
if (scale > 0)
|
static constexpr uint8_t kslShift[4] = {8, 1, 2, 0};
|
||||||
KeyScaleShift = 3 - scale;
|
KeyScaleShift = kslShift[scale];
|
||||||
|
|
||||||
// No scaling, ensure it has no effect
|
|
||||||
else
|
|
||||||
KeyScaleShift = 15;
|
|
||||||
|
|
||||||
ComputeKeyScaleLevel();
|
ComputeKeyScaleLevel();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//==================================================================================================
|
//==================================================================================================
|
||||||
// Sets the output level (volume) of the operator.
|
// Sets the output level (volume) of the operator.
|
||||||
//==================================================================================================
|
//==================================================================================================
|
||||||
void Opal::Operator::SetOutputLevel(uint16_t level) {
|
void Opal::Operator::SetOutputLevel(uint16_t level) { OutputLevel = level * 4; }
|
||||||
|
|
||||||
OutputLevel = level * 4;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//==================================================================================================
|
//==================================================================================================
|
||||||
// Operator attack rate.
|
// Operator attack rate.
|
||||||
|
@ -1211,8 +1137,6 @@ void Opal::Operator::SetAttackRate(uint16_t rate) {
|
||||||
ComputeRates();
|
ComputeRates();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//==================================================================================================
|
//==================================================================================================
|
||||||
// Operator decay rate.
|
// Operator decay rate.
|
||||||
//==================================================================================================
|
//==================================================================================================
|
||||||
|
@ -1223,8 +1147,6 @@ void Opal::Operator::SetDecayRate(uint16_t rate) {
|
||||||
ComputeRates();
|
ComputeRates();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//==================================================================================================
|
//==================================================================================================
|
||||||
// Operator sustain level.
|
// Operator sustain level.
|
||||||
//==================================================================================================
|
//==================================================================================================
|
||||||
|
@ -1234,8 +1156,6 @@ void Opal::Operator::SetSustainLevel(uint16_t level) {
|
||||||
SustainLevel *= 16;
|
SustainLevel *= 16;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//==================================================================================================
|
//==================================================================================================
|
||||||
// Operator release rate.
|
// Operator release rate.
|
||||||
//==================================================================================================
|
//==================================================================================================
|
||||||
|
@ -1246,17 +1166,10 @@ void Opal::Operator::SetReleaseRate(uint16_t rate) {
|
||||||
ComputeRates();
|
ComputeRates();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//==================================================================================================
|
//==================================================================================================
|
||||||
// Assign the waveform this operator will use.
|
// Assign the waveform this operator will use.
|
||||||
//==================================================================================================
|
//==================================================================================================
|
||||||
void Opal::Operator::SetWaveform(uint16_t wave) {
|
void Opal::Operator::SetWaveform(uint16_t wave) { Waveform = wave & 7; }
|
||||||
|
|
||||||
Waveform = wave & 7;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//==================================================================================================
|
//==================================================================================================
|
||||||
// Compute actual rate from register rate. From the Yamaha data sheet:
|
// Compute actual rate from register rate. From the Yamaha data sheet:
|
||||||
|
@ -1277,7 +1190,7 @@ void Opal::Operator::ComputeRates() {
|
||||||
int rate_high = combined_rate >> 2;
|
int rate_high = combined_rate >> 2;
|
||||||
int rate_low = combined_rate & 3;
|
int rate_low = combined_rate & 3;
|
||||||
|
|
||||||
AttackShift = rate_high < 12 ? 12 - rate_high : 0;
|
AttackShift = static_cast<uint16_t>(rate_high < 12 ? 12 - rate_high : 0);
|
||||||
AttackMask = (1 << AttackShift) - 1;
|
AttackMask = (1 << AttackShift) - 1;
|
||||||
AttackAdd = (rate_high < 12) ? 1 : 1 << (rate_high - 12);
|
AttackAdd = (rate_high < 12) ? 1 : 1 << (rate_high - 12);
|
||||||
AttackTab = Master->RateTables[rate_low];
|
AttackTab = Master->RateTables[rate_low];
|
||||||
|
@ -1290,7 +1203,7 @@ void Opal::Operator::ComputeRates() {
|
||||||
rate_high = combined_rate >> 2;
|
rate_high = combined_rate >> 2;
|
||||||
rate_low = combined_rate & 3;
|
rate_low = combined_rate & 3;
|
||||||
|
|
||||||
DecayShift = rate_high < 12 ? 12 - rate_high : 0;
|
DecayShift = static_cast<uint16_t>(rate_high < 12 ? 12 - rate_high : 0);
|
||||||
DecayMask = (1 << DecayShift) - 1;
|
DecayMask = (1 << DecayShift) - 1;
|
||||||
DecayAdd = (rate_high < 12) ? 1 : 1 << (rate_high - 12);
|
DecayAdd = (rate_high < 12) ? 1 : 1 << (rate_high - 12);
|
||||||
DecayTab = Master->RateTables[rate_low];
|
DecayTab = Master->RateTables[rate_low];
|
||||||
|
@ -1299,21 +1212,20 @@ void Opal::Operator::ComputeRates() {
|
||||||
rate_high = combined_rate >> 2;
|
rate_high = combined_rate >> 2;
|
||||||
rate_low = combined_rate & 3;
|
rate_low = combined_rate & 3;
|
||||||
|
|
||||||
ReleaseShift = rate_high < 12 ? 12 - rate_high : 0;
|
ReleaseShift = static_cast<uint16_t>(rate_high < 12 ? 12 - rate_high : 0);
|
||||||
ReleaseMask = (1 << ReleaseShift) - 1;
|
ReleaseMask = (1 << ReleaseShift) - 1;
|
||||||
ReleaseAdd = (rate_high < 12) ? 1 : 1 << (rate_high - 12);
|
ReleaseAdd = (rate_high < 12) ? 1 : 1 << (rate_high - 12);
|
||||||
ReleaseTab = Master->RateTables[rate_low];
|
ReleaseTab = Master->RateTables[rate_low];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//==================================================================================================
|
//==================================================================================================
|
||||||
// Compute the operator's key scale level. This changes based on the channel frequency/octave and
|
// Compute the operator's key scale level. This changes based on the channel frequency/octave and
|
||||||
// operator key scale value.
|
// operator key scale value.
|
||||||
//==================================================================================================
|
//==================================================================================================
|
||||||
void Opal::Operator::ComputeKeyScaleLevel() {
|
void Opal::Operator::ComputeKeyScaleLevel() {
|
||||||
|
|
||||||
static const uint16_t levtab[] = {
|
// clang-format off
|
||||||
|
static constexpr uint8_t levtab[] = {
|
||||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 12, 16, 20, 24, 28, 32,
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 12, 16, 20, 24, 28, 32,
|
||||||
0, 0, 0, 0, 0, 12, 20, 28, 32, 40, 44, 48, 52, 56, 60, 64,
|
0, 0, 0, 0, 0, 12, 20, 28, 32, 40, 44, 48, 52, 56, 60, 64,
|
||||||
|
@ -1323,6 +1235,7 @@ void Opal::Operator::ComputeKeyScaleLevel() {
|
||||||
0, 64, 96, 116, 128, 140, 148, 156, 160, 168, 172, 176, 180, 184, 188, 192,
|
0, 64, 96, 116, 128, 140, 148, 156, 160, 168, 172, 176, 180, 184, 188, 192,
|
||||||
0, 96, 128, 148, 160, 172, 180, 188, 192, 200, 204, 208, 212, 216, 220, 224,
|
0, 96, 128, 148, 160, 172, 180, 188, 192, 200, 204, 208, 212, 216, 220, 224,
|
||||||
};
|
};
|
||||||
|
// clang-format on
|
||||||
|
|
||||||
// This uses a combined value of the top four bits of frequency with the octave/block
|
// This uses a combined value of the top four bits of frequency with the octave/block
|
||||||
uint16_t i = (Chan->GetOctave() << 4) | (Chan->GetFreq() >> 6);
|
uint16_t i = (Chan->GetOctave() << 4) | (Chan->GetFreq() >> 6);
|
||||||
|
|
|
@ -24,18 +24,14 @@
|
||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
#include <cstdlib>
|
||||||
#include <stdint.h>
|
#include <cstring>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#ifndef RAD_DETECT_REPEATS
|
#ifndef RAD_DETECT_REPEATS
|
||||||
# define RAD_DETECT_REPEATS 0
|
# define RAD_DETECT_REPEATS 0
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//==================================================================================================
|
//==================================================================================================
|
||||||
// RAD player class.
|
// RAD player class.
|
||||||
//==================================================================================================
|
//==================================================================================================
|
||||||
|
@ -62,18 +58,12 @@ class RADPlayer {
|
||||||
cmRiff = ('R' - 55),
|
cmRiff = ('R' - 55),
|
||||||
cmTranspose = ('T' - 55),
|
cmTranspose = ('T' - 55),
|
||||||
cmFeedback = ('U' - 55),
|
cmFeedback = ('U' - 55),
|
||||||
cmVolume = ('V' - 55),
|
cmVolume = ('V' - 55)
|
||||||
};
|
};
|
||||||
|
|
||||||
enum e_Source {
|
enum e_Source { SNone, SRiff, SIRiff };
|
||||||
SNone, SRiff, SIRiff,
|
|
||||||
};
|
|
||||||
|
|
||||||
enum {
|
enum { fKeyOn = 1 << 0, fKeyOff = 1 << 1, fKeyedOn = 1 << 2 };
|
||||||
fKeyOn = 1 << 0,
|
|
||||||
fKeyOff = 1 << 1,
|
|
||||||
fKeyedOn = 1 << 2,
|
|
||||||
};
|
|
||||||
|
|
||||||
struct CInstrument {
|
struct CInstrument {
|
||||||
uint8_t Feedback[2];
|
uint8_t Feedback[2];
|
||||||
|
@ -132,6 +122,13 @@ class RADPlayer {
|
||||||
int GetMasterVolume() const { return MasterVol; }
|
int GetMasterVolume() const { return MasterVol; }
|
||||||
int GetSpeed() const { return Speed; }
|
int GetSpeed() const { return Speed; }
|
||||||
|
|
||||||
|
/* BEGIN MEGAZEUX ADDITIONS */
|
||||||
|
|
||||||
|
void SetTunePos(uint32_t order, uint32_t line);
|
||||||
|
int GetTuneEffectiveLength();
|
||||||
|
|
||||||
|
/* END MEGAZEUX ADDITIONS */
|
||||||
|
|
||||||
#if RAD_DETECT_REPEATS
|
#if RAD_DETECT_REPEATS
|
||||||
uint32_t ComputeTotalTime();
|
uint32_t ComputeTotalTime();
|
||||||
#endif
|
#endif
|
||||||
|
@ -158,9 +155,17 @@ class RADPlayer {
|
||||||
OPL3Regs[reg] = val;
|
OPL3Regs[reg] = val;
|
||||||
OPL3(OPL3Arg, reg, val);
|
OPL3(OPL3Arg, reg, val);
|
||||||
}
|
}
|
||||||
uint8_t GetOPL3(uint16_t reg) const {
|
uint8_t GetOPL3(uint16_t reg) const { return OPL3Regs[reg]; }
|
||||||
return OPL3Regs[reg];
|
|
||||||
}
|
/* BEGIN MEGAZEUX ADDITIONS */
|
||||||
|
|
||||||
|
void Init10(const void *tune);
|
||||||
|
bool UnpackNote10(uint8_t *&s);
|
||||||
|
uint8_t *SkipToLine10(uint8_t *trk, uint8_t linenum);
|
||||||
|
int LastPatternOrder;
|
||||||
|
bool Is10;
|
||||||
|
|
||||||
|
/* END MEGAZEUX ADDITIONS */
|
||||||
|
|
||||||
void (*OPL3)(void *, uint16_t, uint8_t);
|
void (*OPL3)(void *, uint16_t, uint8_t);
|
||||||
void *OPL3Arg;
|
void *OPL3Arg;
|
||||||
|
@ -193,7 +198,8 @@ class RADPlayer {
|
||||||
uint8_t InstNum;
|
uint8_t InstNum;
|
||||||
uint8_t EffectNum;
|
uint8_t EffectNum;
|
||||||
uint8_t Param;
|
uint8_t Param;
|
||||||
bool LastNote;
|
// Unused field, commented out to suppress warnings.
|
||||||
|
// bool LastNote;
|
||||||
|
|
||||||
static const int8_t NoteSize[];
|
static const int8_t NoteSize[];
|
||||||
static const uint16_t ChanOffsets3[9], Chn2Offsets3[9];
|
static const uint16_t ChanOffsets3[9], Chn2Offsets3[9];
|
||||||
|
@ -206,6 +212,7 @@ const int8_t RADPlayer::NoteSize[] = { 0, 2, 1, 3, 1, 3, 2, 4 };
|
||||||
const uint16_t RADPlayer::ChanOffsets3[9] = {0, 1, 2, 0x100, 0x101, 0x102, 6, 7, 8}; // OPL3 first channel
|
const uint16_t RADPlayer::ChanOffsets3[9] = {0, 1, 2, 0x100, 0x101, 0x102, 6, 7, 8}; // OPL3 first channel
|
||||||
const uint16_t RADPlayer::Chn2Offsets3[9] = {3, 4, 5, 0x103, 0x104, 0x105, 0x106, 0x107, 0x108}; // OPL3 second channel
|
const uint16_t RADPlayer::Chn2Offsets3[9] = {3, 4, 5, 0x103, 0x104, 0x105, 0x106, 0x107, 0x108}; // OPL3 second channel
|
||||||
const uint16_t RADPlayer::NoteFreq[] = {0x16b, 0x181, 0x198, 0x1b0, 0x1ca, 0x1e5, 0x202, 0x220, 0x241, 0x263, 0x287, 0x2ae};
|
const uint16_t RADPlayer::NoteFreq[] = {0x16b, 0x181, 0x198, 0x1b0, 0x1ca, 0x1e5, 0x202, 0x220, 0x241, 0x263, 0x287, 0x2ae};
|
||||||
|
// clang-format off
|
||||||
const uint16_t RADPlayer::OpOffsets3[9][4] = {
|
const uint16_t RADPlayer::OpOffsets3[9][4] = {
|
||||||
{ 0x00B, 0x008, 0x003, 0x000 },
|
{ 0x00B, 0x008, 0x003, 0x000 },
|
||||||
{ 0x00C, 0x009, 0x004, 0x001 },
|
{ 0x00C, 0x009, 0x004, 0x001 },
|
||||||
|
@ -226,8 +233,7 @@ const bool RADPlayer::AlgCarriers[7][4] = {
|
||||||
{ true, false, true, true }, // 5 - 4op - op < op + op + op
|
{ true, false, true, true }, // 5 - 4op - op < op + op + op
|
||||||
{ true, true, true, true }, // 6 - 4op - op + op + op + op
|
{ true, true, true, true }, // 6 - 4op - op + op + op + op
|
||||||
};
|
};
|
||||||
|
// clang-format on
|
||||||
|
|
||||||
|
|
||||||
//==================================================================================================
|
//==================================================================================================
|
||||||
// Initialise a RAD tune for playback. This assumes the tune data is valid and does minimal data
|
// Initialise a RAD tune for playback. This assumes the tune data is valid and does minimal data
|
||||||
|
@ -236,9 +242,12 @@ const bool RADPlayer::AlgCarriers[7][4] = {
|
||||||
void RADPlayer::Init(const void *tune, void (*opl3)(void *, uint16_t, uint8_t), void *arg) {
|
void RADPlayer::Init(const void *tune, void (*opl3)(void *, uint16_t, uint8_t), void *arg) {
|
||||||
|
|
||||||
Initialised = false;
|
Initialised = false;
|
||||||
|
Is10 = false;
|
||||||
|
LastPatternOrder = -1;
|
||||||
|
|
||||||
// Version check; we only support version 2.1 tune files
|
// Version check; we only support version 2.1 tune files
|
||||||
if (*((uint8_t *)tune + 0x10) != 0x21) {
|
uint8_t version = *((uint8_t *)tune + 0x10);
|
||||||
|
if ((version != 0x10) && (version != 0x21)) {
|
||||||
Hertz = -1;
|
Hertz = -1;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -254,6 +263,15 @@ void RADPlayer::Init(const void *tune, void (*opl3)(void *, uint16_t, uint8_t),
|
||||||
for (int j = 0; j < kChannels; j++)
|
for (int j = 0; j < kChannels; j++)
|
||||||
Riffs[i][j] = 0;
|
Riffs[i][j] = 0;
|
||||||
|
|
||||||
|
// These aren't guaranteed to all be stored in the file...
|
||||||
|
memset(&Instruments, 0, sizeof(Instruments));
|
||||||
|
|
||||||
|
if (version == 0x10) {
|
||||||
|
Is10 = true;
|
||||||
|
Init10(tune);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
uint8_t *s = (uint8_t *)tune + 0x11;
|
uint8_t *s = (uint8_t *)tune + 0x11;
|
||||||
|
|
||||||
uint8_t flags = *s++;
|
uint8_t flags = *s++;
|
||||||
|
@ -284,7 +302,8 @@ void RADPlayer::Init(const void *tune, void (*opl3)(void *, uint16_t, uint8_t),
|
||||||
break;
|
break;
|
||||||
|
|
||||||
// Skip instrument name
|
// Skip instrument name
|
||||||
s += *s++;
|
uint8_t name_len = *(s++);
|
||||||
|
s += name_len;
|
||||||
|
|
||||||
CInstrument &inst = Instruments[inst_num - 1];
|
CInstrument &inst = Instruments[inst_num - 1];
|
||||||
|
|
||||||
|
@ -374,8 +393,6 @@ void RADPlayer::Init(const void *tune, void (*opl3)(void *, uint16_t, uint8_t),
|
||||||
Initialised = true;
|
Initialised = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//==================================================================================================
|
//==================================================================================================
|
||||||
// Stop all sounds and reset the tune. Tune will play from the beginning again if you continue to
|
// Stop all sounds and reset the tune. Tune will play from the beginning again if you continue to
|
||||||
// Update().
|
// Update().
|
||||||
|
@ -430,8 +447,6 @@ void RADPlayer::Stop() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//==================================================================================================
|
//==================================================================================================
|
||||||
// Playback update. Call BPM * 2 / 5 times a second. Use GetHertz() for this number after the
|
// Playback update. Call BPM * 2 / 5 times a second. Use GetHertz() for this number after the
|
||||||
// tune has been initialised. Returns true if tune is starting to repeat.
|
// tune has been initialised. Returns true if tune is starting to repeat.
|
||||||
|
@ -469,13 +484,14 @@ bool RADPlayer::Update() {
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//==================================================================================================
|
//==================================================================================================
|
||||||
// Unpacks a single RAD note.
|
// Unpacks a single RAD note.
|
||||||
//==================================================================================================
|
//==================================================================================================
|
||||||
bool RADPlayer::UnpackNote(uint8_t *&s, uint8_t &last_instrument) {
|
bool RADPlayer::UnpackNote(uint8_t *&s, uint8_t &last_instrument) {
|
||||||
|
|
||||||
|
if (Is10)
|
||||||
|
return UnpackNote10(s);
|
||||||
|
|
||||||
uint8_t chanid = *s++;
|
uint8_t chanid = *s++;
|
||||||
|
|
||||||
InstNum = 0;
|
InstNum = 0;
|
||||||
|
@ -511,8 +527,6 @@ bool RADPlayer::UnpackNote(uint8_t *&s, uint8_t &last_instrument) {
|
||||||
return ((chanid & 0x80) != 0);
|
return ((chanid & 0x80) != 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//==================================================================================================
|
//==================================================================================================
|
||||||
// Get current track as indicated by order list.
|
// Get current track as indicated by order list.
|
||||||
//==================================================================================================
|
//==================================================================================================
|
||||||
|
@ -546,13 +560,14 @@ uint8_t *RADPlayer::GetTrack() {
|
||||||
return Tracks[track_num];
|
return Tracks[track_num];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//==================================================================================================
|
//==================================================================================================
|
||||||
// Skip through track till we reach the given line or the next higher one. Returns null if none.
|
// Skip through track till we reach the given line or the next higher one. Returns null if none.
|
||||||
//==================================================================================================
|
//==================================================================================================
|
||||||
uint8_t *RADPlayer::SkipToLine(uint8_t *trk, uint8_t linenum, bool chan_riff) {
|
uint8_t *RADPlayer::SkipToLine(uint8_t *trk, uint8_t linenum, bool chan_riff) {
|
||||||
|
|
||||||
|
if (Is10)
|
||||||
|
return SkipToLine10(trk, linenum);
|
||||||
|
|
||||||
while (1) {
|
while (1) {
|
||||||
|
|
||||||
uint8_t lineid = *trk;
|
uint8_t lineid = *trk;
|
||||||
|
@ -573,8 +588,6 @@ uint8_t *RADPlayer::SkipToLine(uint8_t *trk, uint8_t linenum, bool chan_riff) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//==================================================================================================
|
//==================================================================================================
|
||||||
// Plays one line of current track and advances pointers.
|
// Plays one line of current track and advances pointers.
|
||||||
//==================================================================================================
|
//==================================================================================================
|
||||||
|
@ -624,11 +637,14 @@ void RADPlayer::PlayLine() {
|
||||||
// Move to next track in order list
|
// Move to next track in order list
|
||||||
Order++;
|
Order++;
|
||||||
Track = GetTrack();
|
Track = GetTrack();
|
||||||
|
|
||||||
|
// NOTE: This fixes a bug where the vanilla copy of this player
|
||||||
|
// fails to handle Dxx correctly. See: mindflux.rad order 13. -Lachesis
|
||||||
|
if (Line > 0)
|
||||||
|
Track = SkipToLine(Track, Line, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//==================================================================================================
|
//==================================================================================================
|
||||||
// Play a single note. Returns the line number in the next pattern to jump to if a jump command was
|
// Play a single note. Returns the line number in the next pattern to jump to if a jump command was
|
||||||
// found, or -1 if none.
|
// found, or -1 if none.
|
||||||
|
@ -667,10 +683,15 @@ void RADPlayer::PlayNote(int channum, int8_t notenum, int8_t octave, uint16_t in
|
||||||
chan.Instrument = inst;
|
chan.Instrument = inst;
|
||||||
|
|
||||||
// Ignore MIDI instruments
|
// Ignore MIDI instruments
|
||||||
|
// NOTE: the vanilla player does this, meaning no effects or new riffs
|
||||||
|
// will trigger on this line.
|
||||||
|
/*
|
||||||
if (inst->Algorithm == 7) {
|
if (inst->Algorithm == 7) {
|
||||||
Entrances--;
|
Entrances--;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
if (inst->Algorithm < 7) {
|
||||||
|
|
||||||
LoadInstrumentOPL3(channum);
|
LoadInstrumentOPL3(channum);
|
||||||
|
|
||||||
|
@ -706,6 +727,8 @@ void RADPlayer::PlayNote(int channum, int8_t notenum, int8_t octave, uint16_t in
|
||||||
} else
|
} else
|
||||||
chan.IRiff.SpeedCnt = 0;
|
chan.IRiff.SpeedCnt = 0;
|
||||||
}
|
}
|
||||||
|
} else
|
||||||
|
chan.Instrument = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Starting a channel riff?
|
// Starting a channel riff?
|
||||||
|
@ -836,8 +859,6 @@ toneslide:
|
||||||
Entrances--;
|
Entrances--;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//==================================================================================================
|
//==================================================================================================
|
||||||
// Sets the OPL3 registers for a given instrument.
|
// Sets the OPL3 registers for a given instrument.
|
||||||
//==================================================================================================
|
//==================================================================================================
|
||||||
|
@ -886,8 +907,6 @@ void RADPlayer::LoadInstrumentOPL3(int channum) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//==================================================================================================
|
//==================================================================================================
|
||||||
// Play note on OPL3 hardware.
|
// Play note on OPL3 hardware.
|
||||||
//==================================================================================================
|
//==================================================================================================
|
||||||
|
@ -934,8 +953,6 @@ void RADPlayer::PlayNoteOPL3(int channum, int8_t octave, int8_t note) {
|
||||||
SetOPL3(0xB0 + o2, (freq >> 8) | (octave << 2) | ((chan.KeyFlags & fKeyedOn) ? 0x20 : 0));
|
SetOPL3(0xB0 + o2, (freq >> 8) | (octave << 2) | ((chan.KeyFlags & fKeyedOn) ? 0x20 : 0));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//==================================================================================================
|
//==================================================================================================
|
||||||
// Prepare FX for new line.
|
// Prepare FX for new line.
|
||||||
//==================================================================================================
|
//==================================================================================================
|
||||||
|
@ -945,8 +962,6 @@ void RADPlayer::ResetFX(CEffects *fx) {
|
||||||
fx->ToneSlideDir = 0;
|
fx->ToneSlideDir = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//==================================================================================================
|
//==================================================================================================
|
||||||
// Tick the channel riff.
|
// Tick the channel riff.
|
||||||
//==================================================================================================
|
//==================================================================================================
|
||||||
|
@ -1006,6 +1021,7 @@ void RADPlayer::TickRiff(int channum, CChannel::CRiff &riff, bool chan_riff) {
|
||||||
if (!trk || (*trk++ & 0x7F) != riff.Line)
|
if (!trk || (*trk++ & 0x7F) != riff.Line)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
lineid = 0; // silence warning
|
||||||
UnpackNote(trk, lineid); // lineid is just a dummy here
|
UnpackNote(trk, lineid); // lineid is just a dummy here
|
||||||
if (EffectNum == cmJumpToLine && Param < kTrackLines) {
|
if (EffectNum == cmJumpToLine && Param < kTrackLines) {
|
||||||
riff.Line = Param;
|
riff.Line = Param;
|
||||||
|
@ -1013,8 +1029,6 @@ void RADPlayer::TickRiff(int channum, CChannel::CRiff &riff, bool chan_riff) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//==================================================================================================
|
//==================================================================================================
|
||||||
// This continues any effects that operate continuously (eg. slides).
|
// This continues any effects that operate continuously (eg. slides).
|
||||||
//==================================================================================================
|
//==================================================================================================
|
||||||
|
@ -1036,8 +1050,6 @@ void RADPlayer::ContinueFX(int channum, CEffects *fx) {
|
||||||
Portamento(channum, fx, fx->ToneSlideDir, true);
|
Portamento(channum, fx, fx->ToneSlideDir, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//==================================================================================================
|
//==================================================================================================
|
||||||
// Sets the volume of given channel.
|
// Sets the volume of given channel.
|
||||||
//==================================================================================================
|
//==================================================================================================
|
||||||
|
@ -1072,8 +1084,6 @@ void RADPlayer::SetVolume(int channum, uint8_t vol) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//==================================================================================================
|
//==================================================================================================
|
||||||
// Starts a tone-slide.
|
// Starts a tone-slide.
|
||||||
//==================================================================================================
|
//==================================================================================================
|
||||||
|
@ -1101,8 +1111,6 @@ void RADPlayer::GetSlideDir(int channum, CEffects *fx) {
|
||||||
fx->ToneSlideDir = speed;
|
fx->ToneSlideDir = speed;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//==================================================================================================
|
//==================================================================================================
|
||||||
// Load multiplier value into operator.
|
// Load multiplier value into operator.
|
||||||
//==================================================================================================
|
//==================================================================================================
|
||||||
|
@ -1111,8 +1119,6 @@ void RADPlayer::LoadInstMultiplierOPL3(int channum, int op, uint8_t mult) {
|
||||||
SetOPL3(reg, (GetOPL3(reg) & 0xF0) | (mult & 15));
|
SetOPL3(reg, (GetOPL3(reg) & 0xF0) | (mult & 15));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//==================================================================================================
|
//==================================================================================================
|
||||||
// Load volume value into operator.
|
// Load volume value into operator.
|
||||||
//==================================================================================================
|
//==================================================================================================
|
||||||
|
@ -1121,8 +1127,6 @@ void RADPlayer::LoadInstVolumeOPL3(int channum, int op, uint8_t vol) {
|
||||||
SetOPL3(reg, (GetOPL3(reg) & 0xC0) | ((vol & 0x3F) ^ 0x3F));
|
SetOPL3(reg, (GetOPL3(reg) & 0xC0) | ((vol & 0x3F) ^ 0x3F));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//==================================================================================================
|
//==================================================================================================
|
||||||
// Load feedback value into instrument.
|
// Load feedback value into instrument.
|
||||||
//==================================================================================================
|
//==================================================================================================
|
||||||
|
@ -1140,8 +1144,6 @@ void RADPlayer::LoadInstFeedbackOPL3(int channum, int which, uint8_t fb) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//==================================================================================================
|
//==================================================================================================
|
||||||
// This adjusts the pitch of the given channel's note. There may also be a limiting value on the
|
// This adjusts the pitch of the given channel's note. There may also be a limiting value on the
|
||||||
// portamento (for tone slides).
|
// portamento (for tone slides).
|
||||||
|
@ -1206,8 +1208,6 @@ void RADPlayer::Portamento(uint16_t channum, CEffects *fx, int8_t amount, bool t
|
||||||
SetOPL3(0xB0 + chan_offset, (frq2 >> 8 & 3) | oct << 2 | (GetOPL3(0xB0 + chan_offset) & 0xE0));
|
SetOPL3(0xB0 + chan_offset, (frq2 >> 8 & 3) | oct << 2 | (GetOPL3(0xB0 + chan_offset) & 0xE0));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//==================================================================================================
|
//==================================================================================================
|
||||||
// Transpose the note returned by UnpackNote().
|
// Transpose the note returned by UnpackNote().
|
||||||
// Note: due to RAD's wonky legacy middle C is octave 3 note number 12.
|
// Note: due to RAD's wonky legacy middle C is octave 3 note number 12.
|
||||||
|
@ -1239,8 +1239,6 @@ void RADPlayer::Transpose(int8_t note, int8_t octave) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//==================================================================================================
|
//==================================================================================================
|
||||||
// Compute total time of tune if it didn't repeat. Note, this stops the tune so should only be done
|
// Compute total time of tune if it didn't repeat. Note, this stops the tune so should only be done
|
||||||
// prior to initial playback.
|
// prior to initial playback.
|
||||||
|
@ -1264,3 +1262,197 @@ uint32_t RADPlayer::ComputeTotalTime() {
|
||||||
return total / Hertz;
|
return total / Hertz;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
// BEGIN MEGAZEUX ADDITIONS
|
||||||
|
|
||||||
|
//==================================================================================================
|
||||||
|
// Initialise a RAD v1 tune for playback. This assumes the tune data is valid and does minimal data
|
||||||
|
// checking. -Lachesis
|
||||||
|
//==================================================================================================
|
||||||
|
void RADPlayer::Init10(const void *tune) {
|
||||||
|
uint8_t *pos = (uint8_t *)tune + 0x11;
|
||||||
|
|
||||||
|
uint8_t flags = *(pos++);
|
||||||
|
Speed = flags & 0x1F;
|
||||||
|
Hertz = 50;
|
||||||
|
|
||||||
|
// Slow timer tune? Return an approximate hz
|
||||||
|
if (flags & 0x40)
|
||||||
|
Hertz = 18;
|
||||||
|
|
||||||
|
// Skip any description (only present if flag is set)
|
||||||
|
if (flags & 0x80)
|
||||||
|
while (*(pos++)) {
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unpack the instruments
|
||||||
|
while (true) {
|
||||||
|
// Instrument number, 0 indicates end of list
|
||||||
|
uint8_t inst_num = *(pos++);
|
||||||
|
if (inst_num == 0)
|
||||||
|
break;
|
||||||
|
|
||||||
|
CInstrument &inst = Instruments[inst_num - 1];
|
||||||
|
|
||||||
|
uint8_t c_flags = pos[0];
|
||||||
|
uint8_t m_flags = pos[1];
|
||||||
|
uint8_t c_ksl_volume = pos[2];
|
||||||
|
uint8_t m_ksl_volume = pos[3];
|
||||||
|
uint8_t c_attack_decay = pos[4];
|
||||||
|
uint8_t m_attack_decay = pos[5];
|
||||||
|
uint8_t c_sustain_release = pos[6];
|
||||||
|
uint8_t m_sustain_release = pos[7];
|
||||||
|
uint8_t feedback_algorithm = pos[8];
|
||||||
|
uint8_t c_waveform = pos[9];
|
||||||
|
uint8_t m_waveform = pos[10];
|
||||||
|
pos += 11;
|
||||||
|
|
||||||
|
inst.Algorithm = (feedback_algorithm & 1);
|
||||||
|
inst.Panning[0] = 0;
|
||||||
|
inst.Panning[1] = 0;
|
||||||
|
|
||||||
|
inst.Feedback[0] = (feedback_algorithm & 0x0E) >> 1;
|
||||||
|
inst.Feedback[1] = (feedback_algorithm & 0x0E) >> 1;
|
||||||
|
|
||||||
|
inst.Volume = (c_ksl_volume & 0x3F) ^ 0x3F;
|
||||||
|
inst.Detune = 0;
|
||||||
|
inst.RiffSpeed = 6;
|
||||||
|
inst.Riff = 0;
|
||||||
|
|
||||||
|
uint8_t *op0 = inst.Operators[0];
|
||||||
|
uint8_t *op1 = inst.Operators[1];
|
||||||
|
uint8_t *op2 = inst.Operators[2];
|
||||||
|
uint8_t *op3 = inst.Operators[3];
|
||||||
|
|
||||||
|
op0[0] = c_flags;
|
||||||
|
op0[1] = c_ksl_volume;
|
||||||
|
op0[2] = c_attack_decay;
|
||||||
|
op0[3] = c_sustain_release;
|
||||||
|
op0[4] = c_waveform;
|
||||||
|
|
||||||
|
op1[0] = m_flags;
|
||||||
|
op1[1] = m_ksl_volume;
|
||||||
|
op1[2] = m_attack_decay;
|
||||||
|
op1[3] = m_sustain_release;
|
||||||
|
op1[4] = m_waveform;
|
||||||
|
|
||||||
|
memset(op2, 0, 5);
|
||||||
|
memset(op3, 0, 5);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get order list
|
||||||
|
OrderListSize = *(pos++);
|
||||||
|
OrderList = pos;
|
||||||
|
pos += OrderListSize;
|
||||||
|
|
||||||
|
// Track offset table
|
||||||
|
for (int i = 0; i < 31; i++) {
|
||||||
|
int offset = pos[0] | (int(pos[1]) << 8);
|
||||||
|
pos += 2;
|
||||||
|
|
||||||
|
if (offset)
|
||||||
|
Tracks[i] = (uint8_t *)tune + offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Done parsing tune, now set up for play
|
||||||
|
for (int i = 0; i < 512; i++)
|
||||||
|
OPL3Regs[i] = 255;
|
||||||
|
|
||||||
|
Stop();
|
||||||
|
|
||||||
|
Initialised = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
//==================================================================================================
|
||||||
|
// Unpacks a single RAD note (v1). -Lachesis
|
||||||
|
//==================================================================================================
|
||||||
|
bool RADPlayer::UnpackNote10(uint8_t *&pos) {
|
||||||
|
uint8_t chanid = *(pos++);
|
||||||
|
uint8_t b1 = *(pos++);
|
||||||
|
uint8_t b2 = *(pos++);
|
||||||
|
|
||||||
|
// Unpack note data
|
||||||
|
NoteNum = b1 & 0x0F;
|
||||||
|
OctaveNum = (b1 & 0x70) >> 4;
|
||||||
|
InstNum = ((b1 & 0x80) >> 3) | ((b2 & 0xF0) >> 4);
|
||||||
|
EffectNum = (b2 & 0x0F);
|
||||||
|
Param = 0;
|
||||||
|
|
||||||
|
// Do we have an effect?
|
||||||
|
if (EffectNum)
|
||||||
|
Param = *(pos++);
|
||||||
|
|
||||||
|
return ((chanid & 0x80) != 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
//==================================================================================================
|
||||||
|
// Skip through track till we reach the given line or the next higher one (v1). Returns null if none.
|
||||||
|
// -Lachesis
|
||||||
|
//==================================================================================================
|
||||||
|
uint8_t *RADPlayer::SkipToLine10(uint8_t *trk, uint8_t linenum) {
|
||||||
|
while (true) {
|
||||||
|
uint8_t lineid = *trk;
|
||||||
|
|
||||||
|
if ((lineid & 0x7F) >= linenum)
|
||||||
|
return trk;
|
||||||
|
|
||||||
|
if (lineid & 0x80)
|
||||||
|
break;
|
||||||
|
|
||||||
|
trk++;
|
||||||
|
|
||||||
|
// Skip channel notes
|
||||||
|
while (true) {
|
||||||
|
uint8_t chanid = *(trk++);
|
||||||
|
trk++;
|
||||||
|
|
||||||
|
// Do we have an effect?
|
||||||
|
if (*(trk++) & 0x0F)
|
||||||
|
trk++;
|
||||||
|
|
||||||
|
if (chanid & 0x80)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
//==================================================================================================
|
||||||
|
// Set the current order and line. -Lachesis
|
||||||
|
//==================================================================================================
|
||||||
|
void RADPlayer::SetTunePos(uint32_t order, uint32_t line) {
|
||||||
|
if (line > kTrackLines)
|
||||||
|
line = 0;
|
||||||
|
|
||||||
|
if (order > kTracks || order > OrderListSize)
|
||||||
|
order = 0;
|
||||||
|
|
||||||
|
Order = order;
|
||||||
|
Line = line;
|
||||||
|
|
||||||
|
Track = GetTrack();
|
||||||
|
|
||||||
|
if (line > 0)
|
||||||
|
Track = SkipToLine(Track, Line, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
//==================================================================================================
|
||||||
|
// Get the effective length of the tune in orders, i.e., the length minus any jumps at the end.
|
||||||
|
// -Lachesis
|
||||||
|
//==================================================================================================
|
||||||
|
int RADPlayer::GetTuneEffectiveLength() {
|
||||||
|
if (LastPatternOrder >= 0)
|
||||||
|
return LastPatternOrder + 1;
|
||||||
|
|
||||||
|
int i;
|
||||||
|
for (i = OrderListSize - 1; i >= 0; i--) {
|
||||||
|
if (!(OrderList[i] & 0x80))
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (i < 0)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
LastPatternOrder = i;
|
||||||
|
return i + 1;
|
||||||
|
}
|
||||||
|
|
|
@ -10,11 +10,7 @@
|
||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
#include <stdint.h>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//==================================================================================================
|
//==================================================================================================
|
||||||
// The error strings are all supplied here in case you want to translate them to another language
|
// The error strings are all supplied here in case you want to translate them to another language
|
||||||
|
@ -41,7 +37,138 @@ const char *g_RADPattBadEffect = "Pattern contains a bad effect and/or parameter
|
||||||
const char *g_RADBadRiffNum = "Tune file contains a bad riff index.";
|
const char *g_RADBadRiffNum = "Tune file contains a bad riff index.";
|
||||||
const char *g_RADExtraBytes = "Tune file contains extra bytes.";
|
const char *g_RADExtraBytes = "Tune file contains extra bytes.";
|
||||||
|
|
||||||
|
//=============================================================================
|
||||||
|
// Validate a RAD V1 file. -Lachesis
|
||||||
|
//=============================================================================
|
||||||
|
static const char *RADValidate10(const void *data, const uint8_t *end) {
|
||||||
|
const uint8_t *start = (const uint8_t *)data;
|
||||||
|
const uint8_t *pos = start;
|
||||||
|
uint16_t pattern_offsets[32];
|
||||||
|
uint32_t i;
|
||||||
|
bool pattern_used[32] = {false};
|
||||||
|
bool last_line;
|
||||||
|
bool last_note;
|
||||||
|
|
||||||
|
// NOTE: an extra byte was allocated and set to null so some extra tracks
|
||||||
|
// would pass this, so bump the end pointer.
|
||||||
|
end++;
|
||||||
|
|
||||||
|
// Description
|
||||||
|
pos += 17;
|
||||||
|
if (*(pos++) & 0x80) {
|
||||||
|
do {
|
||||||
|
if (pos >= end)
|
||||||
|
return g_RADTruncated;
|
||||||
|
} while (*(pos++));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Instruments
|
||||||
|
while (true) {
|
||||||
|
uint8_t num = *(pos++);
|
||||||
|
|
||||||
|
if (num) {
|
||||||
|
pos += 11;
|
||||||
|
|
||||||
|
if (pos >= end)
|
||||||
|
return g_RADTruncated;
|
||||||
|
} else
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Order list
|
||||||
|
uint8_t num_orders = *(pos++);
|
||||||
|
if (num_orders > 128)
|
||||||
|
return g_RADOrderListTooLarge;
|
||||||
|
|
||||||
|
if (pos + num_orders >= end)
|
||||||
|
return g_RADTruncated;
|
||||||
|
|
||||||
|
for (i = 0; i < num_orders; i++) {
|
||||||
|
uint8_t order = *(pos++);
|
||||||
|
|
||||||
|
// Jump marker
|
||||||
|
if (order & 0x80) {
|
||||||
|
order &= 0x7F;
|
||||||
|
if (order >= num_orders)
|
||||||
|
return g_RADBadJumpMarker;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pattern number
|
||||||
|
else {
|
||||||
|
if (order >= 32)
|
||||||
|
return g_RADBadOrderEntry;
|
||||||
|
|
||||||
|
// Mark used patterns for validation.
|
||||||
|
// TODO: possibly walk through the order list instead so unreached
|
||||||
|
// orders don't have their patterns marked used.
|
||||||
|
pattern_used[order] = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pattern offset table
|
||||||
|
if (pos + 64 >= end)
|
||||||
|
return g_RADTruncated;
|
||||||
|
|
||||||
|
for (i = 0; i < 32; i++) {
|
||||||
|
// Ignore offsets to unused patterns- usually they will already be
|
||||||
|
// zero, but "blue_ad.rad" has unused garbage patterns that will fail
|
||||||
|
// validation despite the track otherwise working fine.
|
||||||
|
pattern_offsets[i] = pattern_used[i] ? (pos[0] | (pos[1] << 8)) : 0;
|
||||||
|
pos += 2;
|
||||||
|
|
||||||
|
if (pattern_used[i] && (start + pattern_offsets[i] >= end))
|
||||||
|
return g_RADPattTruncated;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Patterns
|
||||||
|
for (i = 0; i < 32; i++) {
|
||||||
|
if (!pattern_offsets[i])
|
||||||
|
continue;
|
||||||
|
|
||||||
|
pos = start + pattern_offsets[i];
|
||||||
|
|
||||||
|
do {
|
||||||
|
// Line number
|
||||||
|
uint8_t line_num = *(pos++);
|
||||||
|
if ((line_num & 0x7F) >= 64)
|
||||||
|
return g_RADPattBadLineNum;
|
||||||
|
|
||||||
|
last_line = (line_num & 0x80) ? true : false;
|
||||||
|
|
||||||
|
do {
|
||||||
|
if (pos + 2 >= end)
|
||||||
|
return g_RADPattTruncated;
|
||||||
|
|
||||||
|
// Channel
|
||||||
|
uint8_t channel = *(pos++);
|
||||||
|
if ((channel & 0x7F) >= 9)
|
||||||
|
return g_RADPattBadChanNum;
|
||||||
|
|
||||||
|
last_note = (channel & 0x80) ? true : false;
|
||||||
|
|
||||||
|
uint8_t b1 = *(pos++);
|
||||||
|
uint8_t b2 = *(pos++);
|
||||||
|
|
||||||
|
// Note
|
||||||
|
uint8_t note = (b1 & 0x0F);
|
||||||
|
if (note == 13 || note == 14)
|
||||||
|
return g_RADPattBadNoteNum;
|
||||||
|
|
||||||
|
// Instrument
|
||||||
|
// uint8_t inst = ((b1 & 0x80) >> 4) | ((b2 & 0xF0) >> 1);
|
||||||
|
|
||||||
|
// Effect
|
||||||
|
uint8_t fx = (b2 & 0x0F);
|
||||||
|
if (fx) {
|
||||||
|
// Parameter
|
||||||
|
// uint8_t param = *pos;
|
||||||
|
pos++;
|
||||||
|
}
|
||||||
|
} while (!last_note);
|
||||||
|
} while (!last_line);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
//==================================================================================================
|
//==================================================================================================
|
||||||
// Validate a RAD V2 (file format 2.1) tune file. Note, this uses no C++ standard library code.
|
// Validate a RAD V2 (file format 2.1) tune file. Note, this uses no C++ standard library code.
|
||||||
|
@ -86,7 +213,7 @@ static const char *RADCheckPattern(const uint8_t *&s, const uint8_t *e, bool rif
|
||||||
return g_RADPattTruncated;
|
return g_RADPattTruncated;
|
||||||
uint8_t note = *s++;
|
uint8_t note = *s++;
|
||||||
uint8_t notenum = note & 15;
|
uint8_t notenum = note & 15;
|
||||||
uint8_t octave = (note >> 4) & 7;
|
// uint8_t octave = (note >> 4) & 7;
|
||||||
if (notenum == 0 || notenum == 13 || notenum == 14)
|
if (notenum == 0 || notenum == 13 || notenum == 14)
|
||||||
return g_RADPattBadNoteNum;
|
return g_RADPattBadNoteNum;
|
||||||
}
|
}
|
||||||
|
@ -121,14 +248,16 @@ static const char *RADCheckPattern(const uint8_t *&s, const uint8_t *e, bool rif
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
//--------------------------------------------------------------------------------------------------
|
//--------------------------------------------------------------------------------------------------
|
||||||
const char *RADValidate(const void *data, size_t data_size) {
|
const char *RADValidate(const void *data, size_t data_size) {
|
||||||
|
|
||||||
const uint8_t *s = (const uint8_t *)data;
|
const uint8_t *s = (const uint8_t *)data;
|
||||||
const uint8_t *e = s + data_size;
|
const uint8_t *e = s + data_size;
|
||||||
|
uint8_t version;
|
||||||
|
|
||||||
// Check header
|
// Check header
|
||||||
if (data_size < 16)
|
if (data_size < 17)
|
||||||
return g_RADNotATuneFile;
|
return g_RADNotATuneFile;
|
||||||
|
|
||||||
const char *hdrtxt = "RAD by REALiTY!!";
|
const char *hdrtxt = "RAD by REALiTY!!";
|
||||||
|
@ -137,7 +266,10 @@ const char *RADValidate(const void *data, size_t data_size) {
|
||||||
return g_RADNotATuneFile;
|
return g_RADNotATuneFile;
|
||||||
|
|
||||||
// Check version
|
// Check version
|
||||||
if (s >= e || *s++ != 0x21)
|
version = *(s++);
|
||||||
|
if (version == 0x10)
|
||||||
|
return RADValidate10(data, e);
|
||||||
|
if (version != 0x21)
|
||||||
return g_RADNotAVersion21Tune;
|
return g_RADNotAVersion21Tune;
|
||||||
|
|
||||||
// Check flags
|
// Check flags
|
||||||
|
@ -148,7 +280,8 @@ const char *RADValidate(const void *data, size_t data_size) {
|
||||||
if (flags & 0x80)
|
if (flags & 0x80)
|
||||||
return g_RADBadFlags; // Bit 7 is unused
|
return g_RADBadFlags; // Bit 7 is unused
|
||||||
|
|
||||||
if (flags & 0x40) {
|
// NOTE: the vanilla validator incorrectly checks 0x40 here.
|
||||||
|
if (flags & 0x20) {
|
||||||
if (s + 2 > e)
|
if (s + 2 > e)
|
||||||
return g_RADTruncated;
|
return g_RADTruncated;
|
||||||
uint16_t bpm = s[0] | (uint16_t(s[1]) << 8);
|
uint16_t bpm = s[0] | (uint16_t(s[1]) << 8);
|
||||||
|
@ -201,7 +334,9 @@ const char *RADValidate(const void *data, size_t data_size) {
|
||||||
return g_RADTruncated;
|
return g_RADTruncated;
|
||||||
if (s[2] >> 4)
|
if (s[2] >> 4)
|
||||||
return g_RADUnknownMIDIVersion;
|
return g_RADUnknownMIDIVersion;
|
||||||
s += 6;
|
// NOTE the vanilla validator skips 6 bytes here as if the algorithm
|
||||||
|
// byte was already skipped. It wasn't, so skip 7 instead.
|
||||||
|
s += 7;
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
|
|
File diff suppressed because it is too large
Load diff
Loading…
Reference in a new issue