diff --git a/internal/c/parts/audio/extras/radv2/opal.cpp b/internal/c/parts/audio/extras/radv2/opal.cpp index 36fe9e582..8710291b2 100644 --- a/internal/c/parts/audio/extras/radv2/opal.cpp +++ b/internal/c/parts/audio/extras/radv2/opal.cpp @@ -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. @@ -14,11 +19,7 @@ */ - - -#include - - +#include //================================================================================================== // Opal class. @@ -29,11 +30,11 @@ class Opal { // Various constants enum { - OPL3SampleRate = 49716, - NumChannels = 18, - NumOperators = 36, + OPL3SampleRate = 49716, + NumChannels = 18, + NumOperators = 36, - EnvOff = -1, + EnvOff = -1, EnvAtt, EnvDec, EnvSus, @@ -43,155 +44,159 @@ class Opal { // A single FM operator class Operator { - public: - Operator(); - void SetMaster(Opal *opal) { Master = opal; } - void SetChannel(Channel *chan) { Chan = chan; } + public: + Operator(); + void SetMaster(Opal *opal) { Master = opal; } + void SetChannel(Channel *chan) { Chan = chan; } - int16_t Output(uint16_t keyscalenum, uint32_t phase_step, int16_t vibrato, int16_t mod = 0, int16_t fbshift = 0); + int16_t Output(uint16_t keyscalenum, uint32_t phase_step, int16_t vibrato, int16_t mod = 0, int16_t fbshift = 0); - void SetKeyOn(bool on); - void SetTremoloEnable(bool on); - void SetVibratoEnable(bool on); - void SetSustainMode(bool on); - void SetEnvelopeScaling(bool on); - void SetFrequencyMultiplier(uint16_t scale); - void SetKeyScale(uint16_t scale); - void SetOutputLevel(uint16_t level); - void SetAttackRate(uint16_t rate); - void SetDecayRate(uint16_t rate); - void SetSustainLevel(uint16_t level); - void SetReleaseRate(uint16_t rate); - void SetWaveform(uint16_t wave); + void SetKeyOn(bool on); + void SetTremoloEnable(bool on); + void SetVibratoEnable(bool on); + void SetSustainMode(bool on); + void SetEnvelopeScaling(bool on); + void SetFrequencyMultiplier(uint16_t scale); + void SetKeyScale(uint16_t scale); + void SetOutputLevel(uint16_t level); + void SetAttackRate(uint16_t rate); + void SetDecayRate(uint16_t rate); + void SetSustainLevel(uint16_t level); + void SetReleaseRate(uint16_t rate); + void SetWaveform(uint16_t wave); - void ComputeRates(); - void ComputeKeyScaleLevel(); + void ComputeRates(); + void ComputeKeyScaleLevel(); - protected: - Opal * Master; // Master object - Channel * Chan; // Owning channel - uint32_t Phase; // The current offset in the selected waveform - uint16_t Waveform; // The waveform id this operator is using - uint16_t FreqMultTimes2; // Frequency multiplier * 2 - int EnvelopeStage; // Which stage the envelope is at (see Env* enums above) - int16_t EnvelopeLevel; // 0 - $1FF, 0 being the loudest - uint16_t OutputLevel; // 0 - $FF - uint16_t AttackRate; - uint16_t DecayRate; - uint16_t SustainLevel; - uint16_t ReleaseRate; - uint16_t AttackShift; - uint16_t AttackMask; - uint16_t AttackAdd; - const uint16_t *AttackTab; - uint16_t DecayShift; - uint16_t DecayMask; - uint16_t DecayAdd; - const uint16_t *DecayTab; - uint16_t ReleaseShift; - uint16_t ReleaseMask; - uint16_t ReleaseAdd; - const uint16_t *ReleaseTab; - uint16_t KeyScaleShift; - uint16_t KeyScaleLevel; - int16_t Out[2]; - bool KeyOn; - bool KeyScaleRate; // Affects envelope rate scaling - bool SustainMode; // Whether to sustain during the sustain phase, or release instead - bool TremoloEnable; - bool VibratoEnable; + protected: + Opal *Master; // Master object + Channel *Chan; // Owning channel + uint32_t Phase; // The current offset in the selected waveform + uint16_t Waveform; // The waveform id this operator is using + uint16_t FreqMultTimes2; // Frequency multiplier * 2 + int EnvelopeStage; // Which stage the envelope is at (see Env* enums above) + int16_t EnvelopeLevel; // 0 - $1FF, 0 being the loudest + uint16_t OutputLevel; // 0 - $FF + uint16_t AttackRate; + uint16_t DecayRate; + uint16_t SustainLevel; + uint16_t ReleaseRate; + uint16_t AttackShift; + uint16_t AttackMask; + uint16_t AttackAdd; + const uint16_t *AttackTab; + uint16_t DecayShift; + uint16_t DecayMask; + uint16_t DecayAdd; + const uint16_t *DecayTab; + uint16_t ReleaseShift; + uint16_t ReleaseMask; + uint16_t ReleaseAdd; + const uint16_t *ReleaseTab; + uint16_t KeyScaleShift; + uint16_t KeyScaleLevel; + int16_t Out[2]; + bool KeyOn; + bool KeyScaleRate; // Affects envelope rate scaling + bool SustainMode; // Whether to sustain during the sustain phase, or release instead + bool TremoloEnable; + bool VibratoEnable; }; // A single channel, which can contain two or more operators class Channel { - public: - Channel(); - void SetMaster(Opal *opal) { Master = opal; } - void SetOperators(Operator *a, Operator *b, Operator *c, Operator *d) { - Op[0] = a; - Op[1] = b; - Op[2] = c; - Op[3] = d; - if (a) - a->SetChannel(this); - if (b) - b->SetChannel(this); - if (c) - c->SetChannel(this); - if (d) - d->SetChannel(this); - } + public: + Channel(); + void SetMaster(Opal *opal) { Master = opal; } + void SetOperators(Operator *a, Operator *b, Operator *c, Operator *d) { + Op[0] = a; + Op[1] = b; + Op[2] = c; + Op[3] = d; + if (a) + a->SetChannel(this); + if (b) + b->SetChannel(this); + if (c) + c->SetChannel(this); + if (d) + d->SetChannel(this); + } - void Output(int16_t &left, int16_t &right); - void SetEnable(bool on) { Enable = on; } - void SetChannelPair(Channel *pair) { ChannelPair = pair; } + void Output(int16_t &left, int16_t &right); + void SetEnable(bool on) { Enable = on; } + void SetChannelPair(Channel *pair) { ChannelPair = pair; } - void SetFrequencyLow(uint16_t freq); - void SetFrequencyHigh(uint16_t freq); - void SetKeyOn(bool on); - void SetOctave(uint16_t oct); - void SetLeftEnable(bool on); - void SetRightEnable(bool on); - void SetFeedback(uint16_t val); - void SetModulationType(uint16_t type); + void SetFrequencyLow(uint16_t freq); + void SetFrequencyHigh(uint16_t freq); + void SetKeyOn(bool on); + void SetOctave(uint16_t oct); + void SetLeftEnable(bool on); + void SetRightEnable(bool on); + void SetFeedback(uint16_t val); + void SetModulationType(uint16_t type); - uint16_t GetFreq() const { return Freq; } - uint16_t GetOctave() const { return Octave; } - uint16_t GetKeyScaleNumber() const { return KeyScaleNumber; } - uint16_t GetModulationType() const { return ModulationType; } + uint16_t GetFreq() const { return Freq; } + uint16_t GetOctave() const { return Octave; } + uint16_t GetKeyScaleNumber() const { return KeyScaleNumber; } + uint16_t GetModulationType() const { return ModulationType; } + Channel *GetChannelPair() const { return ChannelPair; } - void ComputeKeyScaleNumber(); + void ComputeKeyScaleNumber(); - protected: - void ComputePhaseStep(); + protected: + void ComputePhaseStep(); - Operator * Op[4]; + Operator *Op[4]; - Opal * Master; // Master object - uint16_t Freq; // Frequency; actually it's a phase stepping value - uint16_t Octave; // Also known as "block" in Yamaha parlance - uint32_t PhaseStep; - uint16_t KeyScaleNumber; - uint16_t FeedbackShift; - uint16_t ModulationType; - Channel * ChannelPair; - bool Enable; - bool LeftEnable, RightEnable; + Opal *Master; // Master object + uint16_t Freq; // Frequency; actually it's a phase stepping value + uint16_t Octave; // Also known as "block" in Yamaha parlance + uint32_t PhaseStep; + uint16_t KeyScaleNumber; + uint16_t FeedbackShift; + uint16_t ModulationType; + Channel *ChannelPair; + bool Enable; + bool LeftEnable, RightEnable; }; - public: - Opal(int sample_rate); - ~Opal(); + public: + Opal(int sample_rate); + Opal(const Opal &) = delete; + Opal(Opal &&) = delete; + ~Opal(); - void SetSampleRate(int sample_rate); - void Port(uint16_t reg_num, uint8_t val); - void Sample(int16_t *left, int16_t *right); + void SetSampleRate(int sample_rate); + void Port(uint16_t reg_num, uint8_t val); + void Sample(int16_t *left, int16_t *right); - protected: - void Init(int sample_rate); - void Output(int16_t &left, int16_t &right); + protected: + void Init(int sample_rate); + void Output(int16_t &left, int16_t &right); - int32_t SampleRate; - int32_t SampleAccum; - int16_t LastOutput[2], CurrOutput[2]; - Channel Chan[NumChannels]; - Operator Op[NumOperators]; -// uint16_t ExpTable[256]; -// uint16_t LogSinTable[256]; - uint16_t Clock; - uint16_t TremoloClock; - uint16_t TremoloLevel; - uint16_t VibratoTick; - uint16_t VibratoClock; - bool NoteSel; - bool TremoloDepth; - bool VibratoDepth; + int32_t SampleRate; + int32_t SampleAccum; + int16_t LastOutput[2], CurrOutput[2]; + Channel Chan[NumChannels]; + Operator Op[NumOperators]; + // uint16_t ExpTable[256]; + // uint16_t LogSinTable[256]; + uint16_t Clock; + uint16_t TremoloClock; + uint16_t TremoloLevel; + uint16_t VibratoTick; + uint16_t VibratoClock; + bool NoteSel; + bool TremoloDepth; + bool VibratoDepth; - static const uint16_t RateTables[4][8]; - static const uint16_t ExpTable[256]; - static const uint16_t LogSinTable[256]; + static const uint16_t RateTables[4][8]; + static const uint16_t ExpTable[256]; + static const uint16_t LogSinTable[256]; }; +// clang-format off //-------------------------------------------------------------------------------------------------- const uint16_t Opal::RateTables[4][8] = { { 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, 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 @@ -247,7 +251,7 @@ const uint16_t Opal::LogSinTable[0x100] = { // https://docs.google.com/document/d/18IGx18NQY_Q1PJVZ-bHywao9bhsDoAqoIn1rIm42nwo/edit //================================================================================================== #if 0 -#include +# include void GenerateTables() { @@ -279,20 +283,12 @@ void GenerateTables() { } #endif - - //================================================================================================== // Constructor/destructor. //================================================================================================== -Opal::Opal(int sample_rate) { - - Init(sample_rate); -} +Opal::Opal(int sample_rate) { Init(sample_rate); } //-------------------------------------------------------------------------------------------------- -Opal::~Opal() { -} - - +Opal::~Opal() {} //================================================================================================== // Initialise the emulation. @@ -301,19 +297,20 @@ void Opal::Init(int sample_rate) { Clock = 0; TremoloClock = 0; + TremoloLevel = 0; VibratoTick = 0; VibratoClock = 0; NoteSel = false; TremoloDepth = false; VibratoDepth = false; -// // Build the exponentiation table (reversed from the official OPL3 ROM) -// for (int i = 0; i < 0x100; i++) -// ExpTable[i] = (pow(2, (0xFF - i) / 256.0) - 1) * 1024 + 0.5; -// -// // Build the log-sin table -// for (int i = 0; i < 0x100; i++) -// LogSinTable[i] = -log(sin((i + 0.5) * 3.1415926535897933 / 256 / 2)) / log(2) * 256 + 0.5; + // // Build the exponentiation table (reversed from the official OPL3 ROM) + // for (int i = 0; i < 0x100; i++) + // ExpTable[i] = (pow(2, (0xFF - i) / 256.0) - 1) * 1024 + 0.5; + // + // // Build the log-sin table + // for (int i = 0; i < 0x100; i++) + // LogSinTable[i] = -log(sin((i + 0.5) * 3.1415926535897933 / 256 / 2)) / log(2) * 256 + 0.5; // Let sub-objects know where to find us for (int i = 0; i < NumOperators; i++) @@ -345,8 +342,6 @@ void Opal::Init(int sample_rate) { SetSampleRate(sample_rate); } - - //================================================================================================== // Change the sample rate. //================================================================================================== @@ -362,26 +357,26 @@ void Opal::SetSampleRate(int sample_rate) { CurrOutput[0] = CurrOutput[1] = 0; } - - //================================================================================================== // Write a value to an OPL3 register. //================================================================================================== 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 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 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; // Is it BD, the one-off register stuck in the middle of the register array? if (reg_num == 0xBD) { - TremoloDepth = (val & 0x80); - VibratoDepth = (val & 0x40); + TremoloDepth = (val & 0x80) != 0; + VibratoDepth = (val & 0x40) != 0; return; } @@ -396,7 +391,7 @@ void Opal::Port(uint16_t reg_num, uint8_t val) { for (int i = 0; i < 6; i++, mask <<= 1) { // The 4-op channels are 0, 1, 2, 9, 10, 11 - uint16_t chan = i < 3 ? i : i + 6; + uint16_t chan = static_cast(i < 3 ? i : i + 6); Channel *primary = &Chan[chan]; Channel *secondary = &Chan[chan + 3]; @@ -418,17 +413,17 @@ void Opal::Port(uint16_t reg_num, uint8_t val) { } } - // CSW / Note-sel + // CSW / Note-sel } 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 for (int i = 0; i < NumChannels; i++) Chan[i].ComputeKeyScaleNumber(); } - // Channel registers + // Channel registers } else if (type >= 0xA0 && type <= 0xC0) { // Convert to channel number @@ -444,34 +439,42 @@ void Opal::Port(uint16_t reg_num, uint8_t val) { 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 switch (reg_num & 0xF0) { - // Frequency low - case 0xA0: { - chan.SetFrequencyLow(val); - break; - } - - // Key-on / Octave / Frequency High - case 0xB0: { - chan.SetKeyOn(val & 0x20); - chan.SetOctave(val >> 2 & 7); - chan.SetFrequencyHigh(val & 3); - break; - } - - // Right Stereo Channel Enable / Left Stereo Channel Enable / Feedback Factor / Modulation Type - case 0xC0: { - chan.SetRightEnable(val & 0x20); - chan.SetLeftEnable(val & 0x10); - chan.SetFeedback(val >> 1 & 7); - chan.SetModulationType(val & 1); - break; + // Frequency low + case 0xA0: { + for (int i = 0; i < numchans; i++) { + chans[i]->SetFrequencyLow(val); } + break; } - // Operator registers + // Key-on / Octave / Frequency High + case 0xB0: { + for (int i = 0; i < numchans; i++) { + chans[i]->SetKeyOn((val & 0x20) != 0); + chans[i]->SetOctave(val >> 2 & 7); + chans[i]->SetFrequencyHigh(val & 3); + } + break; + } + + // Right Stereo Channel Enable / Left Stereo Channel Enable / Feedback Factor / Modulation Type + case 0xC0: { + chan.SetRightEnable((val & 0x20) != 0); + chan.SetLeftEnable((val & 0x10) != 0); + chan.SetFeedback(val >> 1 & 7); + chan.SetModulationType(val & 1); + break; + } + } + + // Operator registers } else if ((type >= 0x20 && type <= 0x80) || type == 0xE0) { // Convert to operator number @@ -490,48 +493,46 @@ void Opal::Port(uint16_t reg_num, uint8_t val) { // Do specific registers switch (type) { - // Tremolo Enable / Vibrato Enable / Sustain Mode / Envelope Scaling / Frequency Multiplier - case 0x20: { - op.SetTremoloEnable(val & 0x80); - op.SetVibratoEnable(val & 0x40); - op.SetSustainMode(val & 0x20); - op.SetEnvelopeScaling(val & 0x10); - op.SetFrequencyMultiplier(val & 15); - break; - } + // Tremolo Enable / Vibrato Enable / Sustain Mode / Envelope Scaling / Frequency Multiplier + case 0x20: { + op.SetTremoloEnable((val & 0x80) != 0); + op.SetVibratoEnable((val & 0x40) != 0); + op.SetSustainMode((val & 0x20) != 0); + op.SetEnvelopeScaling((val & 0x10) != 0); + op.SetFrequencyMultiplier(val & 15); + break; + } - // Key Scale / Output Level - case 0x40: { - op.SetKeyScale(val >> 6); - op.SetOutputLevel(val & 0x3F); - break; - } + // Key Scale / Output Level + case 0x40: { + op.SetKeyScale(val >> 6); + op.SetOutputLevel(val & 0x3F); + break; + } - // Attack Rate / Decay Rate - case 0x60: { - op.SetAttackRate(val >> 4); - op.SetDecayRate(val & 15); - break; - } + // Attack Rate / Decay Rate + case 0x60: { + op.SetAttackRate(val >> 4); + op.SetDecayRate(val & 15); + break; + } - // Sustain Level / Release Rate - case 0x80: { - op.SetSustainLevel(val >> 4); - op.SetReleaseRate(val & 15); - break; - } + // Sustain Level / Release Rate + case 0x80: { + op.SetSustainLevel(val >> 4); + op.SetReleaseRate(val & 15); + break; + } - // Waveform - case 0xE0: { - op.SetWaveform(val & 7); - break; - } + // Waveform + case 0xE0: { + op.SetWaveform(val & 7); + break; + } } } } - - //================================================================================================== // 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 @@ -552,14 +553,12 @@ void Opal::Sample(int16_t *left, int16_t *right) { // Mix with the partial accumulation int32_t omblend = SampleRate - SampleAccum; - *left = (LastOutput[0] * omblend + CurrOutput[0] * SampleAccum) / SampleRate; - *right = (LastOutput[1] * omblend + CurrOutput[1] * SampleAccum) / SampleRate; + *left = static_cast((LastOutput[0] * omblend + CurrOutput[0] * SampleAccum) / SampleRate); + *right = static_cast((LastOutput[1] * omblend + CurrOutput[1] * SampleAccum) / SampleRate); SampleAccum += OPL3SampleRate; } - - //================================================================================================== // 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) left = 0x7FFF; else - left = leftmix; + left = static_cast(leftmix); if (rightmix < -0x8000) right = -0x8000; else if (rightmix > 0x7FFF) right = 0x7FFF; else - right = rightmix; + right = static_cast(rightmix); Clock++; @@ -612,8 +611,6 @@ void Opal::Output(int16_t &left, int16_t &right) { } } - - //================================================================================================== // Channel constructor. //================================================================================================== @@ -628,10 +625,10 @@ Opal::Channel::Channel() { ModulationType = 0; ChannelPair = 0; Enable = true; + LeftEnable = true; + RightEnable = true; } - - //================================================================================================== // Produce output from channel. //================================================================================================== @@ -651,15 +648,16 @@ void Opal::Channel::Output(int16_t &left, int16_t &right) { // 0 3 7 3 0 -3 -7 -3 uint16_t clk = Master->VibratoClock; if (!(clk & 3)) - vibrato = 0; // Position 0 and 4 is zero + vibrato = 0; // Position 0 and 4 is zero else { if (clk & 1) - vibrato >>= 1; // Odd positions are half the magnitude - if (clk & 4) - vibrato = -vibrato; // The second half positions are negative - } + vibrato >>= 1; // Odd positions are half the magnitude - vibrato <<= Octave; + vibrato <<= Octave; + + if (clk & 4) + vibrato = -vibrato; // The second half positions are negative + } // Combine individual operator outputs int16_t out, acc; @@ -674,17 +672,17 @@ void Opal::Channel::Output(int16_t &left, int16_t &right) { if (ModulationType == 0) { // feedback -> modulator -> modulator -> modulator -> carrier - out = Op[0]->Output(KeyScaleNumber, PhaseStep, vibrato, 0, FeedbackShift); - out = Op[1]->Output(KeyScaleNumber, PhaseStep, vibrato, out, 0); - out = Op[2]->Output(KeyScaleNumber, PhaseStep, vibrato, out, 0); - out = Op[3]->Output(KeyScaleNumber, PhaseStep, vibrato, out, 0); + out = Op[0]->Output(KeyScaleNumber, PhaseStep, vibrato, 0, FeedbackShift); + out = Op[1]->Output(KeyScaleNumber, PhaseStep, vibrato, out, 0); + out = Op[2]->Output(KeyScaleNumber, PhaseStep, vibrato, out, 0); + out = Op[3]->Output(KeyScaleNumber, PhaseStep, vibrato, out, 0); } else { // (feedback -> carrier) + (modulator -> modulator -> carrier) - out = Op[0]->Output(KeyScaleNumber, PhaseStep, vibrato, 0, FeedbackShift); - acc = Op[1]->Output(KeyScaleNumber, PhaseStep, vibrato, 0, 0); - acc = Op[2]->Output(KeyScaleNumber, PhaseStep, vibrato, acc, 0); + out = Op[0]->Output(KeyScaleNumber, PhaseStep, vibrato, 0, FeedbackShift); + acc = Op[1]->Output(KeyScaleNumber, PhaseStep, vibrato, 0, 0); + acc = Op[2]->Output(KeyScaleNumber, PhaseStep, vibrato, acc, 0); out += Op[3]->Output(KeyScaleNumber, PhaseStep, vibrato, acc, 0); } @@ -693,16 +691,16 @@ void Opal::Channel::Output(int16_t &left, int16_t &right) { if (ModulationType == 0) { // (feedback -> modulator -> carrier) + (modulator -> carrier) - out = Op[0]->Output(KeyScaleNumber, PhaseStep, vibrato, 0, FeedbackShift); - out = Op[1]->Output(KeyScaleNumber, PhaseStep, vibrato, out, 0); - acc = Op[2]->Output(KeyScaleNumber, PhaseStep, vibrato, 0, 0); + out = Op[0]->Output(KeyScaleNumber, PhaseStep, vibrato, 0, FeedbackShift); + out = Op[1]->Output(KeyScaleNumber, PhaseStep, vibrato, out, 0); + acc = Op[2]->Output(KeyScaleNumber, PhaseStep, vibrato, 0, 0); out += Op[3]->Output(KeyScaleNumber, PhaseStep, vibrato, acc, 0); } else { // (feedback -> carrier) + (modulator -> carrier) + carrier - out = Op[0]->Output(KeyScaleNumber, PhaseStep, vibrato, 0, FeedbackShift); - acc = Op[1]->Output(KeyScaleNumber, PhaseStep, vibrato, 0, 0); + out = Op[0]->Output(KeyScaleNumber, PhaseStep, vibrato, 0, FeedbackShift); + acc = Op[1]->Output(KeyScaleNumber, PhaseStep, vibrato, 0, 0); out += Op[2]->Output(KeyScaleNumber, PhaseStep, vibrato, acc, 0); out += Op[3]->Output(KeyScaleNumber, PhaseStep, vibrato, 0, 0); } @@ -729,8 +727,6 @@ void Opal::Channel::Output(int16_t &left, int16_t &right) { right = RightEnable ? out : 0; } - - //================================================================================================== // Set phase step for operators using this channel. //================================================================================================== @@ -749,8 +745,6 @@ void Opal::Channel::SetFrequencyHigh(uint16_t freq) { ComputeKeyScaleNumber(); } - - //================================================================================================== // Set the octave of the channel (0 to 7). //================================================================================================== @@ -761,8 +755,6 @@ void Opal::Channel::SetOctave(uint16_t oct) { ComputeKeyScaleNumber(); } - - //================================================================================================== // Keys the channel on/off. //================================================================================================== @@ -772,58 +764,31 @@ void Opal::Channel::SetKeyOn(bool on) { Op[1]->SetKeyOn(on); } - - //================================================================================================== // Enable left stereo channel. //================================================================================================== -void Opal::Channel::SetLeftEnable(bool on) { - - LeftEnable = on; -} - - +void Opal::Channel::SetLeftEnable(bool on) { LeftEnable = on; } //================================================================================================== // Enable right stereo channel. //================================================================================================== -void Opal::Channel::SetRightEnable(bool on) { - - RightEnable = on; -} - - +void Opal::Channel::SetRightEnable(bool on) { RightEnable = on; } //================================================================================================== // Set the channel feedback amount. //================================================================================================== -void Opal::Channel::SetFeedback(uint16_t val) { - - FeedbackShift = val ? 9 - val : 0; -} - - +void Opal::Channel::SetFeedback(uint16_t val) { FeedbackShift = val ? 9 - val : 0; } //================================================================================================== // Set frequency modulation/additive modulation //================================================================================================== -void Opal::Channel::SetModulationType(uint16_t type) { - - ModulationType = type; -} - - +void Opal::Channel::SetModulationType(uint16_t type) { ModulationType = type; } //================================================================================================== // Compute the stepping factor for the operator waveform phase based on the frequency and octave // values of the channel. //================================================================================================== -void Opal::Channel::ComputePhaseStep() { - - PhaseStep = uint32_t(Freq) << Octave; -} - - +void Opal::Channel::ComputePhaseStep() { PhaseStep = uint32_t(Freq) << Octave; } //================================================================================================== // Compute the key scale number and key scale levels. @@ -848,8 +813,6 @@ void Opal::Channel::ComputeKeyScaleNumber() { } } - - //================================================================================================== // Operator constructor. //================================================================================================== @@ -876,12 +839,10 @@ Opal::Operator::Operator() { VibratoEnable = false; } - - //================================================================================================== // 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 if (VibratoEnable) @@ -892,173 +853,174 @@ int16_t Opal::Operator::Output(uint16_t keyscalenum, uint32_t phase_step, int16_ switch (EnvelopeStage) { - // Attack stage - case EnvAtt: { - if (AttackRate == 0) - break; - if (AttackMask && (Master->Clock & AttackMask)) - break; - uint16_t add = ((AttackAdd >> AttackTab[Master->Clock >> AttackShift & 7]) * ~EnvelopeLevel) >> 3; - EnvelopeLevel += add; - if (EnvelopeLevel <= 0) { - EnvelopeLevel = 0; - EnvelopeStage = EnvDec; - } + // Attack stage + case EnvAtt: { + 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; + if (EnvelopeLevel <= 0) { + EnvelopeLevel = 0; + EnvelopeStage = EnvDec; + } + break; + } + + // Decay stage + case EnvDec: { + uint16_t add = DecayAdd >> DecayTab[Master->Clock >> DecayShift & 7]; + if (DecayRate == 0) + add = 0; + if (DecayMask && (Master->Clock & DecayMask)) + add = 0; + EnvelopeLevel += add; + if (EnvelopeLevel >= SustainLevel) { + EnvelopeLevel = SustainLevel; + EnvelopeStage = EnvSus; + } + break; + } + + // Sustain stage + case EnvSus: { + + if (SustainMode) break; - } - // Decay stage - case EnvDec: { - if (DecayRate == 0) - break; - if (DecayMask && (Master->Clock & DecayMask)) - break; - uint16_t add = DecayAdd >> DecayTab[Master->Clock >> DecayShift & 7]; - EnvelopeLevel += add; - if (EnvelopeLevel >= SustainLevel) { - EnvelopeLevel = SustainLevel; - EnvelopeStage = EnvSus; - } - break; - } + // Note: fall-through! + [[fallthrough]]; + } - // Sustain stage - case EnvSus: { - - if (SustainMode) - break; - - // Note: fall-through! - } - - // Release stage - case EnvRel: { - if (ReleaseRate == 0) - break; - if (ReleaseMask && (Master->Clock & ReleaseMask)) - break; - uint16_t add = ReleaseAdd >> ReleaseTab[Master->Clock >> ReleaseShift & 7]; - EnvelopeLevel += add; - if (EnvelopeLevel >= 0x1FF) { - EnvelopeLevel = 0x1FF; - EnvelopeStage = EnvOff; - Out[0] = Out[1] = 0; - return 0; - } - break; - } - - // Envelope, and therefore the operator, is not running - default: + // Release stage + case EnvRel: { + uint16_t add = ReleaseAdd >> ReleaseTab[Master->Clock >> ReleaseShift & 7]; + if (ReleaseRate == 0) + add = 0; + if (ReleaseMask && (Master->Clock & ReleaseMask)) + add = 0; + EnvelopeLevel += add; + if (EnvelopeLevel >= 0x1FF) { + EnvelopeLevel = 0x1FF; + EnvelopeStage = EnvOff; Out[0] = Out[1] = 0; return 0; + } + break; + } + + // Envelope, and therefore the operator, is not running + default: + Out[0] = Out[1] = 0; + return 0; } // Feedback? In that case we modulate by a blend of the last two samples if (fbshift) mod += (Out[0] + Out[1]) >> fbshift; - uint16_t phase = (Phase >> 10) + mod; + uint16_t phase = static_cast(Phase >> 10) + mod; uint16_t offset = phase & 0xFF; uint16_t logsin; bool negate = false; switch (Waveform) { - //------------------------------------ - // Standard sine wave - //------------------------------------ - case 0: - if (phase & 0x100) + //------------------------------------ + // Standard sine wave + //------------------------------------ + case 0: + if (phase & 0x100) + offset ^= 0xFF; + logsin = Master->LogSinTable[offset]; + negate = (phase & 0x200) != 0; + break; + + //------------------------------------ + // Half sine wave + //------------------------------------ + case 1: + if (phase & 0x200) + offset = 0; + else if (phase & 0x100) + offset ^= 0xFF; + logsin = Master->LogSinTable[offset]; + break; + + //------------------------------------ + // Positive sine wave + //------------------------------------ + case 2: + if (phase & 0x100) + offset ^= 0xFF; + logsin = Master->LogSinTable[offset]; + break; + + //------------------------------------ + // Quarter positive sine wave + //------------------------------------ + case 3: + if (phase & 0x100) + offset = 0; + logsin = Master->LogSinTable[offset]; + break; + + //------------------------------------ + // Double-speed sine wave + //------------------------------------ + case 4: + if (phase & 0x200) + offset = 0; + + else { + + if (phase & 0x80) offset ^= 0xFF; - logsin = Master->LogSinTable[offset]; - negate = (phase & 0x200); - break; - //------------------------------------ - // Half sine wave - //------------------------------------ - case 1: - if (phase & 0x200) - offset = 0; - else if (phase & 0x100) + offset = (offset + offset) & 0xFF; + negate = (phase & 0x100) != 0; + } + + logsin = Master->LogSinTable[offset]; + break; + + //------------------------------------ + // Double-speed positive sine wave + //------------------------------------ + case 5: + if (phase & 0x200) + offset = 0; + + else { + + offset = (offset + offset) & 0xFF; + if (phase & 0x80) offset ^= 0xFF; - logsin = Master->LogSinTable[offset]; - break; + } - //------------------------------------ - // Positive sine wave - //------------------------------------ - case 2: - if (phase & 0x100) - offset ^= 0xFF; - logsin = Master->LogSinTable[offset]; - break; + logsin = Master->LogSinTable[offset]; + break; - //------------------------------------ - // Quarter positive sine wave - //------------------------------------ - case 3: - if (phase & 0x100) - offset = 0; - logsin = Master->LogSinTable[offset]; - break; + //------------------------------------ + // Square wave + //------------------------------------ + case 6: + logsin = 0; + negate = (phase & 0x200) != 0; + break; - //------------------------------------ - // Double-speed sine wave - //------------------------------------ - case 4: - if (phase & 0x200) - offset = 0; - - else { - - if (phase & 0x80) - offset ^= 0xFF; - - offset = (offset + offset) & 0xFF; - negate = (phase & 0x100); - } - - logsin = Master->LogSinTable[offset]; - break; - - //------------------------------------ - // Double-speed positive sine wave - //------------------------------------ - case 5: - if (phase & 0x200) - offset = 0; - - else { - - offset = (offset + offset) & 0xFF; - if (phase & 0x80) - offset ^= 0xFF; - } - - logsin = Master->LogSinTable[offset]; - break; - - //------------------------------------ - // Square wave - //------------------------------------ - case 6: - logsin = 0; - negate = (phase & 0x200); - break; - - //------------------------------------ - // Exponentiation wave - //------------------------------------ - default: - logsin = phase & 0x1FF; - if (phase & 0x200) { - logsin ^= 0x1FF; - negate = true; - } - logsin <<= 3; - break; + //------------------------------------ + // Exponentiation wave + //------------------------------------ + default: + logsin = phase & 0x1FF; + if (phase & 0x200) { + logsin ^= 0x1FF; + negate = true; + } + logsin <<= 3; + break; } uint16_t mix = logsin + level; @@ -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 // significand of the floating point output and the yet unused MSB's of the input are the // exponent of the floating point output." - int16_t v = Master->ExpTable[mix & 0xFF] + 1024; - v >>= mix >> 8; + int16_t v = (Master->ExpTable[mix & 0xFF] + 1024u) >> (mix >> 8u); v += v; if (negate) v = ~v; @@ -1083,8 +1044,6 @@ int16_t Opal::Operator::Output(uint16_t keyscalenum, uint32_t phase_step, int16_ return v; } - - //================================================================================================== // Trigger operator. //================================================================================================== @@ -1114,38 +1073,21 @@ void Opal::Operator::SetKeyOn(bool on) { } } - - //================================================================================================== // Enable amplitude vibrato. //================================================================================================== -void Opal::Operator::SetTremoloEnable(bool on) { - - TremoloEnable = on; -} - - +void Opal::Operator::SetTremoloEnable(bool on) { TremoloEnable = on; } //================================================================================================== // Enable frequency vibrato. //================================================================================================== -void Opal::Operator::SetVibratoEnable(bool on) { - - VibratoEnable = on; -} - - +void Opal::Operator::SetVibratoEnable(bool on) { VibratoEnable = on; } //================================================================================================== // Sets whether we release or sustain during the sustain phase of the envelope. 'true' is to // sustain, otherwise release. //================================================================================================== -void Opal::Operator::SetSustainMode(bool on) { - - SustainMode = on; -} - - +void Opal::Operator::SetSustainMode(bool on) { SustainMode = on; } //================================================================================================== // 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(); } - - //================================================================================================== // Multiplies the phase frequency. //================================================================================================== @@ -1172,34 +1112,20 @@ void Opal::Operator::SetFrequencyMultiplier(uint16_t scale) { FreqMultTimes2 = mul_times_2[scale & 15]; } - - //================================================================================================== // Attenuates output level towards higher pitch. //================================================================================================== void Opal::Operator::SetKeyScale(uint16_t scale) { - if (scale > 0) - KeyScaleShift = 3 - scale; - - // No scaling, ensure it has no effect - else - KeyScaleShift = 15; - + static constexpr uint8_t kslShift[4] = {8, 1, 2, 0}; + KeyScaleShift = kslShift[scale]; ComputeKeyScaleLevel(); } - - //================================================================================================== // Sets the output level (volume) of the operator. //================================================================================================== -void Opal::Operator::SetOutputLevel(uint16_t level) { - - OutputLevel = level * 4; -} - - +void Opal::Operator::SetOutputLevel(uint16_t level) { OutputLevel = level * 4; } //================================================================================================== // Operator attack rate. @@ -1211,8 +1137,6 @@ void Opal::Operator::SetAttackRate(uint16_t rate) { ComputeRates(); } - - //================================================================================================== // Operator decay rate. //================================================================================================== @@ -1223,8 +1147,6 @@ void Opal::Operator::SetDecayRate(uint16_t rate) { ComputeRates(); } - - //================================================================================================== // Operator sustain level. //================================================================================================== @@ -1234,8 +1156,6 @@ void Opal::Operator::SetSustainLevel(uint16_t level) { SustainLevel *= 16; } - - //================================================================================================== // Operator release rate. //================================================================================================== @@ -1246,17 +1166,10 @@ void Opal::Operator::SetReleaseRate(uint16_t rate) { ComputeRates(); } - - //================================================================================================== // Assign the waveform this operator will use. //================================================================================================== -void Opal::Operator::SetWaveform(uint16_t wave) { - - Waveform = wave & 7; -} - - +void Opal::Operator::SetWaveform(uint16_t wave) { Waveform = wave & 7; } //================================================================================================== // 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_low = combined_rate & 3; - AttackShift = rate_high < 12 ? 12 - rate_high : 0; + AttackShift = static_cast(rate_high < 12 ? 12 - rate_high : 0); AttackMask = (1 << AttackShift) - 1; AttackAdd = (rate_high < 12) ? 1 : 1 << (rate_high - 12); AttackTab = Master->RateTables[rate_low]; @@ -1290,7 +1203,7 @@ void Opal::Operator::ComputeRates() { rate_high = combined_rate >> 2; rate_low = combined_rate & 3; - DecayShift = rate_high < 12 ? 12 - rate_high : 0; + DecayShift = static_cast(rate_high < 12 ? 12 - rate_high : 0); DecayMask = (1 << DecayShift) - 1; DecayAdd = (rate_high < 12) ? 1 : 1 << (rate_high - 12); DecayTab = Master->RateTables[rate_low]; @@ -1299,21 +1212,20 @@ void Opal::Operator::ComputeRates() { rate_high = combined_rate >> 2; rate_low = combined_rate & 3; - ReleaseShift = rate_high < 12 ? 12 - rate_high : 0; + ReleaseShift = static_cast(rate_high < 12 ? 12 - rate_high : 0); ReleaseMask = (1 << ReleaseShift) - 1; ReleaseAdd = (rate_high < 12) ? 1 : 1 << (rate_high - 12); ReleaseTab = Master->RateTables[rate_low]; } - - //================================================================================================== // Compute the operator's key scale level. This changes based on the channel frequency/octave and // operator key scale value. //================================================================================================== 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, 8, 12, 16, 20, 24, 28, 32, 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, 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 uint16_t i = (Chan->GetOctave() << 4) | (Chan->GetFreq() >> 6); diff --git a/internal/c/parts/audio/extras/radv2/player20.cpp b/internal/c/parts/audio/extras/radv2/player20.cpp index b7fa75f1a..622c4f9b4 100644 --- a/internal/c/parts/audio/extras/radv2/player20.cpp +++ b/internal/c/parts/audio/extras/radv2/player20.cpp @@ -24,18 +24,14 @@ */ - - -#include - - +#include +#include +#include #ifndef RAD_DETECT_REPEATS -#define RAD_DETECT_REPEATS 0 +# define RAD_DETECT_REPEATS 0 #endif - - //================================================================================================== // RAD player class. //================================================================================================== @@ -43,169 +39,180 @@ class RADPlayer { // Various constants enum { - kTracks = 100, - kChannels = 9, - kTrackLines = 64, - kRiffTracks = 10, - kInstruments = 127, + kTracks = 100, + kChannels = 9, + kTrackLines = 64, + kRiffTracks = 10, + kInstruments = 127, - cmPortamentoUp = 0x1, - cmPortamentoDwn = 0x2, - cmToneSlide = 0x3, - cmToneVolSlide = 0x5, - cmVolSlide = 0xA, - cmSetVol = 0xC, - cmJumpToLine = 0xD, - cmSetSpeed = 0xF, - cmIgnore = ('I' - 55), - cmMultiplier = ('M' - 55), - cmRiff = ('R' - 55), - cmTranspose = ('T' - 55), - cmFeedback = ('U' - 55), - cmVolume = ('V' - 55), + cmPortamentoUp = 0x1, + cmPortamentoDwn = 0x2, + cmToneSlide = 0x3, + cmToneVolSlide = 0x5, + cmVolSlide = 0xA, + cmSetVol = 0xC, + cmJumpToLine = 0xD, + cmSetSpeed = 0xF, + cmIgnore = ('I' - 55), + cmMultiplier = ('M' - 55), + cmRiff = ('R' - 55), + cmTranspose = ('T' - 55), + cmFeedback = ('U' - 55), + cmVolume = ('V' - 55) }; - enum e_Source { - SNone, SRiff, SIRiff, - }; + enum e_Source { SNone, SRiff, SIRiff }; - enum { - fKeyOn = 1 << 0, - fKeyOff = 1 << 1, - fKeyedOn = 1 << 2, - }; + enum { fKeyOn = 1 << 0, fKeyOff = 1 << 1, fKeyedOn = 1 << 2 }; struct CInstrument { - uint8_t Feedback[2]; - uint8_t Panning[2]; - uint8_t Algorithm; - uint8_t Detune; - uint8_t Volume; - uint8_t RiffSpeed; - uint8_t * Riff; - uint8_t Operators[4][5]; + uint8_t Feedback[2]; + uint8_t Panning[2]; + uint8_t Algorithm; + uint8_t Detune; + uint8_t Volume; + uint8_t RiffSpeed; + uint8_t *Riff; + uint8_t Operators[4][5]; }; struct CEffects { - int8_t PortSlide; - int8_t VolSlide; - uint16_t ToneSlideFreq; - uint8_t ToneSlideOct; - uint8_t ToneSlideSpeed; - int8_t ToneSlideDir; + int8_t PortSlide; + int8_t VolSlide; + uint16_t ToneSlideFreq; + uint8_t ToneSlideOct; + uint8_t ToneSlideSpeed; + int8_t ToneSlideDir; }; struct CChannel { - uint8_t LastInstrument; - CInstrument * Instrument; - uint8_t Volume; - uint8_t DetuneA; - uint8_t DetuneB; - uint8_t KeyFlags; - uint16_t CurrFreq; - int8_t CurrOctave; - CEffects FX; + uint8_t LastInstrument; + CInstrument *Instrument; + uint8_t Volume; + uint8_t DetuneA; + uint8_t DetuneB; + uint8_t KeyFlags; + uint16_t CurrFreq; + int8_t CurrOctave; + CEffects FX; struct CRiff { - CEffects FX; - uint8_t * Track; - uint8_t * TrackStart; - uint8_t Line; - uint8_t Speed; - uint8_t SpeedCnt; - int8_t TransposeOctave; - int8_t TransposeNote; - uint8_t LastInstrument; + CEffects FX; + uint8_t *Track; + uint8_t *TrackStart; + uint8_t Line; + uint8_t Speed; + uint8_t SpeedCnt; + int8_t TransposeOctave; + int8_t TransposeNote; + uint8_t LastInstrument; } Riff, IRiff; }; - public: - RADPlayer() : Initialised(false) {} - void Init(const void *tune, void (*opl3)(void *, uint16_t, uint8_t), void *arg); - void Stop(); - bool Update(); - int GetHertz() const { return Hertz; } - int GetPlayTimeInSeconds() const { return PlayTime / Hertz; } - int GetTunePos() const { return Order; } - int GetTuneLength() const { return OrderListSize; } - int GetTuneLine() const { return Line; } - void SetMasterVolume(int vol) { MasterVol = vol; } - int GetMasterVolume() const { return MasterVol; } - int GetSpeed() const { return Speed; } + public: + RADPlayer() : Initialised(false) {} + void Init(const void *tune, void (*opl3)(void *, uint16_t, uint8_t), void *arg); + void Stop(); + bool Update(); + int GetHertz() const { return Hertz; } + int GetPlayTimeInSeconds() const { return PlayTime / Hertz; } + int GetTunePos() const { return Order; } + int GetTuneLength() const { return OrderListSize; } + int GetTuneLine() const { return Line; } + void SetMasterVolume(int vol) { MasterVol = vol; } + int GetMasterVolume() const { return MasterVol; } + 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 - uint32_t ComputeTotalTime(); + uint32_t ComputeTotalTime(); #endif - private: - bool UnpackNote(uint8_t *&s, uint8_t &last_instrument); - uint8_t * GetTrack(); - uint8_t * SkipToLine(uint8_t *trk, uint8_t linenum, bool chan_riff = false); - void PlayLine(); - void PlayNote(int channum, int8_t notenum, int8_t octave, uint16_t instnum, uint8_t cmd = 0, uint8_t param = 0, e_Source src = SNone, int op = 0); - void LoadInstrumentOPL3(int channum); - void PlayNoteOPL3(int channum, int8_t octave, int8_t note); - void ResetFX(CEffects *fx); - void TickRiff(int channum, CChannel::CRiff &riff, bool chan_riff); - void ContinueFX(int channum, CEffects *fx); - void SetVolume(int channum, uint8_t vol); - void GetSlideDir(int channum, CEffects *fx); - void LoadInstMultiplierOPL3(int channum, int op, uint8_t mult); - void LoadInstVolumeOPL3(int channum, int op, uint8_t vol); - void LoadInstFeedbackOPL3(int channum, int which, uint8_t fb); - void Portamento(uint16_t channum, CEffects *fx, int8_t amount, bool toneslide); - void Transpose(int8_t note, int8_t octave); - void SetOPL3(uint16_t reg, uint8_t val) { - OPL3Regs[reg] = val; - OPL3(OPL3Arg, reg, val); - } - uint8_t GetOPL3(uint16_t reg) const { - return OPL3Regs[reg]; - } + private: + bool UnpackNote(uint8_t *&s, uint8_t &last_instrument); + uint8_t *GetTrack(); + uint8_t *SkipToLine(uint8_t *trk, uint8_t linenum, bool chan_riff = false); + void PlayLine(); + void PlayNote(int channum, int8_t notenum, int8_t octave, uint16_t instnum, uint8_t cmd = 0, uint8_t param = 0, e_Source src = SNone, int op = 0); + void LoadInstrumentOPL3(int channum); + void PlayNoteOPL3(int channum, int8_t octave, int8_t note); + void ResetFX(CEffects *fx); + void TickRiff(int channum, CChannel::CRiff &riff, bool chan_riff); + void ContinueFX(int channum, CEffects *fx); + void SetVolume(int channum, uint8_t vol); + void GetSlideDir(int channum, CEffects *fx); + void LoadInstMultiplierOPL3(int channum, int op, uint8_t mult); + void LoadInstVolumeOPL3(int channum, int op, uint8_t vol); + void LoadInstFeedbackOPL3(int channum, int which, uint8_t fb); + void Portamento(uint16_t channum, CEffects *fx, int8_t amount, bool toneslide); + void Transpose(int8_t note, int8_t octave); + void SetOPL3(uint16_t reg, uint8_t val) { + OPL3Regs[reg] = val; + OPL3(OPL3Arg, reg, val); + } + uint8_t GetOPL3(uint16_t reg) const { return OPL3Regs[reg]; } - void (*OPL3)(void *, uint16_t, uint8_t); - void * OPL3Arg; - CInstrument Instruments[kInstruments]; - CChannel Channels[kChannels]; - uint32_t PlayTime; + /* 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 *OPL3Arg; + CInstrument Instruments[kInstruments]; + CChannel Channels[kChannels]; + uint32_t PlayTime; #if RAD_DETECT_REPEATS - uint32_t OrderMap[4]; - bool Repeating; + uint32_t OrderMap[4]; + bool Repeating; #endif - int16_t Hertz; - uint8_t * OrderList; - uint8_t * Tracks[kTracks]; - uint8_t * Riffs[kRiffTracks][kChannels]; - uint8_t * Track; - bool Initialised; - uint8_t Speed; - uint8_t OrderListSize; - uint8_t SpeedCnt; - uint8_t Order; - uint8_t Line; - int8_t Entrances; - uint8_t MasterVol; - int8_t LineJump; - uint8_t OPL3Regs[512]; + int16_t Hertz; + uint8_t *OrderList; + uint8_t *Tracks[kTracks]; + uint8_t *Riffs[kRiffTracks][kChannels]; + uint8_t *Track; + bool Initialised; + uint8_t Speed; + uint8_t OrderListSize; + uint8_t SpeedCnt; + uint8_t Order; + uint8_t Line; + int8_t Entrances; + uint8_t MasterVol; + int8_t LineJump; + uint8_t OPL3Regs[512]; - // Values exported by UnpackNote() - int8_t NoteNum; - int8_t OctaveNum; - uint8_t InstNum; - uint8_t EffectNum; - uint8_t Param; - bool LastNote; + // Values exported by UnpackNote() + int8_t NoteNum; + int8_t OctaveNum; + uint8_t InstNum; + uint8_t EffectNum; + uint8_t Param; + // Unused field, commented out to suppress warnings. + // bool LastNote; - static const int8_t NoteSize[]; - static const uint16_t ChanOffsets3[9], Chn2Offsets3[9]; - static const uint16_t NoteFreq[]; - static const uint16_t OpOffsets3[9][4]; - static const bool AlgCarriers[7][4]; + static const int8_t NoteSize[]; + static const uint16_t ChanOffsets3[9], Chn2Offsets3[9]; + static const uint16_t NoteFreq[]; + static const uint16_t OpOffsets3[9][4]; + static const bool AlgCarriers[7][4]; }; //-------------------------------------------------------------------------------------------------- -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::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 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::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}; +// clang-format off const uint16_t RADPlayer::OpOffsets3[9][4] = { { 0x00B, 0x008, 0x003, 0x000 }, { 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, 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 @@ -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) { Initialised = false; + Is10 = false; + LastPatternOrder = -1; // 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; 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++) 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 flags = *s++; @@ -284,7 +302,8 @@ void RADPlayer::Init(const void *tune, void (*opl3)(void *, uint16_t, uint8_t), break; // Skip instrument name - s += *s++; + uint8_t name_len = *(s++); + s += name_len; 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; } - - //================================================================================================== // Stop all sounds and reset the tune. Tune will play from the beginning again if you continue to // Update(). @@ -430,8 +447,6 @@ void RADPlayer::Stop() { } } - - //================================================================================================== // 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. @@ -469,18 +484,19 @@ bool RADPlayer::Update() { #endif } - - //================================================================================================== // Unpacks a single RAD note. //================================================================================================== bool RADPlayer::UnpackNote(uint8_t *&s, uint8_t &last_instrument) { + if (Is10) + return UnpackNote10(s); + uint8_t chanid = *s++; - InstNum = 0; + InstNum = 0; EffectNum = 0; - Param = 0; + Param = 0; // Unpack note data uint8_t note = 0; @@ -511,8 +527,6 @@ bool RADPlayer::UnpackNote(uint8_t *&s, uint8_t &last_instrument) { return ((chanid & 0x80) != 0); } - - //================================================================================================== // Get current track as indicated by order list. //================================================================================================== @@ -546,13 +560,14 @@ uint8_t *RADPlayer::GetTrack() { return Tracks[track_num]; } - - //================================================================================================== // 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) { + if (Is10) + return SkipToLine10(trk, linenum); + while (1) { uint8_t lineid = *trk; @@ -573,8 +588,6 @@ uint8_t *RADPlayer::SkipToLine(uint8_t *trk, uint8_t linenum, bool chan_riff) { return 0; } - - //================================================================================================== // Plays one line of current track and advances pointers. //================================================================================================== @@ -624,11 +637,14 @@ void RADPlayer::PlayLine() { // Move to next track in order list Order++; 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 // found, or -1 if none. @@ -667,45 +683,52 @@ void RADPlayer::PlayNote(int channum, int8_t notenum, int8_t octave, uint16_t in chan.Instrument = inst; // Ignore MIDI instruments + // NOTE: the vanilla player does this, meaning no effects or new riffs + // will trigger on this line. + /* if (inst->Algorithm == 7) { Entrances--; return; } + */ + if (inst->Algorithm < 7) { - LoadInstrumentOPL3(channum); + LoadInstrumentOPL3(channum); - // Bounce the channel - chan.KeyFlags |= fKeyOff | fKeyOn; + // Bounce the channel + chan.KeyFlags |= fKeyOff | fKeyOn; - ResetFX(&chan.IRiff.FX); + ResetFX(&chan.IRiff.FX); - if (src != SIRiff || inst != oldinst) { + if (src != SIRiff || inst != oldinst) { - // Instrument riff? - if (inst->Riff && inst->RiffSpeed > 0) { + // Instrument riff? + if (inst->Riff && inst->RiffSpeed > 0) { - chan.IRiff.Track = chan.IRiff.TrackStart = inst->Riff; - chan.IRiff.Line = 0; - chan.IRiff.Speed = inst->RiffSpeed; - chan.IRiff.LastInstrument = 0; + chan.IRiff.Track = chan.IRiff.TrackStart = inst->Riff; + chan.IRiff.Line = 0; + chan.IRiff.Speed = inst->RiffSpeed; + chan.IRiff.LastInstrument = 0; - // Note given with riff command is used to transpose the riff - if (notenum >= 1 && notenum <= 12) { - chan.IRiff.TransposeOctave = octave; - chan.IRiff.TransposeNote = notenum; - transposing = true; - } else { - chan.IRiff.TransposeOctave = 3; - chan.IRiff.TransposeNote = 12; - } + // Note given with riff command is used to transpose the riff + if (notenum >= 1 && notenum <= 12) { + chan.IRiff.TransposeOctave = octave; + chan.IRiff.TransposeNote = notenum; + transposing = true; + } else { + chan.IRiff.TransposeOctave = 3; + chan.IRiff.TransposeNote = 12; + } - // Do first tick of riff - chan.IRiff.SpeedCnt = 1; - TickRiff(channum, chan.IRiff, false); + // Do first tick of riff + chan.IRiff.SpeedCnt = 1; + TickRiff(channum, chan.IRiff, false); - } else - chan.IRiff.SpeedCnt = 0; - } + } else + chan.IRiff.SpeedCnt = 0; + } + } else + chan.Instrument = 0; } // Starting a channel riff? @@ -755,89 +778,87 @@ void RADPlayer::PlayNote(int channum, int8_t notenum, int8_t octave, uint16_t in // Process effect switch (cmd) { - case cmSetVol: - SetVolume(channum, param); - break; + case cmSetVol: + SetVolume(channum, param); + break; - case cmSetSpeed: - if (src == SNone) { - Speed = param; - SpeedCnt = param; - } else if (src == SRiff) { - chan.Riff.Speed = param; - chan.Riff.SpeedCnt = param; - } else if (src == SIRiff) { - chan.IRiff.Speed = param; - chan.IRiff.SpeedCnt = param; - } - break; - - case cmPortamentoUp: - fx->PortSlide = param; - break; - - case cmPortamentoDwn: - fx->PortSlide = -int8_t(param); - break; - - case cmToneVolSlide: - case cmVolSlide: { - int8_t val = param; - if (val >= 50) - val = -(val - 50); - fx->VolSlide = val; - if (cmd != cmToneVolSlide) - break; + case cmSetSpeed: + if (src == SNone) { + Speed = param; + SpeedCnt = param; + } else if (src == SRiff) { + chan.Riff.Speed = param; + chan.Riff.SpeedCnt = param; + } else if (src == SIRiff) { + chan.IRiff.Speed = param; + chan.IRiff.SpeedCnt = param; } + break; + + case cmPortamentoUp: + fx->PortSlide = param; + break; + + case cmPortamentoDwn: + fx->PortSlide = -int8_t(param); + break; + + case cmToneVolSlide: + case cmVolSlide: { + int8_t val = param; + if (val >= 50) + val = -(val - 50); + fx->VolSlide = val; + if (cmd != cmToneVolSlide) + break; + } // Fall through! - case cmToneSlide: { -toneslide: - uint8_t speed = param; - if (speed) - fx->ToneSlideSpeed = speed; - GetSlideDir(channum, fx); - break; - } - - case cmJumpToLine: { - if (param >= kTrackLines) - break; - - // Note: jump commands in riffs are checked for within TickRiff() - if (src == SNone) - LineJump = param; - - break; - } - - case cmMultiplier: { - if (src == SIRiff) - LoadInstMultiplierOPL3(channum, op, param); - break; - } - - case cmVolume: { - if (src == SIRiff) - LoadInstVolumeOPL3(channum, op, param); - break; - } - - case cmFeedback: { - if (src == SIRiff) { - uint8_t which = param / 10; - uint8_t fb = param % 10; - LoadInstFeedbackOPL3(channum, which, fb); - } + case cmToneSlide: { + toneslide: + uint8_t speed = param; + if (speed) + fx->ToneSlideSpeed = speed; + GetSlideDir(channum, fx); + break; + } + + case cmJumpToLine: { + if (param >= kTrackLines) break; + + // Note: jump commands in riffs are checked for within TickRiff() + if (src == SNone) + LineJump = param; + + break; + } + + case cmMultiplier: { + if (src == SIRiff) + LoadInstMultiplierOPL3(channum, op, param); + break; + } + + case cmVolume: { + if (src == SIRiff) + LoadInstVolumeOPL3(channum, op, param); + break; + } + + case cmFeedback: { + if (src == SIRiff) { + uint8_t which = param / 10; + uint8_t fb = param % 10; + LoadInstFeedbackOPL3(channum, which, fb); } + break; + } } Entrances--; } - - //================================================================================================== // Sets the OPL3 registers for a given instrument. //================================================================================================== @@ -866,7 +887,7 @@ void RADPlayer::LoadInstrumentOPL3(int channum) { // Load the operators for (int i = 0; i < 4; i++) { - static const uint8_t blank[] = { 0, 0x3F, 0, 0xF0, 0 }; + static const uint8_t blank[] = {0, 0x3F, 0, 0xF0, 0}; const uint8_t *op = (alg < 2 && i >= 2) ? blank : inst->Operators[i]; uint16_t reg = OpOffsets3[channum][i]; @@ -886,8 +907,6 @@ void RADPlayer::LoadInstrumentOPL3(int channum) { } } - - //================================================================================================== // 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)); } - - //================================================================================================== // Prepare FX for new line. //================================================================================================== @@ -945,8 +962,6 @@ void RADPlayer::ResetFX(CEffects *fx) { fx->ToneSlideDir = 0; } - - //================================================================================================== // 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) return; + lineid = 0; // silence warning UnpackNote(trk, lineid); // lineid is just a dummy here if (EffectNum == cmJumpToLine && Param < kTrackLines) { 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). //================================================================================================== @@ -1036,8 +1050,6 @@ void RADPlayer::ContinueFX(int channum, CEffects *fx) { Portamento(channum, fx, fx->ToneSlideDir, true); } - - //================================================================================================== // Sets the volume of given channel. //================================================================================================== @@ -1072,8 +1084,6 @@ void RADPlayer::SetVolume(int channum, uint8_t vol) { } } - - //================================================================================================== // Starts a tone-slide. //================================================================================================== @@ -1101,8 +1111,6 @@ void RADPlayer::GetSlideDir(int channum, CEffects *fx) { fx->ToneSlideDir = speed; } - - //================================================================================================== // 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)); } - - //================================================================================================== // 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)); } - - //================================================================================================== // 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 // 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)); } - - //================================================================================================== // Transpose the note returned by UnpackNote(). // 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 // prior to initial playback. @@ -1264,3 +1262,197 @@ uint32_t RADPlayer::ComputeTotalTime() { return total / Hertz; } #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; +} diff --git a/internal/c/parts/audio/extras/radv2/validate20.cpp b/internal/c/parts/audio/extras/radv2/validate20.cpp index de6eb31c0..b43f05974 100644 --- a/internal/c/parts/audio/extras/radv2/validate20.cpp +++ b/internal/c/parts/audio/extras/radv2/validate20.cpp @@ -10,11 +10,7 @@ */ - - -#include - - +#include //================================================================================================== // 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_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. @@ -86,7 +213,7 @@ static const char *RADCheckPattern(const uint8_t *&s, const uint8_t *e, bool rif return g_RADPattTruncated; uint8_t note = *s++; uint8_t notenum = note & 15; - uint8_t octave = (note >> 4) & 7; + // uint8_t octave = (note >> 4) & 7; if (notenum == 0 || notenum == 13 || notenum == 14) return g_RADPattBadNoteNum; } @@ -121,14 +248,16 @@ static const char *RADCheckPattern(const uint8_t *&s, const uint8_t *e, bool rif return 0; } + //-------------------------------------------------------------------------------------------------- const char *RADValidate(const void *data, size_t data_size) { const uint8_t *s = (const uint8_t *)data; const uint8_t *e = s + data_size; + uint8_t version; // Check header - if (data_size < 16) + if (data_size < 17) return g_RADNotATuneFile; const char *hdrtxt = "RAD by REALiTY!!"; @@ -137,7 +266,10 @@ const char *RADValidate(const void *data, size_t data_size) { return g_RADNotATuneFile; // Check version - if (s >= e || *s++ != 0x21) + version = *(s++); + if (version == 0x10) + return RADValidate10(data, e); + if (version != 0x21) return g_RADNotAVersion21Tune; // Check flags @@ -148,7 +280,8 @@ const char *RADValidate(const void *data, size_t data_size) { if (flags & 0x80) 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) return g_RADTruncated; 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; if (s[2] >> 4) 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 { diff --git a/internal/c/parts/audio/miniaudio.h b/internal/c/parts/audio/miniaudio.h index 66ffdc65c..facf124cd 100644 --- a/internal/c/parts/audio/miniaudio.h +++ b/internal/c/parts/audio/miniaudio.h @@ -1,6 +1,6 @@ /* Audio playback and capture library. Choice of public domain or MIT-0. See license statements at the end of this file. -miniaudio - v0.11.13 - 2023-03-23 +miniaudio - v0.11.14 - 2023-03-29 David Reid - mackron@gmail.com @@ -398,13 +398,13 @@ the be started and/or stopped at a specific time. This can be done with the foll ``` The start/stop time needs to be specified based on the absolute timer which is controlled by the -engine. The current global time time in PCM frames can be retrieved with `ma_engine_get_time()`. -The engine's global time can be changed with `ma_engine_set_time()` for synchronization purposes if -required. Note that scheduling a start time still requires an explicit call to `ma_sound_start()` -before anything will play: +engine. The current global time time in PCM frames can be retrieved with +`ma_engine_get_time_in_pcm_frames()`. The engine's global time can be changed with +`ma_engine_set_time_in_pcm_frames()` for synchronization purposes if required. Note that scheduling +a start time still requires an explicit call to `ma_sound_start()` before anything will play: ```c - ma_sound_set_start_time_in_pcm_frames(&sound, ma_engine_get_time(&engine) + (ma_engine_get_sample_rate(&engine) * 2); + ma_sound_set_start_time_in_pcm_frames(&sound, ma_engine_get_time_in_pcm_frames(&engine) + (ma_engine_get_sample_rate(&engine) * 2); ma_sound_start(&sound); ``` @@ -946,7 +946,7 @@ base object (`ma_data_source_base`): // Retrieve the length in PCM frames here. Return MA_NOT_IMPLEMENTED and set *pLength to 0 if there is no notion of a length or if the length is unknown. } - static g_my_data_source_vtable = + static ma_data_source_vtable g_my_data_source_vtable = { my_data_source_read, my_data_source_seek, @@ -1429,19 +1429,19 @@ can be useful to schedule a sound to start or stop: ```c // Start the sound in 1 second from now. - ma_sound_set_start_time_in_pcm_frames(&sound, ma_engine_get_time(&engine) + (ma_engine_get_sample_rate(&engine) * 1)); + ma_sound_set_start_time_in_pcm_frames(&sound, ma_engine_get_time_in_pcm_frames(&engine) + (ma_engine_get_sample_rate(&engine) * 1)); // Stop the sound in 2 seconds from now. - ma_sound_set_stop_time_in_pcm_frames(&sound, ma_engine_get_time(&engine) + (ma_engine_get_sample_rate(&engine) * 2)); + ma_sound_set_stop_time_in_pcm_frames(&sound, ma_engine_get_time_in_pcm_frames(&engine) + (ma_engine_get_sample_rate(&engine) * 2)); ``` Note that scheduling a start time still requires an explicit call to `ma_sound_start()` before anything will play. The time is specified in global time which is controlled by the engine. You can get the engine's -current time with `ma_engine_get_time()`. The engine's global time is incremented automatically as -audio data is read, but it can be reset with `ma_engine_set_time()` in case it needs to be -resynchronized for some reason. +current time with `ma_engine_get_time_in_pcm_frames()`. The engine's global time is incremented +automatically as audio data is read, but it can be reset with `ma_engine_set_time_in_pcm_frames()` +in case it needs to be resynchronized for some reason. To determine whether or not a sound is currently playing, use `ma_sound_is_playing()`. This will take the scheduled start and stop times into account. @@ -3719,7 +3719,7 @@ extern "C" { #define MA_VERSION_MAJOR 0 #define MA_VERSION_MINOR 11 -#define MA_VERSION_REVISION 13 +#define MA_VERSION_REVISION 14 #define MA_VERSION_STRING MA_XSTRINGIFY(MA_VERSION_MAJOR) "." MA_XSTRINGIFY(MA_VERSION_MINOR) "." MA_XSTRINGIFY(MA_VERSION_REVISION) #if defined(_MSC_VER) && !defined(__clang__) @@ -3799,7 +3799,18 @@ typedef double ma_double; typedef void* ma_handle; typedef void* ma_ptr; + +/* +ma_proc is annoying because when compiling with GCC we get pendantic warnings about converting +between `void*` and `void (*)()`. We can't use `void (*)()` with MSVC however, because we'll get +warning C4191 about "type cast between incompatible function types". To work around this I'm going +to use a different data type depending on the compiler. +*/ +#if defined(__GNUC__) +typedef void (*ma_proc)(void); +#else typedef void* ma_proc; +#endif #if defined(_MSC_VER) && !defined(_WCHAR_T_DEFINED) typedef ma_uint16 wchar_t; @@ -3849,31 +3860,39 @@ typedef ma_uint16 wchar_t; typedef union ma_pthread_cond_t { char __data[48]; ma_uint64 __alignment; } ma_pthread_cond_t; #endif - #ifdef __unix__ + #if defined(__unix__) #define MA_UNIX - #ifdef __ORBIS__ - #define MA_ORBIS - #elif defined(__PROSPERO__) - #define MA_PROSPERO - #elif defined(__DragonFly__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) - #define MA_BSD - #endif #endif - #ifdef __linux__ + #if defined(__linux__) #define MA_LINUX #endif - #ifdef __APPLE__ + #if defined(__APPLE__) #define MA_APPLE #endif - #ifdef __ANDROID__ + #if defined(__DragonFly__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) + #define MA_BSD + #endif + #if defined(__ANDROID__) #define MA_ANDROID #endif - #ifdef __EMSCRIPTEN__ + #if defined(__EMSCRIPTEN__) #define MA_EMSCRIPTEN #endif + #if defined(__ORBIS__) + #define MA_ORBIS + #endif + #if defined(__PROSPERO__) + #define MA_PROSPERO + #endif #if defined(__NX__) #define MA_NX #endif + #if defined(__BEOS__) || defined(__HAIKU__) + #define MA_BEOS + #endif + #if defined(__HAIKU__) + #define MA_HAIKU + #endif #endif #if defined(__has_c_attribute) @@ -5721,6 +5740,197 @@ MA_API ma_uint64 ma_convert_frames(void* pOut, ma_uint64 frameCountOut, ma_forma MA_API ma_uint64 ma_convert_frames_ex(void* pOut, ma_uint64 frameCountOut, const void* pIn, ma_uint64 frameCountIn, const ma_data_converter_config* pConfig); +/************************************************************************************************************************************************************ + +Data Source + +************************************************************************************************************************************************************/ +typedef void ma_data_source; + +#define MA_DATA_SOURCE_SELF_MANAGED_RANGE_AND_LOOP_POINT 0x00000001 + +typedef struct +{ + ma_result (* onRead)(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead); + ma_result (* onSeek)(ma_data_source* pDataSource, ma_uint64 frameIndex); + ma_result (* onGetDataFormat)(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap); + ma_result (* onGetCursor)(ma_data_source* pDataSource, ma_uint64* pCursor); + ma_result (* onGetLength)(ma_data_source* pDataSource, ma_uint64* pLength); + ma_result (* onSetLooping)(ma_data_source* pDataSource, ma_bool32 isLooping); + ma_uint32 flags; +} ma_data_source_vtable; + +typedef ma_data_source* (* ma_data_source_get_next_proc)(ma_data_source* pDataSource); + +typedef struct +{ + const ma_data_source_vtable* vtable; +} ma_data_source_config; + +MA_API ma_data_source_config ma_data_source_config_init(void); + + +typedef struct +{ + const ma_data_source_vtable* vtable; + ma_uint64 rangeBegInFrames; + ma_uint64 rangeEndInFrames; /* Set to -1 for unranged (default). */ + ma_uint64 loopBegInFrames; /* Relative to rangeBegInFrames. */ + ma_uint64 loopEndInFrames; /* Relative to rangeBegInFrames. Set to -1 for the end of the range. */ + ma_data_source* pCurrent; /* When non-NULL, the data source being initialized will act as a proxy and will route all operations to pCurrent. Used in conjunction with pNext/onGetNext for seamless chaining. */ + ma_data_source* pNext; /* When set to NULL, onGetNext will be used. */ + ma_data_source_get_next_proc onGetNext; /* Will be used when pNext is NULL. If both are NULL, no next will be used. */ + MA_ATOMIC(4, ma_bool32) isLooping; +} ma_data_source_base; + +MA_API ma_result ma_data_source_init(const ma_data_source_config* pConfig, ma_data_source* pDataSource); +MA_API void ma_data_source_uninit(ma_data_source* pDataSource); +MA_API ma_result ma_data_source_read_pcm_frames(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead); /* Must support pFramesOut = NULL in which case a forward seek should be performed. */ +MA_API ma_result ma_data_source_seek_pcm_frames(ma_data_source* pDataSource, ma_uint64 frameCount, ma_uint64* pFramesSeeked); /* Can only seek forward. Equivalent to ma_data_source_read_pcm_frames(pDataSource, NULL, frameCount, &framesRead); */ +MA_API ma_result ma_data_source_seek_to_pcm_frame(ma_data_source* pDataSource, ma_uint64 frameIndex); +MA_API ma_result ma_data_source_get_data_format(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap); +MA_API ma_result ma_data_source_get_cursor_in_pcm_frames(ma_data_source* pDataSource, ma_uint64* pCursor); +MA_API ma_result ma_data_source_get_length_in_pcm_frames(ma_data_source* pDataSource, ma_uint64* pLength); /* Returns MA_NOT_IMPLEMENTED if the length is unknown or cannot be determined. Decoders can return this. */ +MA_API ma_result ma_data_source_get_cursor_in_seconds(ma_data_source* pDataSource, float* pCursor); +MA_API ma_result ma_data_source_get_length_in_seconds(ma_data_source* pDataSource, float* pLength); +MA_API ma_result ma_data_source_set_looping(ma_data_source* pDataSource, ma_bool32 isLooping); +MA_API ma_bool32 ma_data_source_is_looping(const ma_data_source* pDataSource); +MA_API ma_result ma_data_source_set_range_in_pcm_frames(ma_data_source* pDataSource, ma_uint64 rangeBegInFrames, ma_uint64 rangeEndInFrames); +MA_API void ma_data_source_get_range_in_pcm_frames(const ma_data_source* pDataSource, ma_uint64* pRangeBegInFrames, ma_uint64* pRangeEndInFrames); +MA_API ma_result ma_data_source_set_loop_point_in_pcm_frames(ma_data_source* pDataSource, ma_uint64 loopBegInFrames, ma_uint64 loopEndInFrames); +MA_API void ma_data_source_get_loop_point_in_pcm_frames(const ma_data_source* pDataSource, ma_uint64* pLoopBegInFrames, ma_uint64* pLoopEndInFrames); +MA_API ma_result ma_data_source_set_current(ma_data_source* pDataSource, ma_data_source* pCurrentDataSource); +MA_API ma_data_source* ma_data_source_get_current(const ma_data_source* pDataSource); +MA_API ma_result ma_data_source_set_next(ma_data_source* pDataSource, ma_data_source* pNextDataSource); +MA_API ma_data_source* ma_data_source_get_next(const ma_data_source* pDataSource); +MA_API ma_result ma_data_source_set_next_callback(ma_data_source* pDataSource, ma_data_source_get_next_proc onGetNext); +MA_API ma_data_source_get_next_proc ma_data_source_get_next_callback(const ma_data_source* pDataSource); + + +typedef struct +{ + ma_data_source_base ds; + ma_format format; + ma_uint32 channels; + ma_uint32 sampleRate; + ma_uint64 cursor; + ma_uint64 sizeInFrames; + const void* pData; +} ma_audio_buffer_ref; + +MA_API ma_result ma_audio_buffer_ref_init(ma_format format, ma_uint32 channels, const void* pData, ma_uint64 sizeInFrames, ma_audio_buffer_ref* pAudioBufferRef); +MA_API void ma_audio_buffer_ref_uninit(ma_audio_buffer_ref* pAudioBufferRef); +MA_API ma_result ma_audio_buffer_ref_set_data(ma_audio_buffer_ref* pAudioBufferRef, const void* pData, ma_uint64 sizeInFrames); +MA_API ma_uint64 ma_audio_buffer_ref_read_pcm_frames(ma_audio_buffer_ref* pAudioBufferRef, void* pFramesOut, ma_uint64 frameCount, ma_bool32 loop); +MA_API ma_result ma_audio_buffer_ref_seek_to_pcm_frame(ma_audio_buffer_ref* pAudioBufferRef, ma_uint64 frameIndex); +MA_API ma_result ma_audio_buffer_ref_map(ma_audio_buffer_ref* pAudioBufferRef, void** ppFramesOut, ma_uint64* pFrameCount); +MA_API ma_result ma_audio_buffer_ref_unmap(ma_audio_buffer_ref* pAudioBufferRef, ma_uint64 frameCount); /* Returns MA_AT_END if the end has been reached. This should be considered successful. */ +MA_API ma_bool32 ma_audio_buffer_ref_at_end(const ma_audio_buffer_ref* pAudioBufferRef); +MA_API ma_result ma_audio_buffer_ref_get_cursor_in_pcm_frames(const ma_audio_buffer_ref* pAudioBufferRef, ma_uint64* pCursor); +MA_API ma_result ma_audio_buffer_ref_get_length_in_pcm_frames(const ma_audio_buffer_ref* pAudioBufferRef, ma_uint64* pLength); +MA_API ma_result ma_audio_buffer_ref_get_available_frames(const ma_audio_buffer_ref* pAudioBufferRef, ma_uint64* pAvailableFrames); + + + +typedef struct +{ + ma_format format; + ma_uint32 channels; + ma_uint32 sampleRate; + ma_uint64 sizeInFrames; + const void* pData; /* If set to NULL, will allocate a block of memory for you. */ + ma_allocation_callbacks allocationCallbacks; +} ma_audio_buffer_config; + +MA_API ma_audio_buffer_config ma_audio_buffer_config_init(ma_format format, ma_uint32 channels, ma_uint64 sizeInFrames, const void* pData, const ma_allocation_callbacks* pAllocationCallbacks); + +typedef struct +{ + ma_audio_buffer_ref ref; + ma_allocation_callbacks allocationCallbacks; + ma_bool32 ownsData; /* Used to control whether or not miniaudio owns the data buffer. If set to true, pData will be freed in ma_audio_buffer_uninit(). */ + ma_uint8 _pExtraData[1]; /* For allocating a buffer with the memory located directly after the other memory of the structure. */ +} ma_audio_buffer; + +MA_API ma_result ma_audio_buffer_init(const ma_audio_buffer_config* pConfig, ma_audio_buffer* pAudioBuffer); +MA_API ma_result ma_audio_buffer_init_copy(const ma_audio_buffer_config* pConfig, ma_audio_buffer* pAudioBuffer); +MA_API ma_result ma_audio_buffer_alloc_and_init(const ma_audio_buffer_config* pConfig, ma_audio_buffer** ppAudioBuffer); /* Always copies the data. Doesn't make sense to use this otherwise. Use ma_audio_buffer_uninit_and_free() to uninit. */ +MA_API void ma_audio_buffer_uninit(ma_audio_buffer* pAudioBuffer); +MA_API void ma_audio_buffer_uninit_and_free(ma_audio_buffer* pAudioBuffer); +MA_API ma_uint64 ma_audio_buffer_read_pcm_frames(ma_audio_buffer* pAudioBuffer, void* pFramesOut, ma_uint64 frameCount, ma_bool32 loop); +MA_API ma_result ma_audio_buffer_seek_to_pcm_frame(ma_audio_buffer* pAudioBuffer, ma_uint64 frameIndex); +MA_API ma_result ma_audio_buffer_map(ma_audio_buffer* pAudioBuffer, void** ppFramesOut, ma_uint64* pFrameCount); +MA_API ma_result ma_audio_buffer_unmap(ma_audio_buffer* pAudioBuffer, ma_uint64 frameCount); /* Returns MA_AT_END if the end has been reached. This should be considered successful. */ +MA_API ma_bool32 ma_audio_buffer_at_end(const ma_audio_buffer* pAudioBuffer); +MA_API ma_result ma_audio_buffer_get_cursor_in_pcm_frames(const ma_audio_buffer* pAudioBuffer, ma_uint64* pCursor); +MA_API ma_result ma_audio_buffer_get_length_in_pcm_frames(const ma_audio_buffer* pAudioBuffer, ma_uint64* pLength); +MA_API ma_result ma_audio_buffer_get_available_frames(const ma_audio_buffer* pAudioBuffer, ma_uint64* pAvailableFrames); + + +/* +Paged Audio Buffer +================== +A paged audio buffer is made up of a linked list of pages. It's expandable, but not shrinkable. It +can be used for cases where audio data is streamed in asynchronously while allowing data to be read +at the same time. + +This is lock-free, but not 100% thread safe. You can append a page and read from the buffer across +simultaneously across different threads, however only one thread at a time can append, and only one +thread at a time can read and seek. +*/ +typedef struct ma_paged_audio_buffer_page ma_paged_audio_buffer_page; +struct ma_paged_audio_buffer_page +{ + MA_ATOMIC(MA_SIZEOF_PTR, ma_paged_audio_buffer_page*) pNext; + ma_uint64 sizeInFrames; + ma_uint8 pAudioData[1]; +}; + +typedef struct +{ + ma_format format; + ma_uint32 channels; + ma_paged_audio_buffer_page head; /* Dummy head for the lock-free algorithm. Always has a size of 0. */ + MA_ATOMIC(MA_SIZEOF_PTR, ma_paged_audio_buffer_page*) pTail; /* Never null. Initially set to &head. */ +} ma_paged_audio_buffer_data; + +MA_API ma_result ma_paged_audio_buffer_data_init(ma_format format, ma_uint32 channels, ma_paged_audio_buffer_data* pData); +MA_API void ma_paged_audio_buffer_data_uninit(ma_paged_audio_buffer_data* pData, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_paged_audio_buffer_page* ma_paged_audio_buffer_data_get_head(ma_paged_audio_buffer_data* pData); +MA_API ma_paged_audio_buffer_page* ma_paged_audio_buffer_data_get_tail(ma_paged_audio_buffer_data* pData); +MA_API ma_result ma_paged_audio_buffer_data_get_length_in_pcm_frames(ma_paged_audio_buffer_data* pData, ma_uint64* pLength); +MA_API ma_result ma_paged_audio_buffer_data_allocate_page(ma_paged_audio_buffer_data* pData, ma_uint64 pageSizeInFrames, const void* pInitialData, const ma_allocation_callbacks* pAllocationCallbacks, ma_paged_audio_buffer_page** ppPage); +MA_API ma_result ma_paged_audio_buffer_data_free_page(ma_paged_audio_buffer_data* pData, ma_paged_audio_buffer_page* pPage, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_result ma_paged_audio_buffer_data_append_page(ma_paged_audio_buffer_data* pData, ma_paged_audio_buffer_page* pPage); +MA_API ma_result ma_paged_audio_buffer_data_allocate_and_append_page(ma_paged_audio_buffer_data* pData, ma_uint32 pageSizeInFrames, const void* pInitialData, const ma_allocation_callbacks* pAllocationCallbacks); + + +typedef struct +{ + ma_paged_audio_buffer_data* pData; /* Must not be null. */ +} ma_paged_audio_buffer_config; + +MA_API ma_paged_audio_buffer_config ma_paged_audio_buffer_config_init(ma_paged_audio_buffer_data* pData); + + +typedef struct +{ + ma_data_source_base ds; + ma_paged_audio_buffer_data* pData; /* Audio data is read from here. Cannot be null. */ + ma_paged_audio_buffer_page* pCurrent; + ma_uint64 relativeCursor; /* Relative to the current page. */ + ma_uint64 absoluteCursor; +} ma_paged_audio_buffer; + +MA_API ma_result ma_paged_audio_buffer_init(const ma_paged_audio_buffer_config* pConfig, ma_paged_audio_buffer* pPagedAudioBuffer); +MA_API void ma_paged_audio_buffer_uninit(ma_paged_audio_buffer* pPagedAudioBuffer); +MA_API ma_result ma_paged_audio_buffer_read_pcm_frames(ma_paged_audio_buffer* pPagedAudioBuffer, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead); /* Returns MA_AT_END if no more pages available. */ +MA_API ma_result ma_paged_audio_buffer_seek_to_pcm_frame(ma_paged_audio_buffer* pPagedAudioBuffer, ma_uint64 frameIndex); +MA_API ma_result ma_paged_audio_buffer_get_cursor_in_pcm_frames(ma_paged_audio_buffer* pPagedAudioBuffer, ma_uint64* pCursor); +MA_API ma_result ma_paged_audio_buffer_get_length_in_pcm_frames(ma_paged_audio_buffer* pPagedAudioBuffer, ma_uint64* pLength); + + + /************************************************************************************************************************************************************ Ring Buffer @@ -5760,9 +5970,11 @@ MA_API void* ma_rb_get_subbuffer_ptr(ma_rb* pRB, size_t subbufferIndex, void* pB typedef struct { + ma_data_source_base ds; ma_rb rb; ma_format format; ma_uint32 channels; + ma_uint32 sampleRate; /* Not required for the ring buffer itself, but useful for associating the data with some sample rate, particularly for data sources. */ } ma_pcm_rb; MA_API ma_result ma_pcm_rb_init_ex(ma_format format, ma_uint32 channels, ma_uint32 subbufferSizeInFrames, ma_uint32 subbufferCount, ma_uint32 subbufferStrideInFrames, void* pOptionalPreallocatedBuffer, const ma_allocation_callbacks* pAllocationCallbacks, ma_pcm_rb* pRB); @@ -5782,6 +5994,10 @@ MA_API ma_uint32 ma_pcm_rb_get_subbuffer_size(ma_pcm_rb* pRB); MA_API ma_uint32 ma_pcm_rb_get_subbuffer_stride(ma_pcm_rb* pRB); MA_API ma_uint32 ma_pcm_rb_get_subbuffer_offset(ma_pcm_rb* pRB, ma_uint32 subbufferIndex); MA_API void* ma_pcm_rb_get_subbuffer_ptr(ma_pcm_rb* pRB, ma_uint32 subbufferIndex, void* pBuffer); +MA_API ma_format ma_pcm_rb_get_format(const ma_pcm_rb* pRB); +MA_API ma_uint32 ma_pcm_rb_get_channels(const ma_pcm_rb* pRB); +MA_API ma_uint32 ma_pcm_rb_get_sample_rate(const ma_pcm_rb* pRB); +MA_API void ma_pcm_rb_set_sample_rate(ma_pcm_rb* pRB, ma_uint32 sampleRate); /* @@ -9542,195 +9758,6 @@ This will run on an optimized path when the volume is equal to 1. MA_API ma_result ma_mix_pcm_frames_f32(float* pDst, const float* pSrc, ma_uint64 frameCount, ma_uint32 channels, float volume); -/************************************************************************************************** - -Data Source - -**************************************************************************************************/ -typedef void ma_data_source; - -#define MA_DATA_SOURCE_SELF_MANAGED_RANGE_AND_LOOP_POINT 0x00000001 - -typedef struct -{ - ma_result (* onRead)(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead); - ma_result (* onSeek)(ma_data_source* pDataSource, ma_uint64 frameIndex); - ma_result (* onGetDataFormat)(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap); - ma_result (* onGetCursor)(ma_data_source* pDataSource, ma_uint64* pCursor); - ma_result (* onGetLength)(ma_data_source* pDataSource, ma_uint64* pLength); - ma_result (* onSetLooping)(ma_data_source* pDataSource, ma_bool32 isLooping); - ma_uint32 flags; -} ma_data_source_vtable; - -typedef ma_data_source* (* ma_data_source_get_next_proc)(ma_data_source* pDataSource); - -typedef struct -{ - const ma_data_source_vtable* vtable; -} ma_data_source_config; - -MA_API ma_data_source_config ma_data_source_config_init(void); - - -typedef struct -{ - const ma_data_source_vtable* vtable; - ma_uint64 rangeBegInFrames; - ma_uint64 rangeEndInFrames; /* Set to -1 for unranged (default). */ - ma_uint64 loopBegInFrames; /* Relative to rangeBegInFrames. */ - ma_uint64 loopEndInFrames; /* Relative to rangeBegInFrames. Set to -1 for the end of the range. */ - ma_data_source* pCurrent; /* When non-NULL, the data source being initialized will act as a proxy and will route all operations to pCurrent. Used in conjunction with pNext/onGetNext for seamless chaining. */ - ma_data_source* pNext; /* When set to NULL, onGetNext will be used. */ - ma_data_source_get_next_proc onGetNext; /* Will be used when pNext is NULL. If both are NULL, no next will be used. */ - MA_ATOMIC(4, ma_bool32) isLooping; -} ma_data_source_base; - -MA_API ma_result ma_data_source_init(const ma_data_source_config* pConfig, ma_data_source* pDataSource); -MA_API void ma_data_source_uninit(ma_data_source* pDataSource); -MA_API ma_result ma_data_source_read_pcm_frames(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead); /* Must support pFramesOut = NULL in which case a forward seek should be performed. */ -MA_API ma_result ma_data_source_seek_pcm_frames(ma_data_source* pDataSource, ma_uint64 frameCount, ma_uint64* pFramesSeeked); /* Can only seek forward. Equivalent to ma_data_source_read_pcm_frames(pDataSource, NULL, frameCount, &framesRead); */ -MA_API ma_result ma_data_source_seek_to_pcm_frame(ma_data_source* pDataSource, ma_uint64 frameIndex); -MA_API ma_result ma_data_source_get_data_format(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap); -MA_API ma_result ma_data_source_get_cursor_in_pcm_frames(ma_data_source* pDataSource, ma_uint64* pCursor); -MA_API ma_result ma_data_source_get_length_in_pcm_frames(ma_data_source* pDataSource, ma_uint64* pLength); /* Returns MA_NOT_IMPLEMENTED if the length is unknown or cannot be determined. Decoders can return this. */ -MA_API ma_result ma_data_source_get_cursor_in_seconds(ma_data_source* pDataSource, float* pCursor); -MA_API ma_result ma_data_source_get_length_in_seconds(ma_data_source* pDataSource, float* pLength); -MA_API ma_result ma_data_source_set_looping(ma_data_source* pDataSource, ma_bool32 isLooping); -MA_API ma_bool32 ma_data_source_is_looping(const ma_data_source* pDataSource); -MA_API ma_result ma_data_source_set_range_in_pcm_frames(ma_data_source* pDataSource, ma_uint64 rangeBegInFrames, ma_uint64 rangeEndInFrames); -MA_API void ma_data_source_get_range_in_pcm_frames(const ma_data_source* pDataSource, ma_uint64* pRangeBegInFrames, ma_uint64* pRangeEndInFrames); -MA_API ma_result ma_data_source_set_loop_point_in_pcm_frames(ma_data_source* pDataSource, ma_uint64 loopBegInFrames, ma_uint64 loopEndInFrames); -MA_API void ma_data_source_get_loop_point_in_pcm_frames(const ma_data_source* pDataSource, ma_uint64* pLoopBegInFrames, ma_uint64* pLoopEndInFrames); -MA_API ma_result ma_data_source_set_current(ma_data_source* pDataSource, ma_data_source* pCurrentDataSource); -MA_API ma_data_source* ma_data_source_get_current(const ma_data_source* pDataSource); -MA_API ma_result ma_data_source_set_next(ma_data_source* pDataSource, ma_data_source* pNextDataSource); -MA_API ma_data_source* ma_data_source_get_next(const ma_data_source* pDataSource); -MA_API ma_result ma_data_source_set_next_callback(ma_data_source* pDataSource, ma_data_source_get_next_proc onGetNext); -MA_API ma_data_source_get_next_proc ma_data_source_get_next_callback(const ma_data_source* pDataSource); - - -typedef struct -{ - ma_data_source_base ds; - ma_format format; - ma_uint32 channels; - ma_uint32 sampleRate; - ma_uint64 cursor; - ma_uint64 sizeInFrames; - const void* pData; -} ma_audio_buffer_ref; - -MA_API ma_result ma_audio_buffer_ref_init(ma_format format, ma_uint32 channels, const void* pData, ma_uint64 sizeInFrames, ma_audio_buffer_ref* pAudioBufferRef); -MA_API void ma_audio_buffer_ref_uninit(ma_audio_buffer_ref* pAudioBufferRef); -MA_API ma_result ma_audio_buffer_ref_set_data(ma_audio_buffer_ref* pAudioBufferRef, const void* pData, ma_uint64 sizeInFrames); -MA_API ma_uint64 ma_audio_buffer_ref_read_pcm_frames(ma_audio_buffer_ref* pAudioBufferRef, void* pFramesOut, ma_uint64 frameCount, ma_bool32 loop); -MA_API ma_result ma_audio_buffer_ref_seek_to_pcm_frame(ma_audio_buffer_ref* pAudioBufferRef, ma_uint64 frameIndex); -MA_API ma_result ma_audio_buffer_ref_map(ma_audio_buffer_ref* pAudioBufferRef, void** ppFramesOut, ma_uint64* pFrameCount); -MA_API ma_result ma_audio_buffer_ref_unmap(ma_audio_buffer_ref* pAudioBufferRef, ma_uint64 frameCount); /* Returns MA_AT_END if the end has been reached. This should be considered successful. */ -MA_API ma_bool32 ma_audio_buffer_ref_at_end(const ma_audio_buffer_ref* pAudioBufferRef); -MA_API ma_result ma_audio_buffer_ref_get_cursor_in_pcm_frames(const ma_audio_buffer_ref* pAudioBufferRef, ma_uint64* pCursor); -MA_API ma_result ma_audio_buffer_ref_get_length_in_pcm_frames(const ma_audio_buffer_ref* pAudioBufferRef, ma_uint64* pLength); -MA_API ma_result ma_audio_buffer_ref_get_available_frames(const ma_audio_buffer_ref* pAudioBufferRef, ma_uint64* pAvailableFrames); - - - -typedef struct -{ - ma_format format; - ma_uint32 channels; - ma_uint32 sampleRate; - ma_uint64 sizeInFrames; - const void* pData; /* If set to NULL, will allocate a block of memory for you. */ - ma_allocation_callbacks allocationCallbacks; -} ma_audio_buffer_config; - -MA_API ma_audio_buffer_config ma_audio_buffer_config_init(ma_format format, ma_uint32 channels, ma_uint64 sizeInFrames, const void* pData, const ma_allocation_callbacks* pAllocationCallbacks); - -typedef struct -{ - ma_audio_buffer_ref ref; - ma_allocation_callbacks allocationCallbacks; - ma_bool32 ownsData; /* Used to control whether or not miniaudio owns the data buffer. If set to true, pData will be freed in ma_audio_buffer_uninit(). */ - ma_uint8 _pExtraData[1]; /* For allocating a buffer with the memory located directly after the other memory of the structure. */ -} ma_audio_buffer; - -MA_API ma_result ma_audio_buffer_init(const ma_audio_buffer_config* pConfig, ma_audio_buffer* pAudioBuffer); -MA_API ma_result ma_audio_buffer_init_copy(const ma_audio_buffer_config* pConfig, ma_audio_buffer* pAudioBuffer); -MA_API ma_result ma_audio_buffer_alloc_and_init(const ma_audio_buffer_config* pConfig, ma_audio_buffer** ppAudioBuffer); /* Always copies the data. Doesn't make sense to use this otherwise. Use ma_audio_buffer_uninit_and_free() to uninit. */ -MA_API void ma_audio_buffer_uninit(ma_audio_buffer* pAudioBuffer); -MA_API void ma_audio_buffer_uninit_and_free(ma_audio_buffer* pAudioBuffer); -MA_API ma_uint64 ma_audio_buffer_read_pcm_frames(ma_audio_buffer* pAudioBuffer, void* pFramesOut, ma_uint64 frameCount, ma_bool32 loop); -MA_API ma_result ma_audio_buffer_seek_to_pcm_frame(ma_audio_buffer* pAudioBuffer, ma_uint64 frameIndex); -MA_API ma_result ma_audio_buffer_map(ma_audio_buffer* pAudioBuffer, void** ppFramesOut, ma_uint64* pFrameCount); -MA_API ma_result ma_audio_buffer_unmap(ma_audio_buffer* pAudioBuffer, ma_uint64 frameCount); /* Returns MA_AT_END if the end has been reached. This should be considered successful. */ -MA_API ma_bool32 ma_audio_buffer_at_end(const ma_audio_buffer* pAudioBuffer); -MA_API ma_result ma_audio_buffer_get_cursor_in_pcm_frames(const ma_audio_buffer* pAudioBuffer, ma_uint64* pCursor); -MA_API ma_result ma_audio_buffer_get_length_in_pcm_frames(const ma_audio_buffer* pAudioBuffer, ma_uint64* pLength); -MA_API ma_result ma_audio_buffer_get_available_frames(const ma_audio_buffer* pAudioBuffer, ma_uint64* pAvailableFrames); - - -/* -Paged Audio Buffer -================== -A paged audio buffer is made up of a linked list of pages. It's expandable, but not shrinkable. It -can be used for cases where audio data is streamed in asynchronously while allowing data to be read -at the same time. - -This is lock-free, but not 100% thread safe. You can append a page and read from the buffer across -simultaneously across different threads, however only one thread at a time can append, and only one -thread at a time can read and seek. -*/ -typedef struct ma_paged_audio_buffer_page ma_paged_audio_buffer_page; -struct ma_paged_audio_buffer_page -{ - MA_ATOMIC(MA_SIZEOF_PTR, ma_paged_audio_buffer_page*) pNext; - ma_uint64 sizeInFrames; - ma_uint8 pAudioData[1]; -}; - -typedef struct -{ - ma_format format; - ma_uint32 channels; - ma_paged_audio_buffer_page head; /* Dummy head for the lock-free algorithm. Always has a size of 0. */ - MA_ATOMIC(MA_SIZEOF_PTR, ma_paged_audio_buffer_page*) pTail; /* Never null. Initially set to &head. */ -} ma_paged_audio_buffer_data; - -MA_API ma_result ma_paged_audio_buffer_data_init(ma_format format, ma_uint32 channels, ma_paged_audio_buffer_data* pData); -MA_API void ma_paged_audio_buffer_data_uninit(ma_paged_audio_buffer_data* pData, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_paged_audio_buffer_page* ma_paged_audio_buffer_data_get_head(ma_paged_audio_buffer_data* pData); -MA_API ma_paged_audio_buffer_page* ma_paged_audio_buffer_data_get_tail(ma_paged_audio_buffer_data* pData); -MA_API ma_result ma_paged_audio_buffer_data_get_length_in_pcm_frames(ma_paged_audio_buffer_data* pData, ma_uint64* pLength); -MA_API ma_result ma_paged_audio_buffer_data_allocate_page(ma_paged_audio_buffer_data* pData, ma_uint64 pageSizeInFrames, const void* pInitialData, const ma_allocation_callbacks* pAllocationCallbacks, ma_paged_audio_buffer_page** ppPage); -MA_API ma_result ma_paged_audio_buffer_data_free_page(ma_paged_audio_buffer_data* pData, ma_paged_audio_buffer_page* pPage, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_result ma_paged_audio_buffer_data_append_page(ma_paged_audio_buffer_data* pData, ma_paged_audio_buffer_page* pPage); -MA_API ma_result ma_paged_audio_buffer_data_allocate_and_append_page(ma_paged_audio_buffer_data* pData, ma_uint32 pageSizeInFrames, const void* pInitialData, const ma_allocation_callbacks* pAllocationCallbacks); - - -typedef struct -{ - ma_paged_audio_buffer_data* pData; /* Must not be null. */ -} ma_paged_audio_buffer_config; - -MA_API ma_paged_audio_buffer_config ma_paged_audio_buffer_config_init(ma_paged_audio_buffer_data* pData); - - -typedef struct -{ - ma_data_source_base ds; - ma_paged_audio_buffer_data* pData; /* Audio data is read from here. Cannot be null. */ - ma_paged_audio_buffer_page* pCurrent; - ma_uint64 relativeCursor; /* Relative to the current page. */ - ma_uint64 absoluteCursor; -} ma_paged_audio_buffer; - -MA_API ma_result ma_paged_audio_buffer_init(const ma_paged_audio_buffer_config* pConfig, ma_paged_audio_buffer* pPagedAudioBuffer); -MA_API void ma_paged_audio_buffer_uninit(ma_paged_audio_buffer* pPagedAudioBuffer); -MA_API ma_result ma_paged_audio_buffer_read_pcm_frames(ma_paged_audio_buffer* pPagedAudioBuffer, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead); /* Returns MA_AT_END if no more pages available. */ -MA_API ma_result ma_paged_audio_buffer_seek_to_pcm_frame(ma_paged_audio_buffer* pPagedAudioBuffer, ma_uint64 frameIndex); -MA_API ma_result ma_paged_audio_buffer_get_cursor_in_pcm_frames(ma_paged_audio_buffer* pPagedAudioBuffer, ma_uint64* pCursor); -MA_API ma_result ma_paged_audio_buffer_get_length_in_pcm_frames(ma_paged_audio_buffer* pPagedAudioBuffer, ma_uint64* pLength); - /************************************************************************************************************************************************************ @@ -10942,13 +10969,17 @@ typedef struct ma_sound ma_sound; /* Sound flags. */ typedef enum { + /* Resource manager flags. */ MA_SOUND_FLAG_STREAM = 0x00000001, /* MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM */ MA_SOUND_FLAG_DECODE = 0x00000002, /* MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_DECODE */ MA_SOUND_FLAG_ASYNC = 0x00000004, /* MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC */ MA_SOUND_FLAG_WAIT_INIT = 0x00000008, /* MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT */ - MA_SOUND_FLAG_NO_DEFAULT_ATTACHMENT = 0x00000010, /* Do not attach to the endpoint by default. Useful for when setting up nodes in a complex graph system. */ - MA_SOUND_FLAG_NO_PITCH = 0x00000020, /* Disable pitch shifting with ma_sound_set_pitch() and ma_sound_group_set_pitch(). This is an optimization. */ - MA_SOUND_FLAG_NO_SPATIALIZATION = 0x00000040 /* Disable spatialization. */ + MA_SOUND_FLAG_UNKNOWN_LENGTH = 0x00000010, /* MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_UNKNOWN_LENGTH */ + + /* ma_sound specific flags. */ + MA_SOUND_FLAG_NO_DEFAULT_ATTACHMENT = 0x00001000, /* Do not attach to the endpoint by default. Useful for when setting up nodes in a complex graph system. */ + MA_SOUND_FLAG_NO_PITCH = 0x00002000, /* Disable pitch shifting with ma_sound_set_pitch() and ma_sound_group_set_pitch(). This is an optimization. */ + MA_SOUND_FLAG_NO_SPATIALIZATION = 0x00004000 /* Disable spatialization. */ } ma_sound_flags; #ifndef MA_ENGINE_MAX_LISTENERS @@ -11138,8 +11169,12 @@ MA_API ma_resource_manager* ma_engine_get_resource_manager(ma_engine* pEngine); MA_API ma_device* ma_engine_get_device(ma_engine* pEngine); MA_API ma_log* ma_engine_get_log(ma_engine* pEngine); MA_API ma_node* ma_engine_get_endpoint(ma_engine* pEngine); -MA_API ma_uint64 ma_engine_get_time(const ma_engine* pEngine); -MA_API ma_result ma_engine_set_time(ma_engine* pEngine, ma_uint64 globalTime); +MA_API ma_uint64 ma_engine_get_time_in_pcm_frames(const ma_engine* pEngine); +MA_API ma_uint64 ma_engine_get_time_in_milliseconds(const ma_engine* pEngine); +MA_API ma_result ma_engine_set_time_in_pcm_frames(ma_engine* pEngine, ma_uint64 globalTime); +MA_API ma_result ma_engine_set_time_in_milliseconds(ma_engine* pEngine, ma_uint64 globalTime); +MA_API ma_uint64 ma_engine_get_time(const ma_engine* pEngine); /* Deprecated. Use ma_engine_get_time_in_pcm_frames(). Will be removed in version 0.12. */ +MA_API ma_result ma_engine_set_time(ma_engine* pEngine, ma_uint64 globalTime); /* Deprecated. Use ma_engine_set_time_in_pcm_frames(). Will be removed in version 0.12. */ MA_API ma_uint32 ma_engine_get_channels(const ma_engine* pEngine); MA_API ma_uint32 ma_engine_get_sample_rate(const ma_engine* pEngine); @@ -13905,6 +13940,13 @@ Atomics #if defined(__cplusplus) extern "C" { #endif +#if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))) + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wlong-long" + #if defined(__clang__) + #pragma GCC diagnostic ignored "-Wc++11-long-long" + #endif +#endif typedef signed char c89atomic_int8; typedef unsigned char c89atomic_uint8; typedef signed short c89atomic_int16; @@ -13915,18 +13957,8 @@ typedef unsigned int c89atomic_uint32; typedef signed __int64 c89atomic_int64; typedef unsigned __int64 c89atomic_uint64; #else - #if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))) - #pragma GCC diagnostic push - #pragma GCC diagnostic ignored "-Wlong-long" - #if defined(__clang__) - #pragma GCC diagnostic ignored "-Wc++11-long-long" - #endif - #endif typedef signed long long c89atomic_int64; typedef unsigned long long c89atomic_uint64; - #if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))) - #pragma GCC diagnostic pop - #endif #endif typedef int c89atomic_memory_order; typedef unsigned char c89atomic_bool; @@ -15291,7 +15323,7 @@ typedef unsigned char c89atomic_bool; #endif #if !defined(C89ATOMIC_HAS_NATIVE_COMPARE_EXCHANGE) #if defined(C89ATOMIC_HAS_8) - c89atomic_bool c89atomic_compare_exchange_strong_explicit_8(volatile c89atomic_uint8* dst, c89atomic_uint8* expected, c89atomic_uint8 desired, c89atomic_memory_order successOrder, c89atomic_memory_order failureOrder) + static C89ATOMIC_INLINE c89atomic_bool c89atomic_compare_exchange_strong_explicit_8(volatile c89atomic_uint8* dst, c89atomic_uint8* expected, c89atomic_uint8 desired, c89atomic_memory_order successOrder, c89atomic_memory_order failureOrder) { c89atomic_uint8 expectedValue; c89atomic_uint8 result; @@ -15308,7 +15340,7 @@ typedef unsigned char c89atomic_bool; } #endif #if defined(C89ATOMIC_HAS_16) - c89atomic_bool c89atomic_compare_exchange_strong_explicit_16(volatile c89atomic_uint16* dst, c89atomic_uint16* expected, c89atomic_uint16 desired, c89atomic_memory_order successOrder, c89atomic_memory_order failureOrder) + static C89ATOMIC_INLINE c89atomic_bool c89atomic_compare_exchange_strong_explicit_16(volatile c89atomic_uint16* dst, c89atomic_uint16* expected, c89atomic_uint16 desired, c89atomic_memory_order successOrder, c89atomic_memory_order failureOrder) { c89atomic_uint16 expectedValue; c89atomic_uint16 result; @@ -15325,7 +15357,7 @@ typedef unsigned char c89atomic_bool; } #endif #if defined(C89ATOMIC_HAS_32) - c89atomic_bool c89atomic_compare_exchange_strong_explicit_32(volatile c89atomic_uint32* dst, c89atomic_uint32* expected, c89atomic_uint32 desired, c89atomic_memory_order successOrder, c89atomic_memory_order failureOrder) + static C89ATOMIC_INLINE c89atomic_bool c89atomic_compare_exchange_strong_explicit_32(volatile c89atomic_uint32* dst, c89atomic_uint32* expected, c89atomic_uint32 desired, c89atomic_memory_order successOrder, c89atomic_memory_order failureOrder) { c89atomic_uint32 expectedValue; c89atomic_uint32 result; @@ -15342,7 +15374,7 @@ typedef unsigned char c89atomic_bool; } #endif #if defined(C89ATOMIC_HAS_64) - c89atomic_bool c89atomic_compare_exchange_strong_explicit_64(volatile c89atomic_uint64* dst, volatile c89atomic_uint64* expected, c89atomic_uint64 desired, c89atomic_memory_order successOrder, c89atomic_memory_order failureOrder) + static C89ATOMIC_INLINE c89atomic_bool c89atomic_compare_exchange_strong_explicit_64(volatile c89atomic_uint64* dst, volatile c89atomic_uint64* expected, c89atomic_uint64 desired, c89atomic_memory_order successOrder, c89atomic_memory_order failureOrder) { c89atomic_uint64 expectedValue; c89atomic_uint64 result; @@ -15820,6 +15852,9 @@ static C89ATOMIC_INLINE void c89atomic_spinlock_unlock(volatile c89atomic_spinlo { c89atomic_flag_clear_explicit(pSpinlock, c89atomic_memory_order_release); } +#if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))) + #pragma GCC diagnostic pop +#endif #if defined(__cplusplus) } #endif @@ -15895,7 +15930,10 @@ MA_ATOMIC_SAFE_TYPE_IMPL(i32, int32) MA_ATOMIC_SAFE_TYPE_IMPL(64, uint64) MA_ATOMIC_SAFE_TYPE_IMPL(f32, float) MA_ATOMIC_SAFE_TYPE_IMPL(32, bool32) + +#if !defined(MA_NO_DEVICE_IO) MA_ATOMIC_SAFE_TYPE_IMPL(i32, device_state) +#endif MA_API ma_uint64 ma_calculate_frame_count_after_resampling(ma_uint32 sampleRateOut, ma_uint32 sampleRateIn, ma_uint64 frameCountIn) @@ -16028,23 +16066,28 @@ static ma_result ma_thread_create__posix(ma_thread* pThread, ma_thread_priority /* We successfully initialized our attributes object so we can assign the pointer so it's passed into pthread_create(). */ pAttr = &attr; - if (priority == ma_thread_priority_idle) { -#ifdef SCHED_IDLE - if (pthread_attr_setschedpolicy(&attr, SCHED_IDLE) == 0) { - scheduler = SCHED_IDLE; + /* We need to set the scheduler policy. Only do this if the OS supports pthread_attr_setschedpolicy() */ + #if !defined(MA_BEOS) + { + if (priority == ma_thread_priority_idle) { + #ifdef SCHED_IDLE + if (pthread_attr_setschedpolicy(&attr, SCHED_IDLE) == 0) { + scheduler = SCHED_IDLE; + } + #endif + } else if (priority == ma_thread_priority_realtime) { + #ifdef SCHED_FIFO + if (pthread_attr_setschedpolicy(&attr, SCHED_FIFO) == 0) { + scheduler = SCHED_FIFO; + } + #endif + #ifdef MA_LINUX + } else { + scheduler = sched_getscheduler(0); + #endif } -#endif - } else if (priority == ma_thread_priority_realtime) { -#ifdef SCHED_FIFO - if (pthread_attr_setschedpolicy(&attr, SCHED_FIFO) == 0) { - scheduler = SCHED_FIFO; - } -#endif -#ifdef MA_LINUX - } else { - scheduler = sched_getscheduler(0); -#endif } + #endif if (stackSize > 0) { pthread_attr_setstacksize(&attr, stackSize); @@ -16438,6 +16481,12 @@ static ma_result ma_thread_create(ma_thread* pThread, ma_thread_priority priorit return MA_OUT_OF_MEMORY; } +#if defined(MA_THREAD_DEFAULT_STACK_SIZE) + if (stackSize == 0) { + stackSize = MA_THREAD_DEFAULT_STACK_SIZE; + } +#endif + pProxyData->entryProc = entryProc; pProxyData->pData = pData; ma_allocation_callbacks_init_copy(&pProxyData->allocationCallbacks, pAllocationCallbacks); @@ -30128,7 +30177,6 @@ static ma_result ma_device_init__pulse(ma_device* pDevice, const ma_device_confi ma_pa_channel_map cmap; ma_pa_buffer_attr attr; const ma_pa_sample_spec* pActualSS = NULL; - const ma_pa_channel_map* pActualCMap = NULL; const ma_pa_buffer_attr* pActualAttr = NULL; ma_uint32 iChannel; ma_pa_stream_flags_t streamFlags; @@ -30184,6 +30232,14 @@ static ma_result ma_device_init__pulse(ma_device* pDevice, const ma_device_confi ss = sourceInfo.sample_spec; cmap = sourceInfo.channel_map; + /* Use the requested channel count if we have one. */ + if (pDescriptorCapture->channels != 0) { + ss.channels = pDescriptorCapture->channels; + } + + /* Use a default channel map. */ + ((ma_pa_channel_map_init_extend_proc)pDevice->pContext->pulse.pa_channel_map_init_extend)(&cmap, ss.channels, MA_PA_CHANNEL_MAP_DEFAULT); + /* Use the requested sample rate if one was specified. */ if (pDescriptorCapture->sampleRate != 0) { ss.rate = pDescriptorCapture->sampleRate; @@ -30278,11 +30334,6 @@ static ma_result ma_device_init__pulse(ma_device* pDevice, const ma_device_confi fixed sooner than later. I might remove this hack later. */ if (pDescriptorCapture->channels > 2) { - pActualCMap = ((ma_pa_stream_get_channel_map_proc)pDevice->pContext->pulse.pa_stream_get_channel_map)((ma_pa_stream*)pDevice->pulse.pStreamCapture); - if (pActualCMap != NULL) { - cmap = *pActualCMap; - } - for (iChannel = 0; iChannel < pDescriptorCapture->channels; ++iChannel) { pDescriptorCapture->channelMap[iChannel] = ma_channel_position_from_pulse(cmap.map[iChannel]); } @@ -30325,6 +30376,15 @@ static ma_result ma_device_init__pulse(ma_device* pDevice, const ma_device_confi ss = sinkInfo.sample_spec; cmap = sinkInfo.channel_map; + /* Use the requested channel count if we have one. */ + if (pDescriptorPlayback->channels != 0) { + ss.channels = pDescriptorPlayback->channels; + } + + /* Use a default channel map. */ + ((ma_pa_channel_map_init_extend_proc)pDevice->pContext->pulse.pa_channel_map_init_extend)(&cmap, ss.channels, MA_PA_CHANNEL_MAP_DEFAULT); + + /* Use the requested sample rate if one was specified. */ if (pDescriptorPlayback->sampleRate != 0) { ss.rate = pDescriptorPlayback->sampleRate; @@ -30423,11 +30483,6 @@ static ma_result ma_device_init__pulse(ma_device* pDevice, const ma_device_confi fixed sooner than later. I might remove this hack later. */ if (pDescriptorPlayback->channels > 2) { - pActualCMap = ((ma_pa_stream_get_channel_map_proc)pDevice->pContext->pulse.pa_stream_get_channel_map)((ma_pa_stream*)pDevice->pulse.pStreamPlayback); - if (pActualCMap != NULL) { - cmap = *pActualCMap; - } - for (iChannel = 0; iChannel < pDescriptorPlayback->channels; ++iChannel) { pDescriptorPlayback->channelMap[iChannel] = ma_channel_position_from_pulse(cmap.map[iChannel]); } @@ -50310,6 +50365,26 @@ MA_API ma_result ma_spatializer_process_pcm_frames(ma_spatializer* pSpatializer, /* Clamp the gain. */ gain = ma_clamp(gain, ma_spatializer_get_min_gain(pSpatializer), ma_spatializer_get_max_gain(pSpatializer)); + /* + The gain needs to be applied per-channel here. The spatialization code below will be changing the per-channel + gains which will then eventually be passed into the gainer which will deal with smoothing the gain transitions + to avoid harsh changes in gain. + */ + for (iChannel = 0; iChannel < channelsOut; iChannel += 1) { + pSpatializer->pNewChannelGainsOut[iChannel] = gain; + } + + /* + Convert to our output channel count. If the listener is disabled we just output silence here. We cannot ignore + the whole section of code here because we need to update some internal spatialization state. + */ + if (ma_spatializer_listener_is_enabled(pListener)) { + ma_channel_map_apply_f32((float*)pFramesOut, pChannelMapOut, channelsOut, (const float*)pFramesIn, pChannelMapIn, channelsIn, frameCount, ma_channel_mix_mode_rectangular, ma_mono_expansion_mode_default); + } else { + ma_silence_pcm_frames(pFramesOut, frameCount, ma_format_f32, pSpatializer->channelsOut); + } + + /* Panning. This is where we'll apply the gain and convert to the output channel count. We have an optimized path for when we're converting to a mono stream. In that case we don't really need to do any panning - we just apply the @@ -50331,19 +50406,6 @@ MA_API ma_result ma_spatializer_process_pcm_frames(ma_spatializer* pSpatializer, be +1 on the X axis. A dot product is performed against the direction vector of the channel and the normalized position of the sound. */ - for (iChannel = 0; iChannel < channelsOut; iChannel += 1) { - pSpatializer->pNewChannelGainsOut[iChannel] = gain; - } - - /* - Convert to our output channel count. If the listener is disabled we just output silence here. We cannot ignore - the whole section of code here because we need to update some internal spatialization state. - */ - if (ma_spatializer_listener_is_enabled(pListener)) { - ma_channel_map_apply_f32((float*)pFramesOut, pChannelMapOut, channelsOut, (const float*)pFramesIn, pChannelMapIn, channelsIn, frameCount, ma_channel_mix_mode_rectangular, ma_mono_expansion_mode_default); - } else { - ma_silence_pcm_frames(pFramesOut, frameCount, ma_format_f32, pSpatializer->channelsOut); - } /* Calculate our per-channel gains. We do this based on the normalized relative position of the sound and it's @@ -56367,6 +56429,85 @@ MA_API void* ma_rb_get_subbuffer_ptr(ma_rb* pRB, size_t subbufferIndex, void* pB +static ma_result ma_pcm_rb_data_source__on_read(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) +{ + /* Since there's no notion of an end, we don't ever want to return MA_AT_END here. But it is possible to return 0. */ + ma_pcm_rb* pRB = (ma_pcm_rb*)pDataSource; + ma_result result; + ma_uint64 totalFramesRead; + + MA_ASSERT(pRB != NULL); + + /* We need to run this in a loop since the ring buffer itself may loop. */ + totalFramesRead = 0; + while (totalFramesRead < frameCount) { + void* pMappedBuffer; + ma_uint32 mappedFrameCount; + ma_uint64 framesToRead = frameCount - totalFramesRead; + if (framesToRead > 0xFFFFFFFF) { + framesToRead = 0xFFFFFFFF; + } + + mappedFrameCount = (ma_uint32)framesToRead; + result = ma_pcm_rb_acquire_read(pRB, &mappedFrameCount, &pMappedBuffer); + if (result != MA_SUCCESS) { + break; + } + + if (mappedFrameCount == 0) { + break; /* <-- End of ring buffer. */ + } + + ma_copy_pcm_frames(ma_offset_pcm_frames_ptr(pFramesOut, totalFramesRead, pRB->format, pRB->channels), pMappedBuffer, mappedFrameCount, pRB->format, pRB->channels); + + result = ma_pcm_rb_commit_read(pRB, mappedFrameCount); + if (result != MA_SUCCESS) { + break; + } + + totalFramesRead += mappedFrameCount; + } + + *pFramesRead = totalFramesRead; + return MA_SUCCESS; +} + +static ma_result ma_pcm_rb_data_source__on_get_data_format(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap) +{ + ma_pcm_rb* pRB = (ma_pcm_rb*)pDataSource; + MA_ASSERT(pRB != NULL); + + if (pFormat != NULL) { + *pFormat = pRB->format; + } + + if (pChannels != NULL) { + *pChannels = pRB->channels; + } + + if (pSampleRate != NULL) { + *pSampleRate = pRB->sampleRate; + } + + /* Just assume the default channel map. */ + if (pChannelMap != NULL) { + ma_channel_map_init_standard(ma_standard_channel_map_default, pChannelMap, channelMapCap, pRB->channels); + } + + return MA_SUCCESS; +} + +static ma_data_source_vtable ma_gRBDataSourceVTable = +{ + ma_pcm_rb_data_source__on_read, + NULL, /* onSeek */ + ma_pcm_rb_data_source__on_get_data_format, + NULL, /* onGetCursor */ + NULL, /* onGetLength */ + NULL, /* onSetLooping */ + 0 +}; + static MA_INLINE ma_uint32 ma_pcm_rb_get_bpf(ma_pcm_rb* pRB) { MA_ASSERT(pRB != NULL); @@ -56395,8 +56536,21 @@ MA_API ma_result ma_pcm_rb_init_ex(ma_format format, ma_uint32 channels, ma_uint return result; } - pRB->format = format; - pRB->channels = channels; + pRB->format = format; + pRB->channels = channels; + pRB->sampleRate = 0; /* The sample rate is not passed in as a parameter. */ + + /* The PCM ring buffer is a data source. We need to get that set up as well. */ + { + ma_data_source_config dataSourceConfig = ma_data_source_config_init(); + dataSourceConfig.vtable = &ma_gRBDataSourceVTable; + + result = ma_data_source_init(&dataSourceConfig, &pRB->ds); + if (result != MA_SUCCESS) { + ma_rb_uninit(&pRB->rb); + return result; + } + } return MA_SUCCESS; } @@ -56412,6 +56566,7 @@ MA_API void ma_pcm_rb_uninit(ma_pcm_rb* pRB) return; } + ma_data_source_uninit(&pRB->ds); ma_rb_uninit(&pRB->rb); } @@ -56563,6 +56718,42 @@ MA_API void* ma_pcm_rb_get_subbuffer_ptr(ma_pcm_rb* pRB, ma_uint32 subbufferInde return ma_rb_get_subbuffer_ptr(&pRB->rb, subbufferIndex, pBuffer); } +MA_API ma_format ma_pcm_rb_get_format(const ma_pcm_rb* pRB) +{ + if (pRB == NULL) { + return ma_format_unknown; + } + + return pRB->format; +} + +MA_API ma_uint32 ma_pcm_rb_get_channels(const ma_pcm_rb* pRB) +{ + if (pRB == NULL) { + return 0; + } + + return pRB->channels; +} + +MA_API ma_uint32 ma_pcm_rb_get_sample_rate(const ma_pcm_rb* pRB) +{ + if (pRB == NULL) { + return 0; + } + + return pRB->sampleRate; +} + +MA_API void ma_pcm_rb_set_sample_rate(ma_pcm_rb* pRB, ma_uint32 sampleRate) +{ + if (pRB == NULL) { + return; + } + + pRB->sampleRate = sampleRate; +} + MA_API ma_result ma_duplex_rb_init(ma_format captureFormat, ma_uint32 captureChannels, ma_uint32 sampleRate, ma_uint32 captureInternalSampleRate, ma_uint32 captureInternalPeriodSizeInFrames, const ma_allocation_callbacks* pAllocationCallbacks, ma_duplex_rb* pRB) @@ -58510,7 +58701,7 @@ MA_API ma_result ma_vfs_read(ma_vfs* pVFS, ma_vfs_file file, void* pDst, size_t { ma_vfs_callbacks* pCallbacks = (ma_vfs_callbacks*)pVFS; ma_result result; - size_t bytesRead; + size_t bytesRead = 0; if (pBytesRead != NULL) { *pBytesRead = 0; @@ -74465,16 +74656,36 @@ MA_API ma_node* ma_engine_get_endpoint(ma_engine* pEngine) return ma_node_graph_get_endpoint(&pEngine->nodeGraph); } -MA_API ma_uint64 ma_engine_get_time(const ma_engine* pEngine) +MA_API ma_uint64 ma_engine_get_time_in_pcm_frames(const ma_engine* pEngine) { return ma_node_graph_get_time(&pEngine->nodeGraph); } -MA_API ma_result ma_engine_set_time(ma_engine* pEngine, ma_uint64 globalTime) +MA_API ma_uint64 ma_engine_get_time_in_milliseconds(const ma_engine* pEngine) +{ + return ma_engine_get_time_in_pcm_frames(pEngine) * 1000 / ma_engine_get_sample_rate(pEngine); +} + +MA_API ma_result ma_engine_set_time_in_pcm_frames(ma_engine* pEngine, ma_uint64 globalTime) { return ma_node_graph_set_time(&pEngine->nodeGraph, globalTime); } +MA_API ma_result ma_engine_set_time_in_milliseconds(ma_engine* pEngine, ma_uint64 globalTime) +{ + return ma_engine_set_time_in_pcm_frames(pEngine, globalTime * ma_engine_get_sample_rate(pEngine) / 1000); +} + +MA_API ma_uint64 ma_engine_get_time(const ma_engine* pEngine) +{ + return ma_engine_get_time_in_pcm_frames(pEngine); +} + +MA_API ma_result ma_engine_set_time(ma_engine* pEngine, ma_uint64 globalTime) +{ + return ma_engine_set_time_in_pcm_frames(pEngine, globalTime); +} + MA_API ma_uint32 ma_engine_get_channels(const ma_engine* pEngine) { return ma_node_graph_get_channels(&pEngine->nodeGraph); @@ -75695,7 +75906,7 @@ MA_API ma_bool32 ma_sound_is_playing(const ma_sound* pSound) return MA_FALSE; } - return ma_node_get_state_by_time(pSound, ma_engine_get_time(ma_sound_get_engine(pSound))) == ma_node_state_started; + return ma_node_get_state_by_time(pSound, ma_engine_get_time_in_pcm_frames(ma_sound_get_engine(pSound))) == ma_node_state_started; } MA_API ma_uint64 ma_sound_get_time_in_pcm_frames(const ma_sound* pSound) @@ -76902,10 +77113,14 @@ DRWAV_PRIVATE drwav_uint64 drwav__read_smpl_to_metadata_obj(drwav__metadata_pars { drwav_uint8 smplHeaderData[DRWAV_SMPL_BYTES]; drwav_uint64 totalBytesRead = 0; - size_t bytesJustRead = drwav__metadata_parser_read(pParser, smplHeaderData, sizeof(smplHeaderData), &totalBytesRead); + size_t bytesJustRead; + if (pMetadata == NULL) { + return 0; + } + bytesJustRead = drwav__metadata_parser_read(pParser, smplHeaderData, sizeof(smplHeaderData), &totalBytesRead); DRWAV_ASSERT(pParser->stage == drwav__metadata_parser_stage_read); DRWAV_ASSERT(pChunkHeader != NULL); - if (bytesJustRead == sizeof(smplHeaderData)) { + if (pMetadata != NULL && bytesJustRead == sizeof(smplHeaderData)) { drwav_uint32 iSampleLoop; pMetadata->type = drwav_metadata_type_smpl; pMetadata->data.smpl.manufacturerId = drwav_bytes_to_u32(smplHeaderData + 0); @@ -76946,7 +77161,11 @@ DRWAV_PRIVATE drwav_uint64 drwav__read_cue_to_metadata_obj(drwav__metadata_parse { drwav_uint8 cueHeaderSectionData[DRWAV_CUE_BYTES]; drwav_uint64 totalBytesRead = 0; - size_t bytesJustRead = drwav__metadata_parser_read(pParser, cueHeaderSectionData, sizeof(cueHeaderSectionData), &totalBytesRead); + size_t bytesJustRead; + if (pMetadata == NULL) { + return 0; + } + bytesJustRead = drwav__metadata_parser_read(pParser, cueHeaderSectionData, sizeof(cueHeaderSectionData), &totalBytesRead); DRWAV_ASSERT(pParser->stage == drwav__metadata_parser_stage_read); if (bytesJustRead == sizeof(cueHeaderSectionData)) { pMetadata->type = drwav_metadata_type_cue; @@ -76981,7 +77200,11 @@ DRWAV_PRIVATE drwav_uint64 drwav__read_cue_to_metadata_obj(drwav__metadata_parse DRWAV_PRIVATE drwav_uint64 drwav__read_inst_to_metadata_obj(drwav__metadata_parser* pParser, drwav_metadata* pMetadata) { drwav_uint8 instData[DRWAV_INST_BYTES]; - drwav_uint64 bytesRead = drwav__metadata_parser_read(pParser, instData, sizeof(instData), NULL); + drwav_uint64 bytesRead; + if (pMetadata == NULL) { + return 0; + } + bytesRead = drwav__metadata_parser_read(pParser, instData, sizeof(instData), NULL); DRWAV_ASSERT(pParser->stage == drwav__metadata_parser_stage_read); if (bytesRead == sizeof(instData)) { pMetadata->type = drwav_metadata_type_inst; @@ -76998,7 +77221,11 @@ DRWAV_PRIVATE drwav_uint64 drwav__read_inst_to_metadata_obj(drwav__metadata_pars DRWAV_PRIVATE drwav_uint64 drwav__read_acid_to_metadata_obj(drwav__metadata_parser* pParser, drwav_metadata* pMetadata) { drwav_uint8 acidData[DRWAV_ACID_BYTES]; - drwav_uint64 bytesRead = drwav__metadata_parser_read(pParser, acidData, sizeof(acidData), NULL); + drwav_uint64 bytesRead; + if (pMetadata == NULL) { + return 0; + } + bytesRead = drwav__metadata_parser_read(pParser, acidData, sizeof(acidData), NULL); DRWAV_ASSERT(pParser->stage == drwav__metadata_parser_stage_read); if (bytesRead == sizeof(acidData)) { pMetadata->type = drwav_metadata_type_acid;