A different approach to audio handling. Just holding data at the moment.

Signed-off-by: Adrian.Conlon <adrian.conlon@gmail.com>
This commit is contained in:
Adrian.Conlon 2017-09-17 22:19:07 +01:00
parent 462b82add6
commit 61ae382a98
6 changed files with 373 additions and 78 deletions

310
LR35902/inc/Audio.h Normal file
View File

@ -0,0 +1,310 @@
#pragma once
#include <array>
#include <memory>
#include <iostream>
#include <cassert>
#include <Processor.h>
namespace EightBit {
namespace GameBoy {
class Envelope {
public:
Envelope() {}
enum Direction { Attenuate, Amplify };
int default() const { return m_defaultValue; }
void setDefault(int value) { m_defaultValue = 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 { 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;
}
private:
int m_defaultValue;
int m_direction;
int m_stepLength;
};
class Sweep {
public:
Sweep() {}
enum Direction { Addition, Subtraction };
int time() const { return m_time; }
void setTime(int value) { m_time = 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 { return m_shift; }
void setShift(int value) { m_shift = value; }
void dump() const {
std::cout << "Sweep: time=" << time() << ",direction=" << direction() << ",shift=" << shift() << std::endl;
}
private:
int m_time;
int m_direction;
int m_shift;
};
class AudioVoice {
public:
AudioVoice() {}
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); }
bool initialise() const { return !!m_initialise; }
void setInitialise(bool value) { m_initialise = value; }
virtual void dump() const {
std::cout << "Audio Voice: type=" << type() << ",initialise=" << initialise() << std::endl;
}
private:
int m_counterContinuous;
int m_initialise;
};
class WaveVoice : public AudioVoice {
public:
WaveVoice() {}
int frequencyLowOrder() const { return m_frequencyLowOrder; }
void setFrequencyLowOrder(int value) {
assert(value < Processor::Bit8);
m_frequencyLowOrder = value;
}
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;
}
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() << std::endl;
}
private:
int m_frequencyLowOrder; // 8 bits
int m_frequencyHighOrder; // 3 bits
};
class RectangularVoice : public WaveVoice {
public:
RectangularVoice() {}
int waveFormDutyCycle() const { return m_waveFormDutyCycle; }
void setWaveFormDutyCycle(int value) { m_waveFormDutyCycle = 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;
}
private:
int m_waveFormDutyCycle;
int m_soundLength;
};
// NR2X
class EnvelopedRectangularVoice : public RectangularVoice {
public:
EnvelopedRectangularVoice() {}
Envelope& envelope() { return m_envelope; }
virtual void dump() const override {
RectangularVoice::dump();
m_envelope.dump();
}
private:
Envelope m_envelope;
};
// NR1X
class SweptEnvelopedRectangularVoice : public EnvelopedRectangularVoice {
public:
SweptEnvelopedRectangularVoice() {}
Sweep& sweep() { return m_sweep; }
virtual void dump() const override {
EnvelopedRectangularVoice::dump();
m_sweep.dump();
}
private:
Sweep m_sweep;
};
// NR3X
class UserDefinedWaveVoice : public WaveVoice {
public:
UserDefinedWaveVoice() {}
bool enabled() const { return !!m_enabled; }
void setEnabled(bool value) { m_enabled = value; }
int length() const { return m_soundLength; }
void setLength(int value) { m_soundLength = value; }
int level() const { return m_outputLevel; }
void setLevel(int value) { m_outputLevel = 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);
}
private:
int m_enabled;
int m_soundLength;
int m_outputLevel;
std::array<uint8_t, 16> m_waveData;
};
// NR4X
class WhiteNoiseWaveVoice : public AudioVoice {
public:
WhiteNoiseWaveVoice() {}
Envelope& envelope() { return m_envelope; }
int length() const { return m_soundLength; }
void setLength(int value) { m_soundLength = value; }
int polynomialShiftClockFrequency() const { return m_polynomialShiftClockFrequency; }
void setPolynomialShiftClockFrequency(int value) { m_polynomialShiftClockFrequency = 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; }
private:
Envelope m_envelope;
int m_soundLength;
int m_polynomialShiftClockFrequency;
int m_polynomialCounterSteps;
int m_frequencyDivisionRatio;
};
class OutputChannel {
public:
OutputChannel() {}
bool vin() const { return m_vin; }
void setVin(bool value) { m_vin = value; }
int outputLevel() const { return m_outputLevel; }
void setOutputLevel(int value) { m_outputLevel = value; }
bool& outputVoice(int voice) { return m_outputVoice[voice]; }
bool& outputVoice1() { return m_outputVoice[0]; }
bool& outputVoice2() { return m_outputVoice[1]; }
bool& outputVoice3() { return m_outputVoice[2]; }
bool& outputVoice4() { return m_outputVoice[3]; }
private:
bool m_vin;
int m_outputLevel;
std::array<bool, 4> m_outputVoice;
};
class Audio {
public:
Audio() {
m_voices[0] = std::make_shared<SweptEnvelopedRectangularVoice>();
m_voices[1] = std::make_shared<EnvelopedRectangularVoice>();
m_voices[2] = std::make_shared<UserDefinedWaveVoice>();
m_voices[3] = std::make_shared<WhiteNoiseWaveVoice>();
}
std::shared_ptr<AudioVoice> voice(int i) { return m_voices[i]; }
SweptEnvelopedRectangularVoice* voice1() {
return (SweptEnvelopedRectangularVoice*)voice(0).get();
}
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; }
void dump() const {
for (int i = 0; i < 4; ++i) {
std::cout << "** Voice " << i + 1 << std::endl;
m_voices[i]->dump();
}
}
private:
std::array<std::shared_ptr<AudioVoice>, 4> m_voices;
std::array<OutputChannel, 2> m_channels;
bool m_enabled;
};
}
}

