diff --git a/platforms/desktop-shared/Makefile.common b/platforms/desktop-shared/Makefile.common
index 26a7da4..3f01197 100644
--- a/platforms/desktop-shared/Makefile.common
+++ b/platforms/desktop-shared/Makefile.common
@@ -17,7 +17,7 @@ SOURCES = $(EMULATOR_DESKTOP_SHARED_SRC)/main.cpp $(EMULATOR_DESKTOP_SHARED_SRC)
SOURCES += $(IMGUI_SRC)/imgui_impl_sdl.cpp $(IMGUI_SRC)/imgui_impl_opengl2.cpp $(IMGUI_SRC)/imgui.cpp $(IMGUI_SRC)/imgui_demo.cpp $(IMGUI_SRC)/imgui_draw.cpp $(IMGUI_SRC)/imgui_widgets.cpp $(IMGUI_FILEBROWSER_SRC)/ImGuiFileBrowser.cpp
-SOURCES += $(EMULATOR_SRC)/Audio.cpp $(EMULATOR_SRC)/Cartridge.cpp $(EMULATOR_SRC)/GearcolecoCore.cpp $(EMULATOR_SRC)/Input.cpp $(EMULATOR_SRC)/Memory.cpp $(EMULATOR_SRC)/opcodes.cpp $(EMULATOR_SRC)/opcodes_cb.cpp $(EMULATOR_SRC)/opcodes_ed.cpp $(EMULATOR_SRC)/Processor.cpp $(EMULATOR_SRC)/ColecoVisionIOPorts.cpp $(EMULATOR_SRC)/Video.cpp
+SOURCES += $(EMULATOR_SRC)/Audio.cpp $(EMULATOR_SRC)/Cartridge.cpp $(EMULATOR_SRC)/GearcolecoCore.cpp $(EMULATOR_SRC)/Input.cpp $(EMULATOR_SRC)/Memory.cpp $(EMULATOR_SRC)/opcodes.cpp $(EMULATOR_SRC)/opcodes_cb.cpp $(EMULATOR_SRC)/opcodes_ed.cpp $(EMULATOR_SRC)/Processor.cpp $(EMULATOR_SRC)/ColecoVisionIOPorts.cpp $(EMULATOR_SRC)/Video.cpp $(EMULATOR_SRC)/AY8910.cpp
SOURCES += $(EMULATOR_AUDIO_SRC)/Blip_Buffer.cpp $(EMULATOR_AUDIO_SRC)/Effects_Buffer.cpp $(EMULATOR_AUDIO_SRC)/Sms_Apu.cpp $(EMULATOR_AUDIO_SRC)/Multi_Buffer.cpp
diff --git a/platforms/desktop-shared/emu.cpp b/platforms/desktop-shared/emu.cpp
index 9ae2393..935f7fe 100644
--- a/platforms/desktop-shared/emu.cpp
+++ b/platforms/desktop-shared/emu.cpp
@@ -67,7 +67,7 @@ void emu_init(void)
gearcoleco->Init();
sound_queue = new Sound_Queue();
- sound_queue->start(44100, 2);
+ sound_queue->start(GC_AUDIO_SAMPLE_RATE, 2);
audio_buffer = new s16[GC_AUDIO_BUFFER_SIZE];
@@ -198,7 +198,7 @@ void emu_audio_volume(float volume)
void emu_audio_reset(void)
{
sound_queue->stop();
- sound_queue->start(44100, 2);
+ sound_queue->start(GC_AUDIO_SAMPLE_RATE, 2);
}
bool emu_is_audio_enabled(void)
diff --git a/platforms/libretro/Makefile.common b/platforms/libretro/Makefile.common
index 77e21ce..b84f1c7 100644
--- a/platforms/libretro/Makefile.common
+++ b/platforms/libretro/Makefile.common
@@ -5,6 +5,7 @@ SOURCES_CXX := $(CORE_DIR)/libretro.cpp \
$(SOURCE_DIR)/Processor.cpp \
$(SOURCE_DIR)/Video.cpp \
$(SOURCE_DIR)/Audio.cpp \
+ $(SOURCE_DIR)/AY8910.cpp \
$(SOURCE_DIR)/Input.cpp \
$(SOURCE_DIR)/Cartridge.cpp \
$(SOURCE_DIR)/ColecoVisionIOPorts.cpp \
diff --git a/platforms/windows/Gearcoleco.vcxproj b/platforms/windows/Gearcoleco.vcxproj
index c6ca9bb..6a7ded9 100644
--- a/platforms/windows/Gearcoleco.vcxproj
+++ b/platforms/windows/Gearcoleco.vcxproj
@@ -12,6 +12,7 @@
+
@@ -44,6 +45,7 @@
+
diff --git a/src/AY8910.cpp b/src/AY8910.cpp
new file mode 100644
index 0000000..1a4c647
--- /dev/null
+++ b/src/AY8910.cpp
@@ -0,0 +1,400 @@
+/*
+ * Gearcoleco - ColecoVision Emulator
+ * Copyright (C) 2021 Ignacio Sanchez
+
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see http://www.gnu.org/licenses/
+ *
+ */
+
+#include "AY8910.h"
+
+AY8910::AY8910()
+{
+ InitPointer(m_pBuffer);
+}
+
+AY8910::~AY8910()
+{
+ SafeDelete(m_pBuffer);
+}
+
+void AY8910::Init(int clockRate)
+{
+ m_pBuffer = new s16[GC_AUDIO_BUFFER_SIZE];
+ Reset(clockRate);
+}
+
+void AY8910::Reset(int clockRate)
+{
+ m_iClockRate = clockRate;
+
+ for (int i = 0; i < 16; i++)
+ {
+ m_Registers[i] = 0;
+ }
+
+ for (int i = 0; i < 3; i++)
+ {
+ m_TonePeriod[i] = 0;
+ m_ToneCounter[i] = 0;
+ m_Amplitude[i] = 0;
+ m_ToneDisable[i] = false;
+ m_NoiseDisable[i] = false;
+ m_EnvelopeMode[i] = false;
+ m_Sign[i] = false;
+ }
+
+ m_SelectedRegister = 0;
+ m_NoisePeriod = 0;
+ m_NoiseCounter = 0;
+ m_NoiseShift = 1;
+ m_EnvelopePeriod = 0;
+ m_EnvelopeCounter = 0;
+ m_EnvelopeSegment = false;
+ m_EnvelopeStep = 0;
+ m_EnvelopeVolume = 0;
+ m_iCycleCounter = 0;
+ m_iSampleCounter = 0;
+ m_iBufferIndex = 0;
+
+ for (int i = 0; i < GC_AUDIO_BUFFER_SIZE; i++)
+ {
+ m_pBuffer[i] = 0;
+ }
+
+ m_ElapsedCycles = 0;
+}
+
+void AY8910::WriteRegister(u8 value)
+{
+ Sync();
+
+ m_Registers[m_SelectedRegister] = value & kAY8910RegisterMask[m_SelectedRegister];
+
+ switch (m_SelectedRegister)
+ {
+ // Channel A tone period
+ case 0:
+ case 1:
+ {
+ m_TonePeriod[0] = (m_Registers[1] << 8) | m_Registers[0];
+ if (m_TonePeriod[0] == 0)
+ {
+ m_TonePeriod[0] = 1;
+ }
+ break;
+ }
+ // Channel B tone period
+ case 2:
+ case 3:
+ {
+ m_TonePeriod[1] = (m_Registers[3] << 8) | m_Registers[2];
+ if (m_TonePeriod[1] == 0)
+ {
+ m_TonePeriod[1] = 1;
+ }
+ break;
+ }
+ // Channel C tone period
+ case 4:
+ case 5:
+ {
+ m_TonePeriod[2] = (m_Registers[5] << 8) | m_Registers[4];
+ if (m_TonePeriod[2] == 0)
+ {
+ m_TonePeriod[2] = 1;
+ }
+ break;
+ }
+ // Noise period
+ case 6:
+ {
+ m_NoisePeriod = m_Registers[6];
+ if (m_NoisePeriod == 0)
+ {
+ m_NoisePeriod = 1;
+ }
+ break;
+ }
+ // Mixer
+ case 7:
+ {
+ m_ToneDisable[0] = IsSetBit(m_Registers[7], 0);
+ m_ToneDisable[1] = IsSetBit(m_Registers[7], 1);
+ m_ToneDisable[2] = IsSetBit(m_Registers[7], 2);
+ m_NoiseDisable[0] = IsSetBit(m_Registers[7], 3);
+ m_NoiseDisable[1] = IsSetBit(m_Registers[7], 4);
+ m_NoiseDisable[2] = IsSetBit(m_Registers[7], 5);
+ break;
+ }
+ // Channel A amplitude
+ case 8:
+ {
+ m_Amplitude[0] = m_Registers[8] & 0x0F;
+ m_EnvelopeMode[0] = IsSetBit(m_Registers[8], 4);
+ break;
+ }
+ // Channel B amplitude
+ case 9:
+ {
+ m_Amplitude[1] = m_Registers[9] & 0x0F;
+ m_EnvelopeMode[1] = IsSetBit(m_Registers[9], 4);
+ break;
+ }
+ // Channel C amplitude
+ case 10:
+ {
+ m_Amplitude[2] = m_Registers[10] & 0x0F;
+ m_EnvelopeMode[2] = IsSetBit(m_Registers[10], 4);
+ break;
+ }
+ // Envelope period
+ case 11:
+ case 12:
+ {
+ m_EnvelopePeriod = (m_Registers[12] << 8) | m_Registers[11];
+ break;
+ }
+ // Envelope shape
+ case 13:
+ {
+ m_EnvelopeCounter = 0;
+ m_EnvelopeSegment = false;
+ EnvelopeReset();
+ break;
+ }
+ default:
+ {
+ break;
+ }
+ }
+}
+
+u8 AY8910::ReadRegister()
+{
+ return m_Registers[m_SelectedRegister];
+}
+
+void AY8910::SelectRegister(u8 reg)
+{
+ m_SelectedRegister = reg & 0x0F;
+}
+
+void AY8910::EnvelopeReset()
+{
+ m_EnvelopeStep = 0;
+
+ if (m_EnvelopeSegment)
+ {
+ switch (m_Registers[13])
+ {
+ case 8:
+ case 11:
+ case 13:
+ case 14:
+ {
+ m_EnvelopeVolume = 0x0F;
+ break;
+ }
+ default:
+ {
+ m_EnvelopeVolume = 0x00;
+ break;
+ }
+ }
+ }
+ else
+ {
+ m_EnvelopeVolume = IsSetBit(m_Registers[13], 2) ? 0x00 : 0x0F;
+ }
+}
+
+void AY8910::Tick(unsigned int clockCycles)
+{
+ m_ElapsedCycles += clockCycles;
+}
+
+void AY8910::Sync()
+{
+ for (int i = 0; i < m_ElapsedCycles; i++)
+ {
+ m_iCycleCounter ++;
+ if (m_iCycleCounter >= 16)
+ {
+ m_iCycleCounter -= 16;
+
+ for (int i = 0; i < 3; i++)
+ {
+ m_ToneCounter[i]++;
+ if (m_ToneCounter[i] >= m_TonePeriod[i])
+ {
+ m_ToneCounter[i] = 0;
+ m_Sign[i] = !m_Sign[i];
+ }
+ }
+
+ m_NoiseCounter++;
+ if (m_NoiseCounter >= (m_NoisePeriod << 1))
+ {
+ m_NoiseCounter = 0;
+ m_NoiseShift = (m_NoiseShift >> 1) | (((m_NoiseShift ^ (m_NoiseShift >> 3)) & 0x01) << 16);
+ }
+
+ m_EnvelopeCounter++;
+ if (m_EnvelopeCounter >= (m_EnvelopePeriod << 1))
+ {
+ m_EnvelopeCounter = 0;
+
+ if (m_EnvelopeStep)
+ {
+ if (m_EnvelopeSegment)
+ {
+ if ((m_Registers[13] == 10) || (m_Registers[13] == 12))
+ {
+ m_EnvelopeVolume++;
+ }
+ else if ((m_Registers[13] == 8) || (m_Registers[13] == 14))
+ {
+ m_EnvelopeVolume--;
+ }
+ }
+ else
+ {
+ if (IsSetBit(m_Registers[13], 2))
+ {
+ m_EnvelopeVolume++;
+ }
+ else
+ {
+ m_EnvelopeVolume--;
+ }
+ }
+ }
+
+ m_EnvelopeStep++;
+ if (m_EnvelopeStep >= 16)
+ {
+ if ((m_Registers[13] & 0x09) == 0x08)
+ {
+ m_EnvelopeSegment = !m_EnvelopeSegment;
+ }
+ else
+ {
+ m_EnvelopeSegment = true;
+ }
+ EnvelopeReset();
+ }
+ }
+ }
+
+ m_iSampleCounter++;
+ int cyclesPerSample = m_iClockRate / GC_AUDIO_SAMPLE_RATE;
+ if (m_iSampleCounter >= cyclesPerSample)
+ {
+ m_iSampleCounter -= cyclesPerSample;
+ s16 sample = 0;
+
+ for (int i = 0; i < 3; i++)
+ {
+ if ((m_ToneDisable[i] || m_Sign[i]) && (m_NoiseDisable[i] || ((m_NoiseShift & 0x01) == 0x01)))
+ {
+ sample += m_EnvelopeMode[i] ? kAY8910VolumeTable[m_EnvelopeVolume] : kAY8910VolumeTable[m_Amplitude[i]];
+ }
+ }
+
+ m_pBuffer[m_iBufferIndex] = sample;
+ m_pBuffer[m_iBufferIndex + 1] = sample;
+ m_iBufferIndex += 2;
+
+ if (m_iBufferIndex >= GC_AUDIO_BUFFER_SIZE)
+ {
+ Log("SGM Audio buffer overflow");
+ m_iBufferIndex = 0;
+ }
+ }
+ }
+
+ m_ElapsedCycles = 0;
+}
+
+int AY8910::EndFrame(s16* pSampleBuffer)
+{
+ Sync();
+
+ int ret = 0;
+
+ if (IsValidPointer(pSampleBuffer))
+ {
+ ret = m_iBufferIndex;
+
+ for (int i = 0; i < m_iBufferIndex; i++)
+ {
+ pSampleBuffer[i] = m_pBuffer[i];
+ }
+ }
+
+ m_iBufferIndex = 0;
+
+ return ret;
+}
+
+void AY8910::SaveState(std::ostream& stream)
+{
+ stream.write(reinterpret_cast(m_Registers), sizeof(m_Registers));
+ stream.write(reinterpret_cast(&m_SelectedRegister), sizeof(m_SelectedRegister));
+ stream.write(reinterpret_cast(m_TonePeriod), sizeof(m_TonePeriod));
+ stream.write(reinterpret_cast(m_ToneCounter), sizeof(m_ToneCounter));
+ stream.write(reinterpret_cast(m_Amplitude), sizeof(m_Amplitude));
+ stream.write(reinterpret_cast(&m_NoisePeriod), sizeof(m_NoisePeriod));
+ stream.write(reinterpret_cast(&m_NoiseCounter), sizeof(m_NoiseCounter));
+ stream.write(reinterpret_cast(&m_NoiseShift), sizeof(m_NoiseShift));
+ stream.write(reinterpret_cast(&m_EnvelopePeriod), sizeof(m_EnvelopePeriod));
+ stream.write(reinterpret_cast(&m_EnvelopeCounter), sizeof(m_EnvelopeCounter));
+ stream.write(reinterpret_cast(&m_EnvelopeSegment), sizeof(m_EnvelopeSegment));
+ stream.write(reinterpret_cast(&m_EnvelopeStep), sizeof(m_EnvelopeStep));
+ stream.write(reinterpret_cast(&m_EnvelopeVolume), sizeof(m_EnvelopeVolume));
+ stream.write(reinterpret_cast(m_ToneDisable), sizeof(m_ToneDisable));
+ stream.write(reinterpret_cast(m_NoiseDisable), sizeof(m_NoiseDisable));
+ stream.write(reinterpret_cast(m_EnvelopeMode), sizeof(m_EnvelopeMode));
+ stream.write(reinterpret_cast(m_Sign), sizeof(m_Sign));
+ stream.write(reinterpret_cast(&m_iCycleCounter), sizeof(m_iCycleCounter));
+ stream.write(reinterpret_cast(&m_iSampleCounter), sizeof(m_iSampleCounter));
+ stream.write(reinterpret_cast(m_pBuffer), GC_AUDIO_BUFFER_SIZE * sizeof(s16));
+ stream.write(reinterpret_cast(&m_iBufferIndex), sizeof(m_iBufferIndex));
+}
+
+void AY8910::LoadState(std::istream& stream)
+{
+ stream.read(reinterpret_cast(m_Registers), sizeof(m_Registers));
+ stream.read(reinterpret_cast(&m_SelectedRegister), sizeof(m_SelectedRegister));
+ stream.read(reinterpret_cast(m_TonePeriod), sizeof(m_TonePeriod));
+ stream.read(reinterpret_cast(m_ToneCounter), sizeof(m_ToneCounter));
+ stream.read(reinterpret_cast(m_Amplitude), sizeof(m_Amplitude));
+ stream.read(reinterpret_cast(&m_NoisePeriod), sizeof(m_NoisePeriod));
+ stream.read(reinterpret_cast(&m_NoiseCounter), sizeof(m_NoiseCounter));
+ stream.read(reinterpret_cast(&m_NoiseShift), sizeof(m_NoiseShift));
+ stream.read(reinterpret_cast(&m_EnvelopePeriod), sizeof(m_EnvelopePeriod));
+ stream.read(reinterpret_cast(&m_EnvelopeCounter), sizeof(m_EnvelopeCounter));
+ stream.read(reinterpret_cast(&m_EnvelopeSegment), sizeof(m_EnvelopeSegment));
+ stream.read(reinterpret_cast(&m_EnvelopeStep), sizeof(m_EnvelopeStep));
+ stream.read(reinterpret_cast(&m_EnvelopeVolume), sizeof(m_EnvelopeVolume));
+ stream.read(reinterpret_cast(m_ToneDisable), sizeof(m_ToneDisable));
+ stream.read(reinterpret_cast(m_NoiseDisable), sizeof(m_NoiseDisable));
+ stream.read(reinterpret_cast(m_EnvelopeMode), sizeof(m_EnvelopeMode));
+ stream.read(reinterpret_cast(m_Sign), sizeof(m_Sign));
+ stream.read(reinterpret_cast(&m_iCycleCounter), sizeof(m_iCycleCounter));
+ stream.read(reinterpret_cast(&m_iSampleCounter), sizeof(m_iSampleCounter));
+ stream.read(reinterpret_cast(m_pBuffer), GC_AUDIO_BUFFER_SIZE * sizeof(s16));
+ stream.read(reinterpret_cast(&m_iBufferIndex), sizeof(m_iBufferIndex));
+}
\ No newline at end of file
diff --git a/src/AY8910.h b/src/AY8910.h
new file mode 100644
index 0000000..81cf1e4
--- /dev/null
+++ b/src/AY8910.h
@@ -0,0 +1,73 @@
+/*
+ * Gearcoleco - ColecoVision Emulator
+ * Copyright (C) 2021 Ignacio Sanchez
+
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see http://www.gnu.org/licenses/
+ *
+ */
+
+#ifndef AY8910_H
+#define AY8910_H
+
+#include "definitions.h"
+
+class AY8910
+{
+public:
+ AY8910();
+ ~AY8910();
+ void Init(int clockRate);
+ void Reset(int clockRate);
+ void WriteRegister(u8 value);
+ u8 ReadRegister();
+ void SelectRegister(u8 reg);
+ void Tick(unsigned int clockCycles);
+ int EndFrame(s16* pSampleBuffer);
+ void SaveState(std::ostream& stream);
+ void LoadState(std::istream& stream);
+
+private:
+ void EnvelopeReset();
+ void Sync();
+
+private:
+ u8 m_Registers[16];
+ u8 m_SelectedRegister;
+ u16 m_TonePeriod[3];
+ u16 m_ToneCounter[3];
+ u8 m_Amplitude[3];
+ u8 m_NoisePeriod;
+ u16 m_NoiseCounter;
+ u32 m_NoiseShift;
+ u16 m_EnvelopePeriod;
+ u16 m_EnvelopeCounter;
+ bool m_EnvelopeSegment;
+ u8 m_EnvelopeStep;
+ u8 m_EnvelopeVolume;
+ bool m_ToneDisable[3];
+ bool m_NoiseDisable[3];
+ bool m_EnvelopeMode[3];
+ bool m_Sign[3];
+ int m_iCycleCounter;
+ int m_iSampleCounter;
+ s16* m_pBuffer;
+ int m_iBufferIndex;
+ int m_ElapsedCycles;
+ int m_iClockRate;
+};
+
+const u8 kAY8910RegisterMask[16] = {0xFF, 0x0F, 0xFF, 0x0F, 0xFF, 0x0F, 0x1F, 0xFF, 0x1F, 0x1F, 0x1F, 0xFF, 0xFF, 0x0F, 0xFF, 0xFF};
+const s16 kAY8910VolumeTable[16] = {0, 40, 60, 86, 124, 186, 264, 440, 518, 840, 1196, 1526, 2016, 2602, 3300, 4096};
+
+#endif /* AY8910_H */
\ No newline at end of file
diff --git a/src/Audio.cpp b/src/Audio.cpp
index eb95f86..1cd82a4 100644
--- a/src/Audio.cpp
+++ b/src/Audio.cpp
@@ -22,11 +22,13 @@
Audio::Audio()
{
m_ElapsedCycles = 0;
- m_iSampleRate = 44100;
+ m_iSampleRate = GC_AUDIO_SAMPLE_RATE;
InitPointer(m_pApu);
InitPointer(m_pBuffer);
InitPointer(m_pSampleBuffer);
m_bPAL = false;
+ InitPointer(m_pAY8910);
+ InitPointer(m_pSGMBuffer);
}
Audio::~Audio()
@@ -34,6 +36,8 @@ Audio::~Audio()
SafeDelete(m_pApu);
SafeDelete(m_pBuffer);
SafeDeleteArray(m_pSampleBuffer);
+ SafeDelete(m_pAY8910);
+ SafeDeleteArray(m_pSGMBuffer);
}
void Audio::Init()
@@ -50,6 +54,11 @@ void Audio::Init()
//m_pBuffer->bass_freq(100);
m_pApu->output(m_pBuffer->center(), m_pBuffer->left(), m_pBuffer->right());
+
+ m_pSGMBuffer = new s16[GC_AUDIO_BUFFER_SIZE];
+
+ m_pAY8910 = new AY8910();
+ m_pAY8910->Init(m_bPAL ? GC_MASTER_CLOCK_PAL : GC_MASTER_CLOCK_NTSC);
}
void Audio::Reset(bool bPAL)
@@ -59,6 +68,7 @@ void Audio::Reset(bool bPAL)
m_pBuffer->clear();
m_pBuffer->clock_rate(m_bPAL ? GC_MASTER_CLOCK_PAL : GC_MASTER_CLOCK_NTSC);
m_ElapsedCycles = 0;
+ m_pAY8910->Reset(m_bPAL ? GC_MASTER_CLOCK_PAL : GC_MASTER_CLOCK_NTSC);
}
void Audio::SetSampleRate(int rate)
@@ -82,13 +92,15 @@ void Audio::EndFrame(s16* pSampleBuffer, int* pSampleCount)
int count = static_cast(m_pBuffer->read_samples(m_pSampleBuffer, GC_AUDIO_BUFFER_SIZE));
+ m_pAY8910->EndFrame(m_pSGMBuffer);
+
if (IsValidPointer(pSampleBuffer) && IsValidPointer(pSampleCount))
{
*pSampleCount = count;
for (int i=0; i (&m_ElapsedCycles), sizeof(m_ElapsedCycles));
stream.write(reinterpret_cast (m_pSampleBuffer), sizeof(blip_sample_t) * GC_AUDIO_BUFFER_SIZE);
-
+ m_pAY8910->SaveState(stream);
}
void Audio::LoadState(std::istream& stream)
{
stream.read(reinterpret_cast (&m_ElapsedCycles), sizeof(m_ElapsedCycles));
stream.read(reinterpret_cast (m_pSampleBuffer), sizeof(blip_sample_t) * GC_AUDIO_BUFFER_SIZE);
+ m_pAY8910->LoadState(stream);
m_pApu->reset();
m_pBuffer->clear();
diff --git a/src/Audio.h b/src/Audio.h
index 0a4be27..2a124cf 100644
--- a/src/Audio.h
+++ b/src/Audio.h
@@ -23,6 +23,7 @@
#include "definitions.h"
#include "audio/Multi_Buffer.h"
#include "audio/Sms_Apu.h"
+#include "AY8910.h"
class Audio
{
@@ -45,17 +46,20 @@ class Audio
private:
Sms_Apu* m_pApu;
Stereo_Buffer* m_pBuffer;
- int m_ElapsedCycles;
+ AY8910* m_pAY8910;
+ u64 m_ElapsedCycles;
int m_iSampleRate;
blip_sample_t* m_pSampleBuffer;
bool m_bPAL;
u8 m_SGMRegister;
u8 m_SGMRegisters[16];
+ s16* m_pSGMBuffer;
};
inline void Audio::Tick(unsigned int clockCycles)
{
m_ElapsedCycles += clockCycles;
+ m_pAY8910->Tick(clockCycles);
}
inline void Audio::WriteAudioRegister(u8 value)
@@ -65,22 +69,17 @@ inline void Audio::WriteAudioRegister(u8 value)
inline void Audio::SGMWrite(u8 value)
{
- uint8_t mask[16] = {
- 0xFF, 0x0F, 0xFF, 0x0F, 0xFF, 0x0F, 0x1F, 0xFF,
- 0x1F, 0x1F, 0x1F, 0xFF, 0xFF, 0x0F, 0xFF, 0xFF,
- };
-
- m_SGMRegisters[m_SGMRegister] = value & mask[m_SGMRegister];
+ m_pAY8910->WriteRegister(value);
}
inline u8 Audio::SGMRead()
{
- return m_SGMRegisters[m_SGMRegister];
+ return m_pAY8910->ReadRegister();
}
inline void Audio::SGMRegister(u8 reg)
{
- m_SGMRegister = reg & 0x0F;
+ m_pAY8910->SelectRegister(reg);
}
#endif /* AUDIO_H */
diff --git a/src/definitions.h b/src/definitions.h
index 7a1e1a9..bf48e68 100644
--- a/src/definitions.h
+++ b/src/definitions.h
@@ -102,6 +102,7 @@ typedef void (*RamChangedCallback) (void);
#define GC_LINES_PER_FRAME_PAL 313
#define GC_FRAMES_PER_SECOND_PAL 50
+#define GC_AUDIO_SAMPLE_RATE 44100
#define GC_AUDIO_BUFFER_SIZE 8192
#define GC_SAVESTATE_MAGIC 0x09200902