mirror of
https://github.com/MoleskiCoder/EightBit.git
synced 2025-01-24 09:30:05 +00:00
Refactor the Audio header into declaration/definition files.
Signed-off-by: Adrian.Conlon <adrian.conlon@arup.com>
This commit is contained in:
parent
877618f973
commit
531d5d6d6b
@ -5,8 +5,6 @@
|
||||
#include <iostream>
|
||||
#include <cassert>
|
||||
|
||||
#include <Processor.h>
|
||||
|
||||
#include "AudioFrame.h"
|
||||
|
||||
namespace EightBit {
|
||||
@ -14,31 +12,24 @@ namespace EightBit {
|
||||
|
||||
class Envelope final {
|
||||
public:
|
||||
Envelope() {}
|
||||
Envelope();
|
||||
|
||||
enum Direction { Attenuate, Amplify };
|
||||
|
||||
void reset() {
|
||||
m_defaultValue = m_direction = m_stepLength = 0;
|
||||
}
|
||||
void reset();
|
||||
bool zeroed() const;
|
||||
|
||||
bool zeroed() const {
|
||||
return (default() == 0) && (stepLength() == 0) && (direction() == Attenuate);
|
||||
}
|
||||
int default() const;
|
||||
void setDefault(int value);
|
||||
|
||||
int default() const { return m_defaultValue; }
|
||||
void setDefault(int value) { m_defaultValue = value; }
|
||||
Direction direction() const;
|
||||
void setDirection(int value);
|
||||
void setDirection(Direction value);
|
||||
|
||||
Direction direction() const { return (Direction)m_direction; }
|
||||
void setDirection(int value) { m_direction = value; }
|
||||
void setDirection(Direction value) { setDirection((int)value); }
|
||||
int stepLength() const;
|
||||
void setStepLength(int value);
|
||||
|
||||
int stepLength() const { return m_stepLength; }
|
||||
void setStepLength(int value) { m_stepLength = value; }
|
||||
|
||||
void dump() const {
|
||||
std::cout << "Envelope: default=" << default() << ",direction=" << direction() << ",step length=" << stepLength() << std::endl;
|
||||
}
|
||||
void dump() const;
|
||||
|
||||
private:
|
||||
int m_defaultValue;
|
||||
@ -48,31 +39,24 @@ namespace EightBit {
|
||||
|
||||
class Sweep final {
|
||||
public:
|
||||
Sweep() {}
|
||||
Sweep();
|
||||
|
||||
enum Direction { Addition, Subtraction };
|
||||
|
||||
void reset() {
|
||||
m_time = m_direction = m_shift = 0;
|
||||
}
|
||||
void reset();
|
||||
bool zeroed() const;
|
||||
|
||||
bool zeroed() const {
|
||||
return (time() == 0) && (shift() == 0) && (direction() == Addition);
|
||||
}
|
||||
int time() const;
|
||||
void setTime(int value);
|
||||
|
||||
int time() const { return m_time; }
|
||||
void setTime(int value) { m_time = value; }
|
||||
Direction direction() const;
|
||||
void setDirection(int value);
|
||||
void setDirection(Direction value);
|
||||
|
||||
Direction direction() const { return (Direction)m_direction; }
|
||||
void setDirection(int value) { m_direction = value; }
|
||||
void setDirection(Direction value) { setDirection((int)value); }
|
||||
int shift() const;
|
||||
void setShift(int value);
|
||||
|
||||
int shift() const { return m_shift; }
|
||||
void setShift(int value) { m_shift = value; }
|
||||
|
||||
void dump() const {
|
||||
std::cout << "Sweep: time=" << time() << ",direction=" << direction() << ",shift=" << shift() << std::endl;
|
||||
}
|
||||
void dump() const;
|
||||
|
||||
private:
|
||||
int m_time;
|
||||
@ -82,28 +66,21 @@ namespace EightBit {
|
||||
|
||||
class AudioVoice {
|
||||
public:
|
||||
AudioVoice() {}
|
||||
AudioVoice();
|
||||
|
||||
virtual void reset() {
|
||||
m_counterContinuous = m_initialise = 0;
|
||||
}
|
||||
|
||||
virtual bool zeroed() const {
|
||||
return !initialise() && (type() == Continuous);
|
||||
}
|
||||
virtual void reset();
|
||||
virtual bool zeroed() const;
|
||||
|
||||
enum Type { Continuous, Counter };
|
||||
|
||||
Type type() const { return (Type)m_counterContinuous; }
|
||||
void setType(int value) { m_counterContinuous = value; }
|
||||
void setType(Type value) { setType((int)value); }
|
||||
Type type() const;
|
||||
void setType(int value);
|
||||
void setType(Type value);
|
||||
|
||||
bool initialise() const { return !!m_initialise; }
|
||||
void setInitialise(bool value) { m_initialise = value; }
|
||||
bool initialise() const;
|
||||
void setInitialise(bool value);
|
||||
|
||||
virtual void dump() const {
|
||||
std::cout << "Audio Voice: type=" << type() << ",initialise=" << initialise() << std::endl;
|
||||
}
|
||||
virtual void dump() const;
|
||||
|
||||
private:
|
||||
int m_counterContinuous;
|
||||
@ -112,52 +89,22 @@ namespace EightBit {
|
||||
|
||||
class WaveVoice : public AudioVoice {
|
||||
public:
|
||||
WaveVoice(int cyclesPerSecond)
|
||||
: m_cyclesPerSecond(cyclesPerSecond) {}
|
||||
WaveVoice(int cyclesPerSecond);
|
||||
|
||||
virtual void reset() override {
|
||||
AudioVoice::reset();
|
||||
m_frequencyLowOrder = m_frequencyHighOrder = 0;
|
||||
}
|
||||
virtual void reset() override;
|
||||
virtual bool zeroed() const override;
|
||||
|
||||
virtual bool zeroed() const override {
|
||||
return AudioVoice::zeroed() && (frequency() == 0);
|
||||
}
|
||||
int frequencyLowOrder() const;
|
||||
void setFrequencyLowOrder(int value);
|
||||
int frequencyHighOrder() const;
|
||||
void setFrequencyHighOrder(int value);
|
||||
|
||||
int frequencyLowOrder() const { return m_frequencyLowOrder; }
|
||||
int frequency() const;
|
||||
void setFrequency(int value);
|
||||
|
||||
void setFrequencyLowOrder(int value) {
|
||||
assert(value < Processor::Bit8);
|
||||
m_frequencyLowOrder = value;
|
||||
}
|
||||
int hertz() const;
|
||||
|
||||
int frequencyHighOrder() const { return m_frequencyHighOrder; }
|
||||
|
||||
void setFrequencyHighOrder(int value) {
|
||||
assert(value < Processor::Bit3);
|
||||
m_frequencyHighOrder = value;
|
||||
}
|
||||
|
||||
int frequency() const {
|
||||
return (m_frequencyHighOrder << 8) | m_frequencyLowOrder;
|
||||
}
|
||||
|
||||
int hertz() const {
|
||||
// f = 4194304 / (4 x 8 x (2048 - X)) Hz
|
||||
auto division = 4 * 8 * (2048 - frequency());
|
||||
return m_cyclesPerSecond / division;
|
||||
}
|
||||
|
||||
void setFrequency(int value) {
|
||||
assert(value < Processor::Bit11);
|
||||
m_frequencyHighOrder = (value >> 8) & Processor::Mask3;
|
||||
m_frequencyLowOrder = value & Processor::Mask8;
|
||||
}
|
||||
|
||||
virtual void dump() const override {
|
||||
AudioVoice::dump();
|
||||
std::cout << "Wave Voice: frequency=" << frequency() << " (" << hertz() << ")" << std::endl;
|
||||
}
|
||||
virtual void dump() const override;
|
||||
|
||||
private:
|
||||
const int m_cyclesPerSecond;
|
||||
@ -167,28 +114,18 @@ namespace EightBit {
|
||||
|
||||
class RectangularVoice : public WaveVoice {
|
||||
public:
|
||||
RectangularVoice(int cyclesPerSecond)
|
||||
: WaveVoice(cyclesPerSecond) {}
|
||||
RectangularVoice(int cyclesPerSecond);
|
||||
|
||||
virtual void reset() override {
|
||||
WaveVoice::reset();
|
||||
m_waveFormDutyCycle = m_soundLength = 0;
|
||||
}
|
||||
virtual void reset() override;
|
||||
virtual bool zeroed() const override;
|
||||
|
||||
virtual bool zeroed() const override {
|
||||
return WaveVoice::zeroed() && (waveFormDutyCycle() == 0) && (length() == 0);
|
||||
}
|
||||
int waveFormDutyCycle() const;
|
||||
void setWaveFormDutyCycle(int value);
|
||||
|
||||
int waveFormDutyCycle() const { return m_waveFormDutyCycle; }
|
||||
void setWaveFormDutyCycle(int value) { m_waveFormDutyCycle = value; }
|
||||
int length() const;
|
||||
void setLength(int value);
|
||||
|
||||
int length() const { return m_soundLength; }
|
||||
void setLength(int value) { m_soundLength = value; }
|
||||
|
||||
virtual void dump() const override {
|
||||
WaveVoice::dump();
|
||||
std::cout << "Rectangular Voice: wave form duty=" << waveFormDutyCycle() << ",length=" << length() << std::endl;
|
||||
}
|
||||
virtual void dump() const override;
|
||||
|
||||
private:
|
||||
int m_waveFormDutyCycle;
|
||||
@ -198,24 +135,14 @@ namespace EightBit {
|
||||
// NR2X
|
||||
class EnvelopedRectangularVoice : public RectangularVoice {
|
||||
public:
|
||||
EnvelopedRectangularVoice(int cyclesPerSecond)
|
||||
: RectangularVoice(cyclesPerSecond) {}
|
||||
EnvelopedRectangularVoice(int cyclesPerSecond);
|
||||
|
||||
virtual void reset() override {
|
||||
RectangularVoice::reset();
|
||||
m_envelope.reset();
|
||||
}
|
||||
virtual void reset() override;
|
||||
virtual bool zeroed() const override;
|
||||
|
||||
virtual bool zeroed() const override {
|
||||
return RectangularVoice::zeroed() && m_envelope.zeroed();
|
||||
}
|
||||
Envelope& envelope();
|
||||
|
||||
Envelope& envelope() { return m_envelope; }
|
||||
|
||||
virtual void dump() const override {
|
||||
RectangularVoice::dump();
|
||||
m_envelope.dump();
|
||||
}
|
||||
virtual void dump() const override;
|
||||
|
||||
private:
|
||||
Envelope m_envelope;
|
||||
@ -224,24 +151,14 @@ namespace EightBit {
|
||||
// NR1X
|
||||
class SweptEnvelopedRectangularVoice : public EnvelopedRectangularVoice {
|
||||
public:
|
||||
SweptEnvelopedRectangularVoice(int cyclesPerSecond)
|
||||
: EnvelopedRectangularVoice(cyclesPerSecond) {}
|
||||
SweptEnvelopedRectangularVoice(int cyclesPerSecond);
|
||||
|
||||
virtual void reset() override {
|
||||
EnvelopedRectangularVoice::reset();
|
||||
m_sweep.reset();
|
||||
}
|
||||
virtual void reset() override;
|
||||
virtual bool zeroed() const override;
|
||||
|
||||
virtual bool zeroed() const override {
|
||||
return EnvelopedRectangularVoice::zeroed() && m_sweep.zeroed();
|
||||
}
|
||||
Sweep& sweep();
|
||||
|
||||
Sweep& sweep() { return m_sweep; }
|
||||
|
||||
virtual void dump() const override {
|
||||
EnvelopedRectangularVoice::dump();
|
||||
m_sweep.dump();
|
||||
}
|
||||
virtual void dump() const override;
|
||||
|
||||
private:
|
||||
Sweep m_sweep;
|
||||
@ -250,56 +167,23 @@ namespace EightBit {
|
||||
// NR3X
|
||||
class UserDefinedWaveVoice : public WaveVoice {
|
||||
public:
|
||||
UserDefinedWaveVoice(int cyclesPerSecond)
|
||||
: WaveVoice(cyclesPerSecond) {}
|
||||
UserDefinedWaveVoice(int cyclesPerSecond);
|
||||
|
||||
virtual void reset() override {
|
||||
WaveVoice::reset();
|
||||
m_enabled = m_soundLength = m_outputLevel = 0;
|
||||
for (auto& datum : m_waveData)
|
||||
datum = 0;
|
||||
}
|
||||
virtual void reset() override;
|
||||
virtual bool zeroed() const override;
|
||||
|
||||
virtual bool zeroed() const override {
|
||||
bool dataZeroed = true;
|
||||
for (const auto& datum : m_waveData) {
|
||||
if (datum != 0) {
|
||||
dataZeroed = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return
|
||||
WaveVoice::zeroed()
|
||||
&& dataZeroed
|
||||
&& !enabled()
|
||||
&& (length() == 0)
|
||||
&& (level() == 0);
|
||||
}
|
||||
|
||||
bool enabled() const { return !!m_enabled; }
|
||||
void setEnabled(bool value) { m_enabled = value; }
|
||||
bool enabled() const;
|
||||
void setEnabled(bool value);
|
||||
|
||||
int length() const { return m_soundLength; }
|
||||
void setLength(int value) { m_soundLength = value; }
|
||||
int length() const;
|
||||
void setLength(int value);
|
||||
|
||||
int level() const { return m_outputLevel; }
|
||||
void setLevel(int value) { m_outputLevel = value; }
|
||||
int level() const;
|
||||
void setLevel(int value);
|
||||
|
||||
int packedWaveDatum(int i) const {
|
||||
assert(i < 16);
|
||||
return m_waveData[i];
|
||||
}
|
||||
|
||||
void setPackedWaveDatum(int i, uint8_t value) {
|
||||
assert(i < 16);
|
||||
m_waveData[i] = value;
|
||||
}
|
||||
|
||||
int waveDatum(int i) const {
|
||||
assert(i < 32);
|
||||
const auto packed = packedWaveDatum(i >> 1);
|
||||
return i & 1 ? Processor::lowNibble(packed) : Processor::highNibble(packed);
|
||||
}
|
||||
int packedWaveDatum(int i) const;
|
||||
void setPackedWaveDatum(int i, uint8_t value);
|
||||
int waveDatum(int i) const;
|
||||
|
||||
private:
|
||||
int m_enabled;
|
||||
@ -311,37 +195,24 @@ namespace EightBit {
|
||||
// NR4X
|
||||
class WhiteNoiseWaveVoice : public AudioVoice {
|
||||
public:
|
||||
WhiteNoiseWaveVoice() {}
|
||||
WhiteNoiseWaveVoice();
|
||||
|
||||
virtual void reset() override {
|
||||
AudioVoice::reset();
|
||||
m_envelope.reset();
|
||||
m_soundLength = m_polynomialShiftClockFrequency = m_polynomialCounterSteps = m_frequencyDivisionRatio = 0;
|
||||
}
|
||||
virtual void reset() override;
|
||||
virtual bool zeroed() const override;
|
||||
|
||||
virtual bool zeroed() const override {
|
||||
return
|
||||
AudioVoice::zeroed()
|
||||
&& m_envelope.zeroed()
|
||||
&& (length() == 0)
|
||||
&& (polynomialShiftClockFrequency() == 0)
|
||||
&& (polynomialCounterSteps() == 0)
|
||||
&& (frequencyDivisionRatio() == 0);
|
||||
}
|
||||
Envelope& envelope();
|
||||
|
||||
Envelope& envelope() { return m_envelope; }
|
||||
int length() const;
|
||||
void setLength(int value);
|
||||
|
||||
int length() const { return m_soundLength; }
|
||||
void setLength(int value) { m_soundLength = value; }
|
||||
int polynomialShiftClockFrequency() const;
|
||||
void setPolynomialShiftClockFrequency(int value);
|
||||
|
||||
int polynomialShiftClockFrequency() const { return m_polynomialShiftClockFrequency; }
|
||||
void setPolynomialShiftClockFrequency(int value) { m_polynomialShiftClockFrequency = value; }
|
||||
int polynomialCounterSteps() const;
|
||||
void setPolynomialCounterSteps(int value);
|
||||
|
||||
int polynomialCounterSteps() const { return m_polynomialCounterSteps; }
|
||||
void setPolynomialCounterSteps(int value) { m_polynomialCounterSteps = value; }
|
||||
|
||||
int frequencyDivisionRatio() const { return m_frequencyDivisionRatio; }
|
||||
void setFrequencyDivisionRatio(int value) { m_frequencyDivisionRatio = value; }
|
||||
int frequencyDivisionRatio() const;
|
||||
void setFrequencyDivisionRatio(int value);
|
||||
|
||||
private:
|
||||
Envelope m_envelope;
|
||||
@ -353,42 +224,24 @@ namespace EightBit {
|
||||
|
||||
class OutputChannel final {
|
||||
public:
|
||||
OutputChannel() {}
|
||||
OutputChannel();
|
||||
|
||||
void reset() {
|
||||
m_vin = false;
|
||||
m_outputLevel = 0;
|
||||
for (auto& outputVoice : m_outputVoices)
|
||||
outputVoice = false;
|
||||
}
|
||||
void reset();
|
||||
bool zeroed() const;
|
||||
|
||||
bool zeroed() const {
|
||||
return
|
||||
!vin()
|
||||
&& outputLevel() == 0
|
||||
&& !m_outputVoices[0] && !m_outputVoices[1] && !m_outputVoices[2] && !m_outputVoices[3];
|
||||
}
|
||||
bool vin() const;
|
||||
void setVin(bool value);
|
||||
|
||||
bool vin() const { return m_vin; }
|
||||
void setVin(bool value) { m_vin = value; }
|
||||
int outputLevel() const;
|
||||
void setOutputLevel(int value);
|
||||
|
||||
int outputLevel() const { return m_outputLevel; }
|
||||
void setOutputLevel(int value) { m_outputLevel = value; }
|
||||
bool& outputVoice(int voice);
|
||||
bool& outputVoice1();
|
||||
bool& outputVoice2();
|
||||
bool& outputVoice3();
|
||||
bool& outputVoice4();
|
||||
|
||||
bool& outputVoice(int voice) { return m_outputVoices[voice]; }
|
||||
bool& outputVoice1() { return m_outputVoices[0]; }
|
||||
bool& outputVoice2() { return m_outputVoices[1]; }
|
||||
bool& outputVoice3() { return m_outputVoices[2]; }
|
||||
bool& outputVoice4() { return m_outputVoices[3]; }
|
||||
|
||||
void dump() const {
|
||||
std::cout
|
||||
<< "Output channel: "
|
||||
<< "Vin:" << vin()
|
||||
<< ",Output level=" << outputLevel()
|
||||
<< ",Voices:" << (int)m_outputVoices[0] << (int)m_outputVoices[1] << (int)m_outputVoices[2] << (int)m_outputVoices[3]
|
||||
<< std::endl;
|
||||
}
|
||||
void dump() const;
|
||||
|
||||
private:
|
||||
bool m_vin;
|
||||
@ -398,396 +251,141 @@ namespace EightBit {
|
||||
|
||||
class Audio final {
|
||||
public:
|
||||
Audio(int cyclesPerSecond)
|
||||
: m_frameSequencer(cyclesPerSecond),
|
||||
m_enabled(false) {
|
||||
Audio(int cyclesPerSecond);
|
||||
|
||||
m_voices[0] = std::make_shared<SweptEnvelopedRectangularVoice>(cyclesPerSecond);
|
||||
m_voices[1] = std::make_shared<EnvelopedRectangularVoice>(cyclesPerSecond);
|
||||
m_voices[2] = std::make_shared<UserDefinedWaveVoice>(cyclesPerSecond);
|
||||
m_voices[3] = std::make_shared<WhiteNoiseWaveVoice>();
|
||||
std::shared_ptr<AudioVoice> voice(int i);
|
||||
SweptEnvelopedRectangularVoice* voice1();
|
||||
EnvelopedRectangularVoice* voice2();
|
||||
UserDefinedWaveVoice* voice3();
|
||||
WhiteNoiseWaveVoice* voice4();
|
||||
|
||||
m_frameSequencer.FrameStep.connect(std::bind(&Audio::Sequencer_FrameStep, this, std::placeholders::_1));
|
||||
m_frameSequencer.LengthStep.connect(std::bind(&Audio::Sequencer_LengthStep, this, std::placeholders::_1));
|
||||
m_frameSequencer.VolumeStep.connect(std::bind(&Audio::Sequencer_VolumeStep, this, std::placeholders::_1));
|
||||
m_frameSequencer.SweepStep.connect(std::bind(&Audio::Sequencer_SweepStep, this, std::placeholders::_1));
|
||||
}
|
||||
OutputChannel& channel(int i);
|
||||
OutputChannel& channel1();
|
||||
OutputChannel& channel2();
|
||||
|
||||
std::shared_ptr<AudioVoice> voice(int i) { return m_voices[i]; }
|
||||
bool enabled() const;
|
||||
void setEnabled(bool value);
|
||||
|
||||
SweptEnvelopedRectangularVoice* voice1() {
|
||||
return (SweptEnvelopedRectangularVoice*)voice(0).get();
|
||||
}
|
||||
void reset();
|
||||
bool zeroed() const;
|
||||
|
||||
EnvelopedRectangularVoice* voice2() {
|
||||
return (EnvelopedRectangularVoice*)voice(1).get();
|
||||
}
|
||||
|
||||
UserDefinedWaveVoice* voice3() {
|
||||
return (UserDefinedWaveVoice*)voice(2).get();
|
||||
}
|
||||
|
||||
WhiteNoiseWaveVoice* voice4() {
|
||||
return (WhiteNoiseWaveVoice*)voice(3).get();
|
||||
}
|
||||
|
||||
OutputChannel& channel(int i) { return m_channels[i]; }
|
||||
OutputChannel& channel1() { return channel(0); }
|
||||
OutputChannel& channel2() { return channel(1); }
|
||||
|
||||
bool enabled() const { return m_enabled; }
|
||||
|
||||
void setEnabled(bool value) {
|
||||
m_enabled = value;
|
||||
if (!enabled())
|
||||
reset();
|
||||
}
|
||||
|
||||
void reset() {
|
||||
m_enabled = false;
|
||||
for (auto voice : m_voices)
|
||||
voice->reset();
|
||||
for (auto& channel : m_channels)
|
||||
channel.reset();
|
||||
}
|
||||
|
||||
bool zeroed() const {
|
||||
auto channelsZeroed = m_channels[0].zeroed() && m_channels[1].zeroed();
|
||||
auto voice1Zero = m_voices[0]->zeroed();
|
||||
auto voice2Zero = m_voices[1]->zeroed();
|
||||
auto voice3Zero = m_voices[2]->zeroed();
|
||||
auto voice4Zero = m_voices[3]->zeroed();
|
||||
auto voicesZeroed = voice1Zero && voice2Zero && voice3Zero && voice4Zero;
|
||||
return !enabled() && channelsZeroed && voicesZeroed;
|
||||
}
|
||||
|
||||
void dumpVoice(int i) const {
|
||||
std::cout << "** Voice " << i + 1 << std::endl;
|
||||
m_voices[i]->dump();
|
||||
}
|
||||
|
||||
void dumpVoices() const {
|
||||
for (int i = 0; i < 4; ++i)
|
||||
dumpVoice(i);
|
||||
}
|
||||
|
||||
void dumpChannel(int i) const {
|
||||
std::cout << "** Channel " << i + 1 << std::endl;
|
||||
m_channels[i].dump();
|
||||
}
|
||||
|
||||
void dumpChannels() const {
|
||||
for (int i = 0; i < 2; ++i)
|
||||
dumpChannel(i);
|
||||
}
|
||||
|
||||
void dump() const {
|
||||
dumpVoices();
|
||||
dumpChannels();
|
||||
}
|
||||
void dumpVoice(int i) const;
|
||||
void dumpVoices() const;
|
||||
void dumpChannel(int i) const;
|
||||
void dumpChannels() const;
|
||||
void dump() const;
|
||||
|
||||
//
|
||||
|
||||
bool voice1On() const { return true; }
|
||||
bool voice2On() const { return true; }
|
||||
bool voice3On() const { return true; }
|
||||
bool voice4On() const { return true; }
|
||||
bool voice1On() const;
|
||||
bool voice2On() const;
|
||||
bool voice3On() const;
|
||||
bool voice4On() const;
|
||||
|
||||
//
|
||||
|
||||
uint8_t toNRx1(int i) {
|
||||
auto voice = (RectangularVoice*)m_voices[i].get();
|
||||
return
|
||||
(voice->waveFormDutyCycle() << 6)
|
||||
| Processor::Mask6;
|
||||
}
|
||||
uint8_t toNRx1(int i);
|
||||
void fromNRx1(int i, uint8_t value);
|
||||
|
||||
void fromNRx1(int i, uint8_t value) {
|
||||
auto voice = (RectangularVoice*)m_voices[i].get();
|
||||
voice->setWaveFormDutyCycle((value >> 6) & Processor::Mask2); // Bits 6-7
|
||||
voice->setLength(value & Processor::Mask6); // Bits 0-5
|
||||
}
|
||||
uint8_t toNRx2(int i);
|
||||
void fromNRx2(int i, uint8_t value);
|
||||
|
||||
uint8_t toNRx2(int i) {
|
||||
auto voice = (EnvelopedRectangularVoice*)m_voices[i].get();
|
||||
auto& envelope = voice->envelope();
|
||||
return
|
||||
(envelope.default() << 4)
|
||||
| (envelope.direction() << 3)
|
||||
| envelope.stepLength();
|
||||
}
|
||||
|
||||
void fromNRx2(int i, uint8_t value) {
|
||||
auto voice = (EnvelopedRectangularVoice*)m_voices[i].get();
|
||||
auto& envelope = voice->envelope();
|
||||
envelope.setDefault((value >> 4) & Processor::Mask4); // Bits 4-7
|
||||
envelope.setDirection((value >> 3) & Processor::Mask1); // Bit 3
|
||||
envelope.setStepLength(value & Processor::Mask3); // Bits 0-2
|
||||
}
|
||||
|
||||
uint8_t toNRx3(int i) {
|
||||
return Processor::Mask8;
|
||||
}
|
||||
|
||||
void fromNRx3(int i, uint8_t value) {
|
||||
auto voice = (WaveVoice*)m_voices[i].get();
|
||||
voice->setFrequencyLowOrder(value);
|
||||
}
|
||||
uint8_t toNRx3(int i);
|
||||
void fromNRx3(int i, uint8_t value);
|
||||
|
||||
// Sound mode 1 register: Sweep
|
||||
|
||||
uint8_t toNR10() {
|
||||
auto& sweep = voice1()->sweep();
|
||||
return
|
||||
Processor::Bit7
|
||||
| (sweep.time() << 4)
|
||||
| (sweep.direction() << 3)
|
||||
| sweep.shift();
|
||||
}
|
||||
|
||||
void fromNR10(uint8_t value) {
|
||||
auto& sweep = voice1()->sweep();
|
||||
sweep.setTime((value >> 4) & Processor::Mask3); // Bits 4-6
|
||||
sweep.setDirection((value >> 3) & Processor::Mask1); // Bit 3
|
||||
sweep.setShift(value & Processor::Mask3); // Bits 0-2
|
||||
}
|
||||
uint8_t toNR10();
|
||||
void fromNR10(uint8_t value);
|
||||
|
||||
// Sound mode 1 register: Sound length / Wave pattern duty
|
||||
uint8_t toNR11() { return toNRx1(0); }
|
||||
void fromNR11(uint8_t value) { fromNRx1(0, value); }
|
||||
uint8_t toNR11();
|
||||
void fromNR11(uint8_t value);
|
||||
|
||||
// Sound mode 1 register: Envelope
|
||||
uint8_t toNR12() { return toNRx2(0); }
|
||||
void fromNR12(uint8_t value) { fromNRx2(0, value); }
|
||||
uint8_t toNR12();
|
||||
void fromNR12(uint8_t value);
|
||||
|
||||
// Sound mode 1 register: Frequency lo
|
||||
uint8_t toNR13() { return toNRx3(0); }
|
||||
void fromNR13(uint8_t value) { fromNRx3(0, value); }
|
||||
uint8_t toNR13();
|
||||
void fromNR13(uint8_t value);
|
||||
|
||||
// Sound mode 1 register: Frequency hi
|
||||
|
||||
uint8_t toNR14() {
|
||||
return
|
||||
Processor::Bit7
|
||||
| (voice1()->type() << 6)
|
||||
| Processor::Mask6;
|
||||
}
|
||||
|
||||
void fromNR14(uint8_t value) {
|
||||
voice1()->setInitialise((value >> 7) & Processor::Mask1); // Bits 7
|
||||
voice1()->setType((value >> 6) & Processor::Mask1); // Bits 6
|
||||
voice1()->setFrequencyHighOrder(value & Processor::Mask3); // Bits 0-2
|
||||
}
|
||||
uint8_t toNR14();
|
||||
void fromNR14(uint8_t value);
|
||||
|
||||
// Sound mode 2 register: Sound length / Wave pattern duty
|
||||
uint8_t toNR21() { return toNRx1(1); }
|
||||
void fromNR21(uint8_t value) { fromNRx1(1, value); }
|
||||
uint8_t toNR21();
|
||||
void fromNR21(uint8_t value);
|
||||
|
||||
// Sound mode 2 register: Envelope
|
||||
uint8_t toNR22() { return toNRx2(1); }
|
||||
void fromNR22(uint8_t value) { fromNRx2(1, value); }
|
||||
uint8_t toNR22();
|
||||
void fromNR22(uint8_t value);
|
||||
|
||||
// Sound mode 2 register: Frequency lo
|
||||
uint8_t toNR23() { return toNRx3(1); }
|
||||
void fromNR23(uint8_t value) { fromNRx3(1, value); }
|
||||
uint8_t toNR23();
|
||||
void fromNR23(uint8_t value);
|
||||
|
||||
// Sound mode 2 register: Frequency hi
|
||||
|
||||
uint8_t toNR24() {
|
||||
return
|
||||
Processor::Bit7
|
||||
| (voice2()->type() << 6)
|
||||
| Processor::Mask6;
|
||||
}
|
||||
|
||||
void fromNR24(uint8_t value) {
|
||||
voice2()->setInitialise((value >> 7) & Processor::Mask1); // Bits 7
|
||||
voice2()->setType((value >> 6) & Processor::Mask1); // Bits 6
|
||||
voice2()->setFrequencyHighOrder(value & Processor::Mask3); // Bits 0-2
|
||||
}
|
||||
uint8_t toNR24();
|
||||
void fromNR24(uint8_t value);
|
||||
|
||||
// Sound mode 3 register: Sound on/off
|
||||
|
||||
uint8_t toNR30() {
|
||||
return
|
||||
(voice3()->enabled() << 7)
|
||||
| Processor::Mask7;
|
||||
}
|
||||
|
||||
void fromNR30(uint8_t value) {
|
||||
voice3()->setEnabled((value >> 7) & Processor::Mask1); // Bit 7
|
||||
}
|
||||
uint8_t toNR30();
|
||||
void fromNR30(uint8_t value);
|
||||
|
||||
// Sound mode 3 register: Sound length
|
||||
|
||||
uint8_t toNR31() {
|
||||
return voice3()->length();
|
||||
}
|
||||
|
||||
void fromNR31(uint8_t value) {
|
||||
voice3()->setLength(value);
|
||||
}
|
||||
uint8_t toNR31();
|
||||
void fromNR31(uint8_t value);
|
||||
|
||||
// Sound mode 3 register: Select output level
|
||||
|
||||
uint8_t toNR32() {
|
||||
return
|
||||
Processor::Bit7
|
||||
| Processor::Bit6
|
||||
| voice3()->level() << 5
|
||||
| Processor::Mask5;
|
||||
}
|
||||
|
||||
void fromNR32(uint8_t value) {
|
||||
voice3()->setLevel((value >> 5) & Processor::Mask2); // Bits 6-5
|
||||
}
|
||||
uint8_t toNR32();
|
||||
void fromNR32(uint8_t value);
|
||||
|
||||
// Sound mode 3 register: Frequency lo
|
||||
uint8_t toNR33() { return toNRx3(2); }
|
||||
void fromNR33(uint8_t value) { fromNRx3(2, value); }
|
||||
uint8_t toNR33();
|
||||
void fromNR33(uint8_t value);
|
||||
|
||||
// Sound mode 3 register: Frequency hi
|
||||
|
||||
uint8_t toNR34() {
|
||||
return
|
||||
Processor::Bit7
|
||||
| (voice3()->type() << 6)
|
||||
| Processor::Mask6;
|
||||
}
|
||||
|
||||
void fromNR34(uint8_t value) {
|
||||
voice3()->setInitialise((value >> 7) & Processor::Mask1); // Bits 7
|
||||
voice3()->setType((value >> 6) & Processor::Mask1); // Bits 6
|
||||
voice3()->setFrequencyHighOrder(value & Processor::Mask3); // Bits 0-2
|
||||
}
|
||||
uint8_t toNR34();
|
||||
void fromNR34(uint8_t value);
|
||||
|
||||
// Sound mode 4 register: Sound length
|
||||
|
||||
uint8_t toNR41() {
|
||||
return
|
||||
Processor::Bit7
|
||||
| Processor::Bit6
|
||||
| voice4()->length();
|
||||
}
|
||||
|
||||
void fromNR41(uint8_t value) {
|
||||
voice4()->setLength(value);
|
||||
}
|
||||
uint8_t toNR41();
|
||||
void fromNR41(uint8_t value);
|
||||
|
||||
// Sound mode 4 register: Envelope
|
||||
uint8_t toNR42() { return toNRx2(3); }
|
||||
void fromNR42(uint8_t value) { fromNRx2(3, value); }
|
||||
uint8_t toNR42();
|
||||
void fromNR42(uint8_t value);
|
||||
|
||||
// Sound mode 4 register: Polynomial counter
|
||||
|
||||
uint8_t toNR43() {
|
||||
return
|
||||
(voice4()->polynomialShiftClockFrequency() << 4)
|
||||
| voice4()->polynomialCounterSteps() << 3
|
||||
| voice4()->frequencyDivisionRatio();
|
||||
}
|
||||
|
||||
void fromNR43(uint8_t value) {
|
||||
voice4()->setPolynomialShiftClockFrequency((value >> 4) & Processor::Mask4); // Bits 4-7
|
||||
voice4()->setPolynomialCounterSteps((value >> 3) & Processor::Mask1); // Bit 3
|
||||
voice4()->setFrequencyDivisionRatio(value & Processor::Mask3); // Bits 0-2
|
||||
}
|
||||
uint8_t toNR43();
|
||||
void fromNR43(uint8_t value);
|
||||
|
||||
// Sound mode 4 register: counter/consecutive; inital
|
||||
|
||||
uint8_t toNR44() {
|
||||
return
|
||||
Processor::Bit7
|
||||
| (voice4()->type() << 6)
|
||||
| Processor::Mask6;
|
||||
}
|
||||
|
||||
void fromNR44(uint8_t value) {
|
||||
voice4()->setInitialise((value >> 7) & Processor::Mask1); // Bit 7
|
||||
voice4()->setType((value >> 6) & Processor::Mask1); // Bit 6
|
||||
}
|
||||
uint8_t toNR44();
|
||||
void fromNR44(uint8_t value);
|
||||
|
||||
// Channel control: on-off/volume
|
||||
|
||||
uint8_t toNR50() {
|
||||
return
|
||||
(channel2().vin() << 7)
|
||||
| (channel2().outputLevel() << 4)
|
||||
| (channel2().vin() << 3)
|
||||
| channel2().outputLevel();
|
||||
}
|
||||
|
||||
void fromNR50(uint8_t value) {
|
||||
channel2().setVin((value >> 7) & Processor::Mask1); // Bit 7
|
||||
channel2().setOutputLevel((value >> 4) & Processor::Mask3); // Bits 4-6
|
||||
channel1().setVin((value >> 3) & Processor::Mask1); // Bit 3
|
||||
channel1().setOutputLevel(value & Processor::Mask3); // Bits 0-2
|
||||
}
|
||||
uint8_t toNR50();
|
||||
void fromNR50(uint8_t value);
|
||||
|
||||
// Selection of Sound output terminal
|
||||
|
||||
uint8_t toNR51() {
|
||||
return
|
||||
(channel2().outputVoice4() << 7)
|
||||
| (channel2().outputVoice3() << 6)
|
||||
| (channel2().outputVoice2() << 5)
|
||||
| (channel2().outputVoice1() << 4)
|
||||
| (channel1().outputVoice4() << 3)
|
||||
| (channel1().outputVoice3() << 2)
|
||||
| (channel1().outputVoice2() << 1)
|
||||
| (int)channel1().outputVoice1();
|
||||
}
|
||||
|
||||
void fromNR51(uint8_t value) {
|
||||
channel2().outputVoice4() = (value >> 7) & Processor::Mask1; // Bit 7
|
||||
channel2().outputVoice3() = (value >> 6) & Processor::Mask1; // Bit 6
|
||||
channel2().outputVoice2() = (value >> 5) & Processor::Mask1; // Bit 5
|
||||
channel2().outputVoice1() = (value >> 4) & Processor::Mask1; // Bit 4
|
||||
channel1().outputVoice4() = (value >> 3) & Processor::Mask1; // Bit 3
|
||||
channel1().outputVoice3() = (value >> 2) & Processor::Mask1; // Bit 2
|
||||
channel1().outputVoice2() = (value >> 1) & Processor::Mask1; // Bit 1
|
||||
channel1().outputVoice1() = value & Processor::Mask1; // Bit 0
|
||||
}
|
||||
uint8_t toNR51();
|
||||
void fromNR51(uint8_t value);
|
||||
|
||||
// Sound on/off
|
||||
uint8_t toNR52();
|
||||
void fromNR52(uint8_t value);
|
||||
|
||||
uint8_t toNR52() {
|
||||
return
|
||||
(enabled() << 7)
|
||||
| Processor::Bit6 | Processor::Bit5 | Processor::Bit4
|
||||
| (voice4On() << 3)
|
||||
| (voice3On() << 2)
|
||||
| (voice2On() << 1)
|
||||
| (int)voice1On();
|
||||
}
|
||||
void setPackedWaveDatum(int i, uint8_t value);
|
||||
uint8_t packedWaveDatum(int i);
|
||||
|
||||
void fromNR52(uint8_t value) {
|
||||
setEnabled((value >> 7) & Processor::Mask1); // Bit 7
|
||||
}
|
||||
void stepFrame(int cycles);
|
||||
|
||||
void setPackedWaveDatum(int i, uint8_t value) {
|
||||
voice3()->setPackedWaveDatum(i, value);
|
||||
}
|
||||
|
||||
uint8_t packedWaveDatum(int i) {
|
||||
return voice3()->packedWaveDatum(i);
|
||||
}
|
||||
|
||||
void stepFrame(int cycles) {
|
||||
m_frameSequencer.step(cycles);
|
||||
}
|
||||
|
||||
void Sequencer_FrameStep(const int step) {
|
||||
}
|
||||
|
||||
void Sequencer_LengthStep(const int step) {
|
||||
}
|
||||
|
||||
void Sequencer_VolumeStep(const int step) {
|
||||
}
|
||||
|
||||
void Sequencer_SweepStep(const int step) {
|
||||
}
|
||||
void Sequencer_FrameStep(int step);
|
||||
void Sequencer_LengthStep(int step);
|
||||
void Sequencer_VolumeStep(int step);
|
||||
void Sequencer_SweepStep(int step);
|
||||
|
||||
private:
|
||||
AudioFrame m_frameSequencer;
|
||||
|
849
LR35902/src/Audio.cpp
Normal file
849
LR35902/src/Audio.cpp
Normal file
@ -0,0 +1,849 @@
|
||||
#include "stdafx.h"
|
||||
#include "Audio.h"
|
||||
|
||||
//
|
||||
|
||||
EightBit::GameBoy::Envelope::Envelope() {}
|
||||
|
||||
void EightBit::GameBoy::Envelope::reset() {
|
||||
m_defaultValue = m_direction = m_stepLength = 0;
|
||||
}
|
||||
|
||||
bool EightBit::GameBoy::Envelope::zeroed() const {
|
||||
return (default() == 0) && (stepLength() == 0) && (direction() == Attenuate);
|
||||
}
|
||||
|
||||
int EightBit::GameBoy::Envelope::default() const {
|
||||
return m_defaultValue;
|
||||
}
|
||||
|
||||
void EightBit::GameBoy::Envelope::setDefault(int value) {
|
||||
m_defaultValue = value;
|
||||
}
|
||||
|
||||
EightBit::GameBoy::Envelope::Direction EightBit::GameBoy::Envelope::direction() const {
|
||||
return (Direction)m_direction;
|
||||
}
|
||||
|
||||
void EightBit::GameBoy::Envelope::setDirection(int value) {
|
||||
m_direction = value;
|
||||
}
|
||||
|
||||
void EightBit::GameBoy::Envelope::setDirection(Direction value) {
|
||||
setDirection((int)value);
|
||||
}
|
||||
|
||||
int EightBit::GameBoy::Envelope::stepLength() const {
|
||||
return m_stepLength;
|
||||
}
|
||||
|
||||
void EightBit::GameBoy::Envelope::setStepLength(int value) {
|
||||
m_stepLength = value;
|
||||
}
|
||||
|
||||
void EightBit::GameBoy::Envelope::dump() const {
|
||||
std::cout << "Envelope: default=" << default() << ",direction=" << direction() << ",step length=" << stepLength() << std::endl;
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
EightBit::GameBoy::Sweep::Sweep() {}
|
||||
|
||||
void EightBit::GameBoy::Sweep::reset() {
|
||||
m_time = m_direction = m_shift = 0;
|
||||
}
|
||||
|
||||
bool EightBit::GameBoy::Sweep::zeroed() const {
|
||||
return (time() == 0) && (shift() == 0) && (direction() == Addition);
|
||||
}
|
||||
|
||||
int EightBit::GameBoy::Sweep::time() const {
|
||||
return m_time;
|
||||
}
|
||||
|
||||
void EightBit::GameBoy::Sweep::setTime(int value) {
|
||||
m_time = value;
|
||||
}
|
||||
|
||||
EightBit::GameBoy::Sweep::Direction EightBit::GameBoy::Sweep::direction() const {
|
||||
return (Direction)m_direction;
|
||||
}
|
||||
|
||||
void EightBit::GameBoy::Sweep::setDirection(int value) {
|
||||
m_direction = value;
|
||||
}
|
||||
|
||||
void EightBit::GameBoy::Sweep::setDirection(Direction value) {
|
||||
setDirection((int)value);
|
||||
}
|
||||
|
||||
int EightBit::GameBoy::Sweep::shift() const {
|
||||
return m_shift;
|
||||
}
|
||||
|
||||
void EightBit::GameBoy::Sweep::setShift(int value) {
|
||||
m_shift = value;
|
||||
}
|
||||
|
||||
void EightBit::GameBoy::Sweep::dump() const {
|
||||
std::cout << "Sweep: time=" << time() << ",direction=" << direction() << ",shift=" << shift() << std::endl;
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
EightBit::GameBoy::AudioVoice::AudioVoice() {}
|
||||
|
||||
void EightBit::GameBoy::AudioVoice::reset() {
|
||||
m_counterContinuous = m_initialise = 0;
|
||||
}
|
||||
|
||||
bool EightBit::GameBoy::AudioVoice::zeroed() const {
|
||||
return !initialise() && (type() == Continuous);
|
||||
}
|
||||
|
||||
EightBit::GameBoy::AudioVoice::Type EightBit::GameBoy::AudioVoice::type() const {
|
||||
return (Type)m_counterContinuous;
|
||||
}
|
||||
void EightBit::GameBoy::AudioVoice::setType(int value) {
|
||||
m_counterContinuous = value;
|
||||
}
|
||||
void EightBit::GameBoy::AudioVoice::setType(Type value) {
|
||||
setType((int)value);
|
||||
}
|
||||
|
||||
bool EightBit::GameBoy::AudioVoice::initialise() const {
|
||||
return !!m_initialise;
|
||||
}
|
||||
void EightBit::GameBoy::AudioVoice::setInitialise(bool value) {
|
||||
m_initialise = value;
|
||||
}
|
||||
|
||||
void EightBit::GameBoy::AudioVoice::dump() const {
|
||||
std::cout << "Audio Voice: type=" << type() << ",initialise=" << initialise() << std::endl;
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
EightBit::GameBoy::WaveVoice::WaveVoice(int cyclesPerSecond)
|
||||
: m_cyclesPerSecond(cyclesPerSecond) {}
|
||||
|
||||
void EightBit::GameBoy::WaveVoice::reset() {
|
||||
AudioVoice::reset();
|
||||
m_frequencyLowOrder = m_frequencyHighOrder = 0;
|
||||
}
|
||||
|
||||
bool EightBit::GameBoy::WaveVoice::zeroed() const {
|
||||
return AudioVoice::zeroed() && (frequency() == 0);
|
||||
}
|
||||
|
||||
int EightBit::GameBoy::WaveVoice::frequencyLowOrder() const {
|
||||
return m_frequencyLowOrder;
|
||||
}
|
||||
|
||||
void EightBit::GameBoy::WaveVoice::setFrequencyLowOrder(int value) {
|
||||
assert(value < Processor::Bit8);
|
||||
m_frequencyLowOrder = value;
|
||||
}
|
||||
|
||||
int EightBit::GameBoy::WaveVoice::frequencyHighOrder() const {
|
||||
return m_frequencyHighOrder;
|
||||
}
|
||||
|
||||
void EightBit::GameBoy::WaveVoice::setFrequencyHighOrder(int value) {
|
||||
assert(value < Processor::Bit3);
|
||||
m_frequencyHighOrder = value;
|
||||
}
|
||||
|
||||
int EightBit::GameBoy::WaveVoice::frequency() const {
|
||||
return (m_frequencyHighOrder << 8) | m_frequencyLowOrder;
|
||||
}
|
||||
|
||||
int EightBit::GameBoy::WaveVoice::hertz() const {
|
||||
// f = 4194304 / (4 x 8 x (2048 - X)) Hz
|
||||
auto division = 4 * 8 * (2048 - frequency());
|
||||
return m_cyclesPerSecond / division;
|
||||
}
|
||||
|
||||
void EightBit::GameBoy::WaveVoice::setFrequency(int value) {
|
||||
assert(value < Processor::Bit11);
|
||||
m_frequencyHighOrder = (value >> 8) & Processor::Mask3;
|
||||
m_frequencyLowOrder = value & Processor::Mask8;
|
||||
}
|
||||
|
||||
void EightBit::GameBoy::WaveVoice::dump() const {
|
||||
AudioVoice::dump();
|
||||
std::cout << "Wave Voice: frequency=" << frequency() << " (" << hertz() << ")" << std::endl;
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
EightBit::GameBoy::RectangularVoice::RectangularVoice(int cyclesPerSecond)
|
||||
: WaveVoice(cyclesPerSecond) {}
|
||||
|
||||
void EightBit::GameBoy::RectangularVoice::reset() {
|
||||
WaveVoice::reset();
|
||||
m_waveFormDutyCycle = m_soundLength = 0;
|
||||
}
|
||||
|
||||
bool EightBit::GameBoy::RectangularVoice::zeroed() const {
|
||||
return WaveVoice::zeroed() && (waveFormDutyCycle() == 0) && (length() == 0);
|
||||
}
|
||||
|
||||
int EightBit::GameBoy::RectangularVoice::waveFormDutyCycle() const {
|
||||
return m_waveFormDutyCycle;
|
||||
}
|
||||
|
||||
void EightBit::GameBoy::RectangularVoice::setWaveFormDutyCycle(int value) {
|
||||
m_waveFormDutyCycle = value;
|
||||
}
|
||||
|
||||
int EightBit::GameBoy::RectangularVoice::length() const {
|
||||
return m_soundLength;
|
||||
}
|
||||
|
||||
void EightBit::GameBoy::RectangularVoice::setLength(int value) {
|
||||
m_soundLength = value;
|
||||
}
|
||||
|
||||
void EightBit::GameBoy::RectangularVoice::dump() const {
|
||||
WaveVoice::dump();
|
||||
std::cout << "Rectangular Voice: wave form duty=" << waveFormDutyCycle() << ",length=" << length() << std::endl;
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
EightBit::GameBoy::EnvelopedRectangularVoice::EnvelopedRectangularVoice(int cyclesPerSecond)
|
||||
: RectangularVoice(cyclesPerSecond) {}
|
||||
|
||||
void EightBit::GameBoy::EnvelopedRectangularVoice::reset() {
|
||||
RectangularVoice::reset();
|
||||
m_envelope.reset();
|
||||
}
|
||||
|
||||
bool EightBit::GameBoy::EnvelopedRectangularVoice::zeroed() const {
|
||||
return RectangularVoice::zeroed() && m_envelope.zeroed();
|
||||
}
|
||||
|
||||
EightBit::GameBoy::Envelope& EightBit::GameBoy::EnvelopedRectangularVoice::envelope() {
|
||||
return m_envelope;
|
||||
}
|
||||
|
||||
void EightBit::GameBoy::EnvelopedRectangularVoice::dump() const {
|
||||
RectangularVoice::dump();
|
||||
m_envelope.dump();
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
EightBit::GameBoy::SweptEnvelopedRectangularVoice::SweptEnvelopedRectangularVoice(int cyclesPerSecond)
|
||||
: EnvelopedRectangularVoice(cyclesPerSecond) {}
|
||||
|
||||
void EightBit::GameBoy::SweptEnvelopedRectangularVoice::reset() {
|
||||
EnvelopedRectangularVoice::reset();
|
||||
m_sweep.reset();
|
||||
}
|
||||
|
||||
bool EightBit::GameBoy::SweptEnvelopedRectangularVoice::zeroed() const {
|
||||
return EnvelopedRectangularVoice::zeroed() && m_sweep.zeroed();
|
||||
}
|
||||
|
||||
EightBit::GameBoy::Sweep& EightBit::GameBoy::SweptEnvelopedRectangularVoice::sweep() {
|
||||
return m_sweep;
|
||||
}
|
||||
|
||||
void EightBit::GameBoy::SweptEnvelopedRectangularVoice::dump() const {
|
||||
EnvelopedRectangularVoice::dump();
|
||||
m_sweep.dump();
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
EightBit::GameBoy::UserDefinedWaveVoice::UserDefinedWaveVoice(int cyclesPerSecond)
|
||||
: WaveVoice(cyclesPerSecond) {}
|
||||
|
||||
void EightBit::GameBoy::UserDefinedWaveVoice::reset() {
|
||||
WaveVoice::reset();
|
||||
m_enabled = m_soundLength = m_outputLevel = 0;
|
||||
for (auto& datum : m_waveData)
|
||||
datum = 0;
|
||||
}
|
||||
|
||||
bool EightBit::GameBoy::UserDefinedWaveVoice::zeroed() const {
|
||||
bool dataZeroed = true;
|
||||
for (const auto& datum : m_waveData) {
|
||||
if (datum != 0) {
|
||||
dataZeroed = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return WaveVoice::zeroed() && dataZeroed && !enabled() && (length() == 0) && (level() == 0);
|
||||
}
|
||||
|
||||
bool EightBit::GameBoy::UserDefinedWaveVoice::enabled() const {
|
||||
return !!m_enabled;
|
||||
}
|
||||
|
||||
void EightBit::GameBoy::UserDefinedWaveVoice::setEnabled(bool value) {
|
||||
m_enabled = value;
|
||||
}
|
||||
|
||||
int EightBit::GameBoy::UserDefinedWaveVoice::length() const {
|
||||
return m_soundLength;
|
||||
}
|
||||
|
||||
void EightBit::GameBoy::UserDefinedWaveVoice::setLength(int value) {
|
||||
m_soundLength = value;
|
||||
}
|
||||
|
||||
int EightBit::GameBoy::UserDefinedWaveVoice::level() const {
|
||||
return m_outputLevel;
|
||||
}
|
||||
|
||||
void EightBit::GameBoy::UserDefinedWaveVoice::setLevel(int value) {
|
||||
m_outputLevel = value;
|
||||
}
|
||||
|
||||
int EightBit::GameBoy::UserDefinedWaveVoice::packedWaveDatum(int i) const {
|
||||
assert(i < 16);
|
||||
return m_waveData[i];
|
||||
}
|
||||
|
||||
void EightBit::GameBoy::UserDefinedWaveVoice::setPackedWaveDatum(int i, uint8_t value) {
|
||||
assert(i < 16);
|
||||
m_waveData[i] = value;
|
||||
}
|
||||
|
||||
int EightBit::GameBoy::UserDefinedWaveVoice::waveDatum(int i) const {
|
||||
assert(i < 32);
|
||||
const auto packed = packedWaveDatum(i >> 1);
|
||||
return i & 1 ? Processor::lowNibble(packed) : Processor::highNibble(packed);
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
EightBit::GameBoy::WhiteNoiseWaveVoice::WhiteNoiseWaveVoice() {}
|
||||
|
||||
void EightBit::GameBoy::WhiteNoiseWaveVoice::reset() {
|
||||
AudioVoice::reset();
|
||||
m_envelope.reset();
|
||||
m_soundLength = m_polynomialShiftClockFrequency = m_polynomialCounterSteps = m_frequencyDivisionRatio = 0;
|
||||
}
|
||||
|
||||
bool EightBit::GameBoy::WhiteNoiseWaveVoice::zeroed() const {
|
||||
return
|
||||
AudioVoice::zeroed()
|
||||
&& m_envelope.zeroed()
|
||||
&& (length() == 0)
|
||||
&& (polynomialShiftClockFrequency() == 0)
|
||||
&& (polynomialCounterSteps() == 0)
|
||||
&& (frequencyDivisionRatio() == 0);
|
||||
}
|
||||
|
||||
EightBit::GameBoy::Envelope& EightBit::GameBoy::WhiteNoiseWaveVoice::envelope() {
|
||||
return m_envelope;
|
||||
}
|
||||
|
||||
int EightBit::GameBoy::WhiteNoiseWaveVoice::length() const {
|
||||
return m_soundLength;
|
||||
}
|
||||
|
||||
void EightBit::GameBoy::WhiteNoiseWaveVoice::setLength(int value) {
|
||||
m_soundLength = value;
|
||||
}
|
||||
|
||||
int EightBit::GameBoy::WhiteNoiseWaveVoice::polynomialShiftClockFrequency() const {
|
||||
return m_polynomialShiftClockFrequency;
|
||||
}
|
||||
|
||||
void EightBit::GameBoy::WhiteNoiseWaveVoice::setPolynomialShiftClockFrequency(int value) {
|
||||
m_polynomialShiftClockFrequency = value;
|
||||
}
|
||||
|
||||
int EightBit::GameBoy::WhiteNoiseWaveVoice::polynomialCounterSteps() const {
|
||||
return m_polynomialCounterSteps;
|
||||
}
|
||||
|
||||
void EightBit::GameBoy::WhiteNoiseWaveVoice::setPolynomialCounterSteps(int value) {
|
||||
m_polynomialCounterSteps = value;
|
||||
}
|
||||
|
||||
int EightBit::GameBoy::WhiteNoiseWaveVoice::frequencyDivisionRatio() const {
|
||||
return m_frequencyDivisionRatio;
|
||||
}
|
||||
|
||||
void EightBit::GameBoy::WhiteNoiseWaveVoice::setFrequencyDivisionRatio(int value) {
|
||||
m_frequencyDivisionRatio = value;
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
EightBit::GameBoy::OutputChannel::OutputChannel() {}
|
||||
|
||||
void EightBit::GameBoy::OutputChannel::reset() {
|
||||
m_vin = false;
|
||||
m_outputLevel = 0;
|
||||
for (auto& outputVoice : m_outputVoices)
|
||||
outputVoice = false;
|
||||
}
|
||||
|
||||
bool EightBit::GameBoy::OutputChannel::zeroed() const {
|
||||
return
|
||||
!vin()
|
||||
&& outputLevel() == 0
|
||||
&& !m_outputVoices[0] && !m_outputVoices[1] && !m_outputVoices[2] && !m_outputVoices[3];
|
||||
}
|
||||
|
||||
bool EightBit::GameBoy::OutputChannel::vin() const {
|
||||
return m_vin;
|
||||
}
|
||||
void EightBit::GameBoy::OutputChannel::setVin(bool value) {
|
||||
m_vin = value;
|
||||
}
|
||||
|
||||
int EightBit::GameBoy::OutputChannel::outputLevel() const {
|
||||
return m_outputLevel;
|
||||
}
|
||||
void EightBit::GameBoy::OutputChannel::setOutputLevel(int value) {
|
||||
m_outputLevel = value;
|
||||
}
|
||||
|
||||
bool& EightBit::GameBoy::OutputChannel::outputVoice(int voice) {
|
||||
return m_outputVoices[voice];
|
||||
}
|
||||
bool& EightBit::GameBoy::OutputChannel::outputVoice1() {
|
||||
return m_outputVoices[0];
|
||||
}
|
||||
bool& EightBit::GameBoy::OutputChannel::outputVoice2() {
|
||||
return m_outputVoices[1];
|
||||
}
|
||||
bool& EightBit::GameBoy::OutputChannel::outputVoice3() {
|
||||
return m_outputVoices[2];
|
||||
}
|
||||
bool& EightBit::GameBoy::OutputChannel::outputVoice4() {
|
||||
return m_outputVoices[3];
|
||||
}
|
||||
|
||||
void EightBit::GameBoy::OutputChannel::dump() const {
|
||||
std::cout
|
||||
<< "Output channel: "
|
||||
<< "Vin:" << vin()
|
||||
<< ",Output level=" << outputLevel()
|
||||
<< ",Voices:" << (int)m_outputVoices[0] << (int)m_outputVoices[1] << (int)m_outputVoices[2] << (int)m_outputVoices[3]
|
||||
<< std::endl;
|
||||
}
|
||||
|
||||
|
||||
EightBit::GameBoy::Audio::Audio(int cyclesPerSecond)
|
||||
: m_frameSequencer(cyclesPerSecond),
|
||||
m_enabled(false) {
|
||||
|
||||
m_voices[0] = std::make_shared<SweptEnvelopedRectangularVoice>(cyclesPerSecond);
|
||||
m_voices[1] = std::make_shared<EnvelopedRectangularVoice>(cyclesPerSecond);
|
||||
m_voices[2] = std::make_shared<UserDefinedWaveVoice>(cyclesPerSecond);
|
||||
m_voices[3] = std::make_shared<WhiteNoiseWaveVoice>();
|
||||
|
||||
m_frameSequencer.FrameStep.connect(std::bind(&Audio::Sequencer_FrameStep, this, std::placeholders::_1));
|
||||
m_frameSequencer.LengthStep.connect(std::bind(&Audio::Sequencer_LengthStep, this, std::placeholders::_1));
|
||||
m_frameSequencer.VolumeStep.connect(std::bind(&Audio::Sequencer_VolumeStep, this, std::placeholders::_1));
|
||||
m_frameSequencer.SweepStep.connect(std::bind(&Audio::Sequencer_SweepStep, this, std::placeholders::_1));
|
||||
}
|
||||
|
||||
std::shared_ptr<EightBit::GameBoy::AudioVoice> EightBit::GameBoy::Audio::voice(int i) {
|
||||
return m_voices[i];
|
||||
}
|
||||
|
||||
EightBit::GameBoy::SweptEnvelopedRectangularVoice* EightBit::GameBoy::Audio::voice1() {
|
||||
return (SweptEnvelopedRectangularVoice*)voice(0).get();
|
||||
}
|
||||
|
||||
EightBit::GameBoy::EnvelopedRectangularVoice* EightBit::GameBoy::Audio::voice2() {
|
||||
return (EnvelopedRectangularVoice*)voice(1).get();
|
||||
}
|
||||
|
||||
EightBit::GameBoy::UserDefinedWaveVoice* EightBit::GameBoy::Audio::voice3() {
|
||||
return (UserDefinedWaveVoice*)voice(2).get();
|
||||
}
|
||||
|
||||
EightBit::GameBoy::WhiteNoiseWaveVoice* EightBit::GameBoy::Audio::voice4() {
|
||||
return (WhiteNoiseWaveVoice*)voice(3).get();
|
||||
}
|
||||
|
||||
EightBit::GameBoy::OutputChannel& EightBit::GameBoy::Audio::channel(int i) {
|
||||
return m_channels[i];
|
||||
}
|
||||
EightBit::GameBoy::OutputChannel& EightBit::GameBoy::Audio::channel1() {
|
||||
return channel(0);
|
||||
}
|
||||
EightBit::GameBoy::OutputChannel& EightBit::GameBoy::Audio::channel2() {
|
||||
return channel(1);
|
||||
}
|
||||
|
||||
bool EightBit::GameBoy::Audio::enabled() const {
|
||||
return m_enabled;
|
||||
}
|
||||
|
||||
void EightBit::GameBoy::Audio::setEnabled(bool value) {
|
||||
m_enabled = value;
|
||||
if (!enabled())
|
||||
reset();
|
||||
}
|
||||
|
||||
void EightBit::GameBoy::Audio::reset() {
|
||||
m_enabled = false;
|
||||
for (auto voice : m_voices)
|
||||
voice->reset();
|
||||
for (auto& channel : m_channels)
|
||||
channel.reset();
|
||||
}
|
||||
|
||||
bool EightBit::GameBoy::Audio::zeroed() const {
|
||||
auto channelsZeroed = m_channels[0].zeroed() && m_channels[1].zeroed();
|
||||
auto voice1Zero = m_voices[0]->zeroed();
|
||||
auto voice2Zero = m_voices[1]->zeroed();
|
||||
auto voice3Zero = m_voices[2]->zeroed();
|
||||
auto voice4Zero = m_voices[3]->zeroed();
|
||||
auto voicesZeroed = voice1Zero && voice2Zero && voice3Zero && voice4Zero;
|
||||
return !enabled() && channelsZeroed && voicesZeroed;
|
||||
}
|
||||
|
||||
void EightBit::GameBoy::Audio::dumpVoice(int i) const {
|
||||
std::cout << "** Voice " << i + 1 << std::endl;
|
||||
m_voices[i]->dump();
|
||||
}
|
||||
|
||||
void EightBit::GameBoy::Audio::dumpVoices() const {
|
||||
for (int i = 0; i < 4; ++i)
|
||||
dumpVoice(i);
|
||||
}
|
||||
|
||||
void EightBit::GameBoy::Audio::dumpChannel(int i) const {
|
||||
std::cout << "** Channel " << i + 1 << std::endl;
|
||||
m_channels[i].dump();
|
||||
}
|
||||
|
||||
void EightBit::GameBoy::Audio::dumpChannels() const {
|
||||
for (int i = 0; i < 2; ++i)
|
||||
dumpChannel(i);
|
||||
}
|
||||
|
||||
void EightBit::GameBoy::Audio::dump() const {
|
||||
dumpVoices();
|
||||
dumpChannels();
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
bool EightBit::GameBoy::Audio::voice1On() const {
|
||||
return true;
|
||||
}
|
||||
bool EightBit::GameBoy::Audio::voice2On() const {
|
||||
return true;
|
||||
}
|
||||
bool EightBit::GameBoy::Audio::voice3On() const {
|
||||
return true;
|
||||
}
|
||||
bool EightBit::GameBoy::Audio::voice4On() const {
|
||||
return true;
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
uint8_t EightBit::GameBoy::Audio::toNRx1(int i) {
|
||||
auto voice = (RectangularVoice*)m_voices[i].get();
|
||||
return (voice->waveFormDutyCycle() << 6) | Processor::Mask6;
|
||||
}
|
||||
|
||||
void EightBit::GameBoy::Audio::fromNRx1(int i, uint8_t value) {
|
||||
auto voice = (RectangularVoice*)m_voices[i].get();
|
||||
voice->setWaveFormDutyCycle((value >> 6) & Processor::Mask2); // Bits 6-7
|
||||
voice->setLength(value & Processor::Mask6); // Bits 0-5
|
||||
}
|
||||
|
||||
uint8_t EightBit::GameBoy::Audio::toNRx2(int i) {
|
||||
auto voice = (EnvelopedRectangularVoice*)m_voices[i].get();
|
||||
auto& envelope = voice->envelope();
|
||||
return (envelope.default() << 4) | (envelope.direction() << 3) | envelope.stepLength();
|
||||
}
|
||||
|
||||
void EightBit::GameBoy::Audio::fromNRx2(int i, uint8_t value) {
|
||||
auto voice = (EnvelopedRectangularVoice*)m_voices[i].get();
|
||||
auto& envelope = voice->envelope();
|
||||
envelope.setDefault((value >> 4) & Processor::Mask4); // Bits 4-7
|
||||
envelope.setDirection((value >> 3) & Processor::Mask1); // Bit 3
|
||||
envelope.setStepLength(value & Processor::Mask3); // Bits 0-2
|
||||
}
|
||||
|
||||
uint8_t EightBit::GameBoy::Audio::toNRx3(int i) {
|
||||
return Processor::Mask8;
|
||||
}
|
||||
|
||||
void EightBit::GameBoy::Audio::fromNRx3(int i, uint8_t value) {
|
||||
auto voice = (WaveVoice*)m_voices[i].get();
|
||||
voice->setFrequencyLowOrder(value);
|
||||
}
|
||||
|
||||
// Sound mode 1 register: Sweep
|
||||
|
||||
uint8_t EightBit::GameBoy::Audio::toNR10() {
|
||||
auto& sweep = voice1()->sweep();
|
||||
return
|
||||
Processor::Bit7
|
||||
| (sweep.time() << 4)
|
||||
| (sweep.direction() << 3)
|
||||
| sweep.shift();
|
||||
}
|
||||
|
||||
void EightBit::GameBoy::Audio::fromNR10(uint8_t value) {
|
||||
auto& sweep = voice1()->sweep();
|
||||
sweep.setTime((value >> 4) & Processor::Mask3); // Bits 4-6
|
||||
sweep.setDirection((value >> 3) & Processor::Mask1); // Bit 3
|
||||
sweep.setShift(value & Processor::Mask3); // Bits 0-2
|
||||
}
|
||||
|
||||
// Sound mode 1 register: Sound length / Wave pattern duty
|
||||
uint8_t EightBit::GameBoy::Audio::toNR11() {
|
||||
return toNRx1(0);
|
||||
}
|
||||
void EightBit::GameBoy::Audio::fromNR11(uint8_t value) {
|
||||
fromNRx1(0, value);
|
||||
}
|
||||
|
||||
// Sound mode 1 register: Envelope
|
||||
uint8_t EightBit::GameBoy::Audio::toNR12() {
|
||||
return toNRx2(0);
|
||||
}
|
||||
void EightBit::GameBoy::Audio::fromNR12(uint8_t value) {
|
||||
fromNRx2(0, value);
|
||||
}
|
||||
|
||||
// Sound mode 1 register: Frequency lo
|
||||
uint8_t EightBit::GameBoy::Audio::toNR13() {
|
||||
return toNRx3(0);
|
||||
}
|
||||
void EightBit::GameBoy::Audio::fromNR13(uint8_t value) {
|
||||
fromNRx3(0, value);
|
||||
}
|
||||
|
||||
// Sound mode 1 register: Frequency hi
|
||||
|
||||
uint8_t EightBit::GameBoy::Audio::toNR14() {
|
||||
return Processor::Bit7 | (voice1()->type() << 6) | Processor::Mask6;
|
||||
}
|
||||
|
||||
void EightBit::GameBoy::Audio::fromNR14(uint8_t value) {
|
||||
voice1()->setInitialise((value >> 7) & Processor::Mask1); // Bits 7
|
||||
voice1()->setType((value >> 6) & Processor::Mask1); // Bits 6
|
||||
voice1()->setFrequencyHighOrder(value & Processor::Mask3); // Bits 0-2
|
||||
}
|
||||
|
||||
// Sound mode 2 register: Sound length / Wave pattern duty
|
||||
uint8_t EightBit::GameBoy::Audio::toNR21() {
|
||||
return toNRx1(1);
|
||||
}
|
||||
void EightBit::GameBoy::Audio::fromNR21(uint8_t value) {
|
||||
fromNRx1(1, value);
|
||||
}
|
||||
|
||||
// Sound mode 2 register: Envelope
|
||||
uint8_t EightBit::GameBoy::Audio::toNR22() {
|
||||
return toNRx2(1);
|
||||
}
|
||||
void EightBit::GameBoy::Audio::fromNR22(uint8_t value) {
|
||||
fromNRx2(1, value);
|
||||
}
|
||||
|
||||
// Sound mode 2 register: Frequency lo
|
||||
uint8_t EightBit::GameBoy::Audio::toNR23() {
|
||||
return toNRx3(1);
|
||||
}
|
||||
void EightBit::GameBoy::Audio::fromNR23(uint8_t value) {
|
||||
fromNRx3(1, value);
|
||||
}
|
||||
|
||||
// Sound mode 2 register: Frequency hi
|
||||
|
||||
uint8_t EightBit::GameBoy::Audio::toNR24() {
|
||||
return Processor::Bit7 | (voice2()->type() << 6) | Processor::Mask6;
|
||||
}
|
||||
|
||||
void EightBit::GameBoy::Audio::fromNR24(uint8_t value) {
|
||||
voice2()->setInitialise((value >> 7) & Processor::Mask1); // Bits 7
|
||||
voice2()->setType((value >> 6) & Processor::Mask1); // Bits 6
|
||||
voice2()->setFrequencyHighOrder(value & Processor::Mask3); // Bits 0-2
|
||||
}
|
||||
|
||||
// Sound mode 3 register: Sound on/off
|
||||
|
||||
uint8_t EightBit::GameBoy::Audio::toNR30() {
|
||||
return (voice3()->enabled() << 7) | Processor::Mask7;
|
||||
}
|
||||
|
||||
void EightBit::GameBoy::Audio::fromNR30(uint8_t value) {
|
||||
voice3()->setEnabled((value >> 7) & Processor::Mask1); // Bit 7
|
||||
}
|
||||
|
||||
// Sound mode 3 register: Sound length
|
||||
|
||||
uint8_t EightBit::GameBoy::Audio::toNR31() {
|
||||
return voice3()->length();
|
||||
}
|
||||
|
||||
void EightBit::GameBoy::Audio::fromNR31(uint8_t value) {
|
||||
voice3()->setLength(value);
|
||||
}
|
||||
|
||||
// Sound mode 3 register: Select output level
|
||||
|
||||
uint8_t EightBit::GameBoy::Audio::toNR32() {
|
||||
return Processor::Bit7 | Processor::Bit6 | voice3()->level() << 5 | Processor::Mask5;
|
||||
}
|
||||
|
||||
void EightBit::GameBoy::Audio::fromNR32(uint8_t value) {
|
||||
voice3()->setLevel((value >> 5) & Processor::Mask2); // Bits 6-5
|
||||
}
|
||||
|
||||
// Sound mode 3 register: Frequency lo
|
||||
uint8_t EightBit::GameBoy::Audio::toNR33() {
|
||||
return toNRx3(2);
|
||||
}
|
||||
void EightBit::GameBoy::Audio::fromNR33(uint8_t value) {
|
||||
fromNRx3(2, value);
|
||||
}
|
||||
|
||||
// Sound mode 3 register: Frequency hi
|
||||
|
||||
uint8_t EightBit::GameBoy::Audio::toNR34() {
|
||||
return Processor::Bit7 | (voice3()->type() << 6) | Processor::Mask6;
|
||||
}
|
||||
|
||||
void EightBit::GameBoy::Audio::fromNR34(uint8_t value) {
|
||||
voice3()->setInitialise((value >> 7) & Processor::Mask1); // Bits 7
|
||||
voice3()->setType((value >> 6) & Processor::Mask1); // Bits 6
|
||||
voice3()->setFrequencyHighOrder(value & Processor::Mask3); // Bits 0-2
|
||||
}
|
||||
|
||||
// Sound mode 4 register: Sound length
|
||||
|
||||
uint8_t EightBit::GameBoy::Audio::toNR41() {
|
||||
return Processor::Bit7 | Processor::Bit6 | voice4()->length();
|
||||
}
|
||||
|
||||
void EightBit::GameBoy::Audio::fromNR41(uint8_t value) {
|
||||
voice4()->setLength(value);
|
||||
}
|
||||
|
||||
// Sound mode 4 register: Envelope
|
||||
uint8_t EightBit::GameBoy::Audio::toNR42() {
|
||||
return toNRx2(3);
|
||||
}
|
||||
void EightBit::GameBoy::Audio::fromNR42(uint8_t value) {
|
||||
fromNRx2(3, value);
|
||||
}
|
||||
|
||||
// Sound mode 4 register: Polynomial counter
|
||||
|
||||
uint8_t EightBit::GameBoy::Audio::toNR43() {
|
||||
return
|
||||
(voice4()->polynomialShiftClockFrequency() << 4)
|
||||
| voice4()->polynomialCounterSteps() << 3
|
||||
| voice4()->frequencyDivisionRatio();
|
||||
}
|
||||
|
||||
void EightBit::GameBoy::Audio::fromNR43(uint8_t value) {
|
||||
voice4()->setPolynomialShiftClockFrequency((value >> 4) & Processor::Mask4); // Bits 4-7
|
||||
voice4()->setPolynomialCounterSteps((value >> 3) & Processor::Mask1); // Bit 3
|
||||
voice4()->setFrequencyDivisionRatio(value & Processor::Mask3); // Bits 0-2
|
||||
}
|
||||
|
||||
// Sound mode 4 register: counter/consecutive; inital
|
||||
|
||||
uint8_t EightBit::GameBoy::Audio::toNR44() {
|
||||
return Processor::Bit7 | (voice4()->type() << 6) | Processor::Mask6;
|
||||
}
|
||||
|
||||
void EightBit::GameBoy::Audio::fromNR44(uint8_t value) {
|
||||
voice4()->setInitialise((value >> 7) & Processor::Mask1); // Bit 7
|
||||
voice4()->setType((value >> 6) & Processor::Mask1); // Bit 6
|
||||
}
|
||||
|
||||
// Channel control: on-off/volume
|
||||
|
||||
uint8_t EightBit::GameBoy::Audio::toNR50() {
|
||||
return
|
||||
(channel2().vin() << 7)
|
||||
| (channel2().outputLevel() << 4)
|
||||
| (channel2().vin() << 3)
|
||||
| channel2().outputLevel();
|
||||
}
|
||||
|
||||
void EightBit::GameBoy::Audio::fromNR50(uint8_t value) {
|
||||
channel2().setVin((value >> 7) & Processor::Mask1); // Bit 7
|
||||
channel2().setOutputLevel((value >> 4) & Processor::Mask3); // Bits 4-6
|
||||
channel1().setVin((value >> 3) & Processor::Mask1); // Bit 3
|
||||
channel1().setOutputLevel(value & Processor::Mask3); // Bits 0-2
|
||||
}
|
||||
|
||||
// Selection of Sound output terminal
|
||||
|
||||
uint8_t EightBit::GameBoy::Audio::toNR51() {
|
||||
return
|
||||
(channel2().outputVoice4() << 7)
|
||||
| (channel2().outputVoice3() << 6)
|
||||
| (channel2().outputVoice2() << 5)
|
||||
| (channel2().outputVoice1() << 4)
|
||||
| (channel1().outputVoice4() << 3)
|
||||
| (channel1().outputVoice3() << 2)
|
||||
| (channel1().outputVoice2() << 1)
|
||||
| (int)channel1().outputVoice1();
|
||||
}
|
||||
|
||||
void EightBit::GameBoy::Audio::fromNR51(uint8_t value) {
|
||||
channel2().outputVoice4() = (value >> 7) & Processor::Mask1; // Bit 7
|
||||
channel2().outputVoice3() = (value >> 6) & Processor::Mask1; // Bit 6
|
||||
channel2().outputVoice2() = (value >> 5) & Processor::Mask1; // Bit 5
|
||||
channel2().outputVoice1() = (value >> 4) & Processor::Mask1; // Bit 4
|
||||
channel1().outputVoice4() = (value >> 3) & Processor::Mask1; // Bit 3
|
||||
channel1().outputVoice3() = (value >> 2) & Processor::Mask1; // Bit 2
|
||||
channel1().outputVoice2() = (value >> 1) & Processor::Mask1; // Bit 1
|
||||
channel1().outputVoice1() = value & Processor::Mask1; // Bit 0
|
||||
}
|
||||
|
||||
// Sound on/off
|
||||
|
||||
uint8_t EightBit::GameBoy::Audio::toNR52() {
|
||||
return
|
||||
(enabled() << 7)
|
||||
| Processor::Bit6 | Processor::Bit5 | Processor::Bit4
|
||||
| (voice4On() << 3)
|
||||
| (voice3On() << 2)
|
||||
| (voice2On() << 1)
|
||||
| (int)voice1On();
|
||||
}
|
||||
|
||||
void EightBit::GameBoy::Audio::fromNR52(uint8_t value) {
|
||||
setEnabled((value >> 7) & Processor::Mask1); // Bit 7
|
||||
}
|
||||
|
||||
void EightBit::GameBoy::Audio::setPackedWaveDatum(int i, uint8_t value) {
|
||||
voice3()->setPackedWaveDatum(i, value);
|
||||
}
|
||||
|
||||
uint8_t EightBit::GameBoy::Audio::packedWaveDatum(int i) {
|
||||
return voice3()->packedWaveDatum(i);
|
||||
}
|
||||
|
||||
void EightBit::GameBoy::Audio::stepFrame(int cycles) {
|
||||
m_frameSequencer.step(cycles);
|
||||
}
|
||||
|
||||
void EightBit::GameBoy::Audio::Sequencer_FrameStep(const int step) {
|
||||
}
|
||||
|
||||
void EightBit::GameBoy::Audio::Sequencer_LengthStep(const int step) {
|
||||
}
|
||||
|
||||
void EightBit::GameBoy::Audio::Sequencer_VolumeStep(const int step) {
|
||||
}
|
||||
|
||||
void EightBit::GameBoy::Audio::Sequencer_SweepStep(const int step) {
|
||||
}
|
@ -152,6 +152,7 @@
|
||||
<ClInclude Include="stdafx.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="Audio.cpp" />
|
||||
<ClCompile Include="GameBoyBus.cpp" />
|
||||
<ClCompile Include="Disassembler.cpp" />
|
||||
<ClCompile Include="Display.cpp" />
|
||||
|
@ -64,5 +64,8 @@
|
||||
<ClCompile Include="GameBoyBus.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Audio.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
</Project>
|
Loading…
x
Reference in New Issue
Block a user