View File

@ -9,6 +9,8 @@
#include <Processor.h>
#include <Signal.h>
#include "Audio.h"
namespace EightBit {
namespace GameBoy {
class Bus : public EightBit::Bus {
@ -118,29 +120,7 @@ namespace EightBit {
Bus();
Signal<std::tuple<int, int>> Audio_SweepTimeModified;
Signal<std::tuple<int, int>> Audio_SweepDirectionModified;
Signal<std::tuple<int, int>> Audio_SweepShiftModified;
Signal<std::tuple<int, int>> Audio_WavePatternDutyModified;
Signal<std::tuple<int, int>> Audio_SoundLengthModified;
Signal<std::tuple<int, int>> Audio_DefaultEnvelopeVolumeModified;
Signal<std::tuple<int, int>> Audio_EnvelopeDirectionModified;
Signal<std::tuple<int, int>> Audio_EnvelopeStepLengthModified;
Signal<std::tuple<int, int>> Audio_FrequencyLoModified;
Signal<std::tuple<int, int>> Audio_InitialiseModified;
Signal<std::tuple<int, int>> Audio_CounterContinuousSelectionModified;
Signal<std::tuple<int, int>> Audio_FrequencyHiModified;
Signal<std::tuple<int, int>> Audio_SoundOnOffModified;
Signal<std::tuple<int, int>> Audio_OutputLevelModified;
Signal<std::tuple<int, int>> Audio_PolynomialShiftClockFrequencyModified;
Signal<std::tuple<int, int>> Audio_PolynomialCounterStepModified;
Signal<std::tuple<int, int>> Audio_FrequencyDivisionRatioModified;
Signal<std::tuple<int, int>> Audio_SO_VinOnOffModified;
Signal<std::tuple<int, int>> Audio_SO_OutputLevelModified;
Signal<std::tuple<int, int, int>> Audio_SO_SoundOutputModified;
Signal<int> Audio_AllSoundOnOffModified;
Signal<std::tuple<int, int>> Audio_ChannelOnOffModified;
Audio& audio() { return m_audio; }
void reset();
@ -337,6 +317,8 @@ namespace EightBit {
bool m_p11; // left/b
bool m_p10; // right/a
Audio m_audio;
void checkTimer(int cycles);
void validateCartridgeType();

View File

@ -246,120 +246,116 @@ void EightBit::GameBoy::Bus::Bus_WrittenByte(const uint16_t address) {
break;
case BASE + NR10: // Sound mode 1 register: Sweep
Audio_SweepTimeModified.fire(std::make_tuple(1, (value >> 4) & Processor::Mask3)); // Bits 4-6
Audio_SweepDirectionModified.fire(std::make_tuple(1, (value >> 3) & Processor::Mask1)); // Bits 3
Audio_SweepShiftModified.fire(std::make_tuple(1, value & Processor::Mask3)); // Bits 0-2
audio().voice1()->sweep().setTime((value >> 4) & Processor::Mask3); // Bits 4-6
audio().voice1()->sweep().setDirection((value >> 3) & Processor::Mask1); // Bit 3
audio().voice1()->sweep().setShift(value & Processor::Mask3); // Bits 0-2
break;
case BASE + NR11: // Sound mode 1 register: Sound length / Wave pattern duty
Audio_WavePatternDutyModified.fire(std::make_tuple(1, (value >> 6) & Processor::Mask2));// Bits 6-7
Audio_SoundLengthModified.fire(std::make_tuple(1, value & Processor::Mask6)); // Bits 0-5
audio().voice1()->setWaveFormDutyCycle((value >> 6) & Processor::Mask2); // Bits 6-7
audio().voice1()->setLength(value & Processor::Mask6); // Bits 0-5
break;
case BASE + NR12: // Sound mode 1 register: Envelope
Audio_DefaultEnvelopeVolumeModified.fire(std::make_tuple(1, (value >> 4) & Processor::Mask4)); // Bits 4-7
Audio_EnvelopeDirectionModified.fire(std::make_tuple(1, (value >> 3) & Processor::Mask1)); // Bits 3
Audio_EnvelopeStepLengthModified.fire(std::make_tuple(1, value & Processor::Mask3)); // Bits 0-2
audio().voice1()->envelope().setDefault((value >> 4) & Processor::Mask4); // Bits 4-7
audio().voice1()->envelope().setDirection((value >> 3) & Processor::Mask1); // Bit 3
audio().voice1()->envelope().setStepLength(value & Processor::Mask3); // Bits 0-2
break;
case BASE + NR13: // Sound mode 1 register: Frequency lo
Audio_FrequencyLoModified.fire(std::make_tuple(1, value));
audio().voice1()->setFrequencyLowOrder(value);
break;
case BASE + NR14: // Sound mode 1 register: Frequency hi
Audio_InitialiseModified.fire(std::make_tuple(1, (value >> 7) & Processor::Mask1)); // Bits 7
Audio_CounterContinuousSelectionModified.fire(std::make_tuple(1, (value >> 6) & Processor::Mask1));// Bits 6
Audio_FrequencyHiModified.fire(std::make_tuple(1, value & Processor::Mask3)); // Bits 0-2
audio().voice1()->setInitialise((value >> 7) & Processor::Mask1); // Bits 7
audio().voice1()->setType((value >> 6) & Processor::Mask1); // Bits 6
audio().voice1()->setFrequencyHighOrder(value & Processor::Mask3); // Bits 0-2
break;
case BASE + NR21: // Sound mode 2 register: Sound length / Wave pattern duty
Audio_WavePatternDutyModified.fire(std::make_tuple(2, (value >> 6) & Processor::Mask2));// Bits 6-7
Audio_SoundLengthModified.fire(std::make_tuple(2, Processor::Mask6)); // Bits 0-5
audio().voice2()->setWaveFormDutyCycle((value >> 6) & Processor::Mask2); // Bits 6-7
audio().voice2()->setLength(value & Processor::Mask6); // Bits 0-5
break;
case BASE + NR22: // Sound mode 2 register: Envelope
Audio_DefaultEnvelopeVolumeModified.fire(std::make_tuple(2, (value >> 4) & Processor::Mask4)); // Bits 4-7
Audio_EnvelopeDirectionModified.fire(std::make_tuple(2, (value >> 3) & Processor::Mask1)); // Bits 3
Audio_EnvelopeStepLengthModified.fire(std::make_tuple(2, value & Processor::Mask3)); // Bits 0-2
audio().voice2()->envelope().setDefault((value >> 4) & Processor::Mask4); // Bits 4-7
audio().voice2()->envelope().setDirection((value >> 3) & Processor::Mask1); // Bit 3
audio().voice2()->envelope().setStepLength(value & Processor::Mask3); // Bits 0-2
break;
case BASE + NR23: // Sound mode 2 register: Frequency lo
Audio_FrequencyLoModified.fire(std::make_tuple(2, value));
audio().voice2()->setFrequencyLowOrder(value);
break;
case BASE + NR24: // Sound mode 2 register: Frequency hi
Audio_InitialiseModified.fire(std::make_tuple(2, (value >> 7) & Processor::Mask1)); // Bits 7
Audio_CounterContinuousSelectionModified.fire(std::make_tuple(2, (value >> 6) & Processor::Mask1));// Bits 6
Audio_FrequencyHiModified.fire(std::make_tuple(2, value & Processor::Mask3)); // Bits 0-2
audio().voice2()->setInitialise((value >> 7) & Processor::Mask1); // Bit 7
audio().voice2()->setType((value >> 6) & Processor::Mask1); // Bit 6
audio().voice2()->setFrequencyHighOrder(value & Processor::Mask3); // Bits 0-2
break;
case BASE + NR30: // Sound mode 3 register: Sound on/off
Audio_SoundOnOffModified.fire(std::make_tuple(3, (value >> 7) & Processor::Mask1)); // Bits 0-2
audio().voice3()->setEnabled((value >> 7) & Processor::Mask1); // Bit 7
break;
case BASE + NR31: // Sound mode 3 register: Sound length
Audio_SoundLengthModified.fire(std::make_tuple(3, value));
audio().voice3()->setLength(value);
break;
case BASE + NR32: // Sound mode 3 register: Select output level
Audio_OutputLevelModified.fire(std::make_tuple(3, value));
audio().voice3()->setLevel(value);
break;
case BASE + NR33: // Sound mode 3 register: Frequency lo
Audio_FrequencyLoModified.fire(std::make_tuple(3, value));
audio().voice3()->setFrequencyLowOrder(value);
break;
case BASE + NR34: // Sound mode 3 register: Frequency hi
Audio_InitialiseModified.fire(std::make_tuple(3, (value >> 7) & Processor::Mask1)); // Bits 7
Audio_CounterContinuousSelectionModified.fire(std::make_tuple(3, (value >> 6) & Processor::Mask1));// Bits 6
Audio_FrequencyHiModified.fire(std::make_tuple(3, value & Processor::Mask3)); // Bits 0-2
audio().voice3()->setInitialise((value >> 7) & Processor::Mask1); // Bits 7
audio().voice3()->setType((value >> 6) & Processor::Mask1); // Bits 6
audio().voice3()->setFrequencyHighOrder(value & Processor::Mask3); // Bits 0-2
break;
case BASE + NR41: // Sound mode 4 register: Sound length
Audio_SoundLengthModified.fire(std::make_tuple(4, value & Processor::Mask6)); // Bits 0-5
audio().voice4()->setLength(value & Processor::Mask6); // Bits 0-5
break;
case BASE + NR42: // Sound mode 4 register: Envelope
Audio_DefaultEnvelopeVolumeModified.fire(std::make_tuple(4, (value >> 4) & Processor::Mask4)); // Bits 4-7
Audio_EnvelopeDirectionModified.fire(std::make_tuple(4, (value >> 3) & Processor::Mask1)); // Bits 3
Audio_EnvelopeStepLengthModified.fire(std::make_tuple(4, value & Processor::Mask3)); // Bits 0-2
audio().voice4()->envelope().setDefault((value >> 4) & Processor::Mask4); // Bits 4-7
audio().voice4()->envelope().setDirection((value >> 3) & Processor::Mask1); // Bit 3
audio().voice4()->envelope().setStepLength(value & Processor::Mask3); // Bits 0-2
break;
case BASE + NR43: // Sound mode 4 register: Polynomial counter
Audio_PolynomialShiftClockFrequencyModified.fire(std::make_tuple(4, (value >> 4) & Processor::Mask4)); // Bits 4-7
Audio_PolynomialCounterStepModified.fire(std::make_tuple(4, (value >> 3) & Processor::Mask1)); // Bit 3
Audio_FrequencyDivisionRatioModified.fire(std::make_tuple(4, value & Processor::Mask3)); // Bits 0-2
audio().voice4()->setPolynomialShiftClockFrequency((value >> 4) & Processor::Mask4); // Bits 4-7
audio().voice4()->setPolynomialCounterSteps((value >> 3) & Processor::Mask1); // Bit 3
audio().voice4()->setFrequencyDivisionRatio(value & Processor::Mask3); // Bits 0-2
break;
case BASE + NR44: // Sound mode 4 register: counter/consecutive; inital
Audio_InitialiseModified.fire(std::make_tuple(4, (value >> 7) & Processor::Mask1)); // Bit 7
Audio_CounterContinuousSelectionModified.fire(std::make_tuple(4, (value >> 6) & Processor::Mask1)); // Bits 6
audio().voice4()->setInitialise((value >> 7) & Processor::Mask1); // Bit 7
audio().voice4()->setType((value >> 6) & Processor::Mask1); // Bit 6
break;
case BASE + NR50: // Channel control/on-off/volume
Audio_SO_VinOnOffModified.fire(std::make_tuple(2, (value >> 7) & Processor::Mask1)); // Bit 7
Audio_SO_OutputLevelModified.fire(std::make_tuple(2, (value >> 4) & Processor::Mask3)); // Bits 4-6
Audio_SO_VinOnOffModified.fire(std::make_tuple(1, (value >> 3) & Processor::Mask1)); // Bit 3
Audio_SO_OutputLevelModified.fire(std::make_tuple(1, value & Processor::Mask3)); // Bits 0-2
case BASE + NR50: // Channel control: on-off/volume
audio().channel2().setVin((value >> 7) & Processor::Mask1); // Bit 7
audio().channel2().setOutputLevel((value >> 4) & Processor::Mask3); // Bits 4-6
audio().channel1().setVin((value >> 3) & Processor::Mask1); // Bit 3
audio().channel1().setOutputLevel(value & Processor::Mask3); // Bits 0-2
break;
case BASE + NR51:
Audio_SO_SoundOutputModified.fire(std::make_tuple(2, 4, (value >> 7) & Processor::Mask1)); // Bit 7
Audio_SO_SoundOutputModified.fire(std::make_tuple(2, 3, (value >> 6) & Processor::Mask1)); // Bit 6
Audio_SO_SoundOutputModified.fire(std::make_tuple(2, 2, (value >> 5) & Processor::Mask1)); // Bit 5
Audio_SO_SoundOutputModified.fire(std::make_tuple(2, 1, (value >> 4) & Processor::Mask1)); // Bit 4
Audio_SO_SoundOutputModified.fire(std::make_tuple(1, 4, (value >> 3) & Processor::Mask1)); // Bit 3
Audio_SO_SoundOutputModified.fire(std::make_tuple(1, 3, (value >> 2) & Processor::Mask1)); // Bit 2
Audio_SO_SoundOutputModified.fire(std::make_tuple(1, 2, (value >> 1) & Processor::Mask1)); // Bit 1
Audio_SO_SoundOutputModified.fire(std::make_tuple(1, 1, value & Processor::Mask1)); // Bit 0
audio().channel2().outputVoice4() = (value >> 7) & Processor::Mask1; // Bit 7
audio().channel2().outputVoice3() = (value >> 6) & Processor::Mask1; // Bit 6
audio().channel2().outputVoice2() = (value >> 5) & Processor::Mask1; // Bit 5
audio().channel2().outputVoice1() = (value >> 4) & Processor::Mask1; // Bit 4
audio().channel1().outputVoice4() = (value >> 3) & Processor::Mask1; // Bit 3
audio().channel1().outputVoice3() = (value >> 2) & Processor::Mask1; // Bit 2
audio().channel1().outputVoice2() = (value >> 1) & Processor::Mask1; // Bit 1
audio().channel1().outputVoice1() = value & Processor::Mask1; // Bit 0
break;
case BASE + NR52: // Sound on/off
Audio_AllSoundOnOffModified.fire((value >> 7) & Processor::Mask1); // Bit 7
Audio_ChannelOnOffModified.fire(std::make_tuple(4, (value >> 3) & Processor::Mask1)); // Bit 3
Audio_ChannelOnOffModified.fire(std::make_tuple(3, (value >> 2) & Processor::Mask1)); // Bit 2
Audio_ChannelOnOffModified.fire(std::make_tuple(2, (value >> 1) & Processor::Mask1)); // Bit 1
Audio_ChannelOnOffModified.fire(std::make_tuple(1, value & Processor::Mask1)); // Bit 0
audio().setEnabled((value >> 7) & Processor::Mask1); // Bit 7
break;
case BASE + LCDC:

View File

@ -140,6 +140,7 @@
</ItemDefinitionGroup>
<ItemGroup>
<ClInclude Include="..\inc\AbstractColourPalette.h" />
<ClInclude Include="..\inc\Audio.h" />
<ClInclude Include="..\inc\GameBoyBus.h" />
<ClInclude Include="..\inc\CharacterDefinition.h" />
<ClInclude Include="..\inc\Disassembler.h" />

View File

@ -38,6 +38,9 @@
<ClInclude Include="..\inc\ObjectAttribute.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\inc\Audio.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="stdafx.cpp">

View File

@ -10,8 +10,11 @@
#include <map>
#include <bitset>
#include <string>
#include <tuple>
#include <cassert>
#include <algorithm>
#include <memory>
#include <iostream>
#include <fstream>