1
1
Fork 0
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:
Samuel Gomes 2023-04-06 18:25:22 +05:30 committed by GitHub
commit 3046d97845
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 1547 additions and 1080 deletions

View file

@ -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);

View file

@ -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;
}

View file

@ -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