diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 7c8dd2a..5775650 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -10,7 +10,7 @@ on: jobs: Building: - runs-on: [ubuntu-latest] + runs-on: [ubuntu-18.04] steps: - uses: actions/checkout@v1 @@ -26,6 +26,8 @@ jobs: mkdir build cd build cmake .. && make -j 4 && sudo make install + - name: Update G++ + run: sudo update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-10 100 - name: Build Makefile with CMake run: mkdir -p build && cd build && cmake .. - name: Build with Makefile diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 5de8a5e..52182cd 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -13,7 +13,7 @@ on: jobs: Testing: - runs-on: [ubuntu-latest] + runs-on: [ubuntu-18.04] steps: - uses: actions/checkout@v1 @@ -25,11 +25,18 @@ jobs: run: sudo apt-get update && sudo apt-get install --yes libsfml-dev qt5-default - name: Install Criterion - run: sudo add-apt-repository ppa:snaipewastaken/ppa && - sudo apt-get update && - sudo apt-get install --yes criterion-dev + run: git clone --recursive https://github.com/Snaipe/Criterion && + cd Criterion && + sudo apt install --yes ninja-build && + pip3 install meson && + meson build && + ninja -C build && + sudo ninja -C build install && + sudo cp -r /usr/local/lib/x86_64-linux-gnu/libcriterion* /usr/lib - name: Install Gcovr run: python -m pip install --upgrade pip gcovr + - name: Update G++ + run: sudo update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-10 100 - name: Build Makefile with CMake run: mkdir -p build && cd build && cmake .. - name: Build with Makefile diff --git a/CMakeLists.txt b/CMakeLists.txt index e08896b..e576844 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,5 +1,5 @@ cmake_minimum_required(VERSION 3.12) -set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD 20) # set the project name project(ComSquare) @@ -103,7 +103,15 @@ add_executable(unit_tests sources/PPU/Background.hpp sources/CPU/DMA/DMA.cpp sources/CPU/DMA/DMA.hpp - tests/CPU/testDMA.cpp sources/Memory/IMemory.hpp) + tests/CPU/testDMA.cpp + sources/Memory/IMemory.hpp + sources/APU/DSP/Voice.cpp + sources/APU/DSP/Echo.cpp + sources/APU/DSP/Gauss.cpp + sources/APU/DSP/Envelope.cpp + sources/APU/DSP/Timer.cpp + sources/APU/DSP/BRR.cpp +) # include criterion & coverage target_link_libraries(unit_tests criterion -lgcov) @@ -220,7 +228,16 @@ add_executable(ComSquare ui/registersView.ui sources/Debugger/RegisterViewer.cpp sources/Debugger/RegisterViewer.hpp - sources/Memory/IMemory.hpp sources/Debugger/CPU/SymbolLoaders/WlaDx.cpp sources/Debugger/CPU/SymbolLoaders/WlaDx.hpp) + sources/Memory/IMemory.hpp + sources/Debugger/CPU/SymbolLoaders/WlaDx.cpp + sources/Debugger/CPU/SymbolLoaders/WlaDx.hpp + sources/APU/DSP/Voice.cpp + sources/APU/DSP/Echo.cpp + sources/APU/DSP/Gauss.cpp + sources/APU/DSP/Envelope.cpp + sources/APU/DSP/Timer.cpp + sources/APU/DSP/BRR.cpp +) target_compile_definitions(ComSquare PUBLIC DEBUGGER_ENABLED) diff --git a/sources/APU/APU.cpp b/sources/APU/APU.cpp index ee93fba..8613883 100644 --- a/sources/APU/APU.cpp +++ b/sources/APU/APU.cpp @@ -10,9 +10,11 @@ namespace ComSquare::APU { - APU::APU(std::shared_ptr &map) : - _map(map), - _dsp(new DSP::DSP()) + APU::APU(Renderer::IRenderer &renderer) : + _renderer(renderer), + _map(new MemoryMap()), + _soundBuffer(), + _dsp(this->_soundBuffer, this->_soundBuffer.size() / 2, _map) { this->reset(); } @@ -42,7 +44,7 @@ namespace ComSquare::APU case 0xF2: return this->_registers.dspregAddr; case 0xF3: - return this->_registers.dspregData; + return this->_dsp.read(this->_registers.dspregAddr); case 0xF4: return this->_registers.port0; case 0xF5: @@ -88,7 +90,7 @@ namespace ComSquare::APU this->_registers.dspregAddr = data; break; case 0xF3: - this->_registers.dspregData = data; + this->_dsp.write(this->_registers.dspregAddr, data); break; case 0xF4: this->_registers.port0 = data; @@ -662,7 +664,7 @@ namespace ComSquare::APU case 0xEA: return this->NOT1(this->_getAbsoluteBit()); case 0xEB: - return this->MOV(this->_getDirectAddr(), this->_internalRegisters.y, 3); + return this->MOV(this->_internalRead(this->_getDirectAddr()), this->_internalRegisters.y, 3); case 0xEC: return this->MOV(this->_getAbsoluteAddr(), this->_internalRegisters.y, 4); case 0xED: @@ -711,6 +713,7 @@ namespace ComSquare::APU void APU::update(unsigned cycles) { unsigned total = 0; + int32_t samples = 0; if (this->_paddingCycles > cycles) { this->_paddingCycles -= cycles; @@ -721,6 +724,11 @@ namespace ComSquare::APU total += this->_executeInstruction(); if (this->_state == Running) this->_paddingCycles = total - cycles; + + this->_dsp.update(); + samples = this->_dsp.getSamplesCount(); + if (samples > 0) + this->_renderer.playAudio(std::span{this->_soundBuffer}, samples / 2); } void APU::_setNZflags(uint8_t value) @@ -733,6 +741,6 @@ namespace ComSquare::APU Page0(0x00F0, Apu, "APU's Page 0"), Page1(0x0100, Apu, "APU's Page 1"), Memory(0xFDC0, Apu, "APU's Ram"), - IPL(Apu, "IPL Rom") + IPL(Apu, "IPL Rom") { } } \ No newline at end of file diff --git a/sources/APU/APU.hpp b/sources/APU/APU.hpp index 1725859..a56a0d6 100644 --- a/sources/APU/APU.hpp +++ b/sources/APU/APU.hpp @@ -10,6 +10,7 @@ #include "../Memory/AMemory.hpp" #include "../Ram/Ram.hpp" #include "IPL/IPL.hpp" +#include "../Renderer/IRenderer.hpp" namespace ComSquare::APU { @@ -75,8 +76,6 @@ namespace ComSquare::APU //! @brief DSP Register Address register uint8_t dspregAddr; - //! @brief DSP Register data register - uint8_t dspregData; //! @brief Port 0 register uint8_t port0; @@ -138,12 +137,17 @@ namespace ComSquare::APU Registers _registers{}; //! @brief Internal registers of the CPU (accessible from the bus via addr $4200 to $421F). InternalRegisters _internalRegisters{}; + //! @brief Renderer used to play sounds + Renderer::IRenderer &_renderer; //! @brief Internal APU memory separated according to their utility std::shared_ptr _map; + //! @brief Buffer containing samples to be played + std::array _soundBuffer; + //! @brief The DSP component used to produce sound - std::shared_ptr _dsp; + DSP::DSP _dsp; //! @brief Read from the APU ram. //! @param addr The address to read from. The address 0x0000 should refer to the first byte of the register. @@ -362,7 +366,7 @@ namespace ComSquare::APU int MOV(uint24_t memFrom, uint8_t ®To, int cycles, bool incrementX = false); int MOV(uint24_t memTo, uint24_t memFrom); public: - explicit APU(std::shared_ptr &map); + explicit APU(Renderer::IRenderer &renderer); APU(const APU &) = default; APU &operator=(const APU &) = default; ~APU() override = default; diff --git a/sources/APU/DSP/BRR.cpp b/sources/APU/DSP/BRR.cpp new file mode 100644 index 0000000..27fa942 --- /dev/null +++ b/sources/APU/DSP/BRR.cpp @@ -0,0 +1,66 @@ +// +// Created by melefo on 2/3/21. +// + +#include +#include "DSP.hpp" + +namespace ComSquare::APU::DSP +{ + void DSP::decodeBRR(Voice &voice) + { + int32_t value = this->_brr.value << 8 | this->_readRAM(voice.brrAddress + voice.brrOffset + 1); + + int32_t filter = this->_brr.header >> 2 & 0b11; + int32_t range = this->_brr.header >> 4 & 0b1111; + + for (int i = 0; i < 4; i++) { + int32_t sample = value >> 12; + value <<= 4; + + if (range <= 12) { + sample <<= range; + sample >>= 1; + } + else + sample &= ~0x7FF; + + int offset = voice.sampleOffset; + int lastSample; + int afterLastSample; + + if (--offset < 0) + offset = 11; + lastSample = voice.samples[offset]; + if (--offset < 0) + offset = 11; + afterLastSample = voice.samples[offset]; + + switch (filter) { + case 0: + break; + case 1: + sample += lastSample; + sample += (lastSample) >> 4; + break; + case 2: + sample += lastSample << 1; + sample += -((lastSample << 1) + lastSample) >> 5; + sample -= afterLastSample; + sample += afterLastSample >> 4; + break; + case 3: + sample += lastSample << 1; + sample += -(lastSample + (lastSample << 2) + (lastSample << 3)) >> 6; + sample -= afterLastSample; + sample += ((afterLastSample << 1) + afterLastSample) >> 4; + break; + } + sample = std::clamp(sample, 0, 16); + sample <<= 1; + voice.samples[voice.sampleOffset] = sample; + if (++voice.sampleOffset >= 12) + voice.sampleOffset = 0; + } + } +} \ No newline at end of file diff --git a/sources/APU/DSP/DSP.cpp b/sources/APU/DSP/DSP.cpp index 59dfad5..fb933c9 100644 --- a/sources/APU/DSP/DSP.cpp +++ b/sources/APU/DSP/DSP.cpp @@ -3,251 +3,251 @@ // #include "DSP.hpp" +#include "../APU.hpp" #include "../../Exceptions/InvalidAddress.hpp" namespace ComSquare::APU::DSP { + DSP::DSP(std::array &buffer, uint32_t size, std::weak_ptr map) : + _state(buffer), _map(map) + { + this->_state.buffer = buffer; + this->_state.bufferSize = size; + } + uint8_t DSP::read(uint24_t addr) const { switch (addr) { case 0x00: - return this->_channels[0].volL; + return this->_voices[0].volume[0]; case 0x10: - return this->_channels[1].volL; + return this->_voices[1].volume[0]; case 0x20: - return this->_channels[2].volL; + return this->_voices[2].volume[0]; case 0x30: - return this->_channels[3].volL; + return this->_voices[3].volume[0]; case 0x40: - return this->_channels[4].volL; + return this->_voices[4].volume[0]; case 0x50: - return this->_channels[5].volL; + return this->_voices[5].volume[0]; case 0x60: - return this->_channels[6].volL; + return this->_voices[6].volume[0]; case 0x70: - return this->_channels[7].volL; + return this->_voices[7].volume[0]; case 0x01: - return this->_channels[0].volR; + return this->_voices[0].volume[1]; case 0x11: - return this->_channels[1].volR; + return this->_voices[1].volume[1]; case 0x21: - return this->_channels[2].volR; + return this->_voices[2].volume[1]; case 0x31: - return this->_channels[3].volR; + return this->_voices[3].volume[1]; case 0x41: - return this->_channels[4].volR; + return this->_voices[4].volume[1]; case 0x51: - return this->_channels[5].volR; + return this->_voices[5].volume[1]; case 0x61: - return this->_channels[6].volR; + return this->_voices[6].volume[1]; case 0x71: - return this->_channels[7].volR; + return this->_voices[7].volume[1]; case 0x02: - return this->_channels[0].pitchL; + return this->_voices[0].pitchL; case 0x12: - return this->_channels[1].pitchL; + return this->_voices[1].pitchL; case 0x22: - return this->_channels[2].pitchL; + return this->_voices[2].pitchL; case 0x32: - return this->_channels[3].pitchL; + return this->_voices[3].pitchL; case 0x42: - return this->_channels[4].pitchL; + return this->_voices[4].pitchL; case 0x52: - return this->_channels[5].pitchL; + return this->_voices[5].pitchL; case 0x62: - return this->_channels[6].pitchL; + return this->_voices[6].pitchL; case 0x72: - return this->_channels[7].pitchL; + return this->_voices[7].pitchL; case 0x03: - return this->_channels[0].pitchH; + return this->_voices[0].pitchH; case 0x13: - return this->_channels[1].pitchH; + return this->_voices[1].pitchH; case 0x23: - return this->_channels[2].pitchH; + return this->_voices[2].pitchH; case 0x33: - return this->_channels[3].pitchH; + return this->_voices[3].pitchH; case 0x43: - return this->_channels[4].pitchH; + return this->_voices[4].pitchH; case 0x53: - return this->_channels[5].pitchH; + return this->_voices[5].pitchH; case 0x63: - return this->_channels[6].pitchH; + return this->_voices[6].pitchH; case 0x73: - return this->_channels[7].pitchH; + return this->_voices[7].pitchH; case 0x04: - return this->_channels[0].srcn; + return this->_voices[0].srcn; case 0x14: - return this->_channels[1].srcn; + return this->_voices[1].srcn; case 0x24: - return this->_channels[2].srcn; + return this->_voices[2].srcn; case 0x34: - return this->_channels[3].srcn; + return this->_voices[3].srcn; case 0x44: - return this->_channels[4].srcn; + return this->_voices[4].srcn; case 0x54: - return this->_channels[5].srcn; + return this->_voices[5].srcn; case 0x64: - return this->_channels[6].srcn; + return this->_voices[6].srcn; case 0x74: - return this->_channels[7].srcn; + return this->_voices[7].srcn; case 0x05: - return this->_channels[0].adsr1; + return this->_voices[0].adsr1; case 0x15: - return this->_channels[1].adsr1; + return this->_voices[1].adsr1; case 0x25: - return this->_channels[2].adsr1; + return this->_voices[2].adsr1; case 0x35: - return this->_channels[3].adsr1; + return this->_voices[3].adsr1; case 0x45: - return this->_channels[4].adsr1; + return this->_voices[4].adsr1; case 0x55: - return this->_channels[5].adsr1; + return this->_voices[5].adsr1; case 0x65: - return this->_channels[6].adsr1; + return this->_voices[6].adsr1; case 0x75: - return this->_channels[7].adsr1; + return this->_voices[7].adsr1; case 0x06: - return this->_channels[0].adsr2; + return this->_voices[0].adsr2; case 0x16: - return this->_channels[1].adsr2; + return this->_voices[1].adsr2; case 0x26: - return this->_channels[2].adsr2; + return this->_voices[2].adsr2; case 0x36: - return this->_channels[3].adsr2; + return this->_voices[3].adsr2; case 0x46: - return this->_channels[4].adsr2; + return this->_voices[4].adsr2; case 0x56: - return this->_channels[5].adsr2; + return this->_voices[5].adsr2; case 0x66: - return this->_channels[6].adsr2; + return this->_voices[6].adsr2; case 0x76: - return this->_channels[7].adsr2; + return this->_voices[7].adsr2; case 0x07: - return this->_channels[0].gain; + return this->_voices[0].gain; case 0x17: - return this->_channels[1].gain; + return this->_voices[1].gain; case 0x27: - return this->_channels[2].gain; + return this->_voices[2].gain; case 0x37: - return this->_channels[3].gain; + return this->_voices[3].gain; case 0x47: - return this->_channels[4].gain; + return this->_voices[4].gain; case 0x57: - return this->_channels[5].gain; + return this->_voices[5].gain; case 0x67: - return this->_channels[6].gain; + return this->_voices[6].gain; case 0x77: - return this->_channels[7].gain; + return this->_voices[7].gain; case 0x08: - return this->_channels[0].envx; case 0x18: - return this->_channels[1].envx; case 0x28: - return this->_channels[2].envx; case 0x38: - return this->_channels[3].envx; case 0x48: - return this->_channels[4].envx; case 0x58: - return this->_channels[5].envx; case 0x68: - return this->_channels[6].envx; case 0x78: - return this->_channels[7].envx; + return this->_latch.envx; case 0x09: - return this->_channels[0].outx; case 0x19: - return this->_channels[1].outx; case 0x29: - return this->_channels[2].outx; case 0x39: - return this->_channels[3].outx; case 0x49: - return this->_channels[4].outx; case 0x59: - return this->_channels[5].outx; case 0x69: - return this->_channels[6].outx; case 0x79: - return this->_channels[7].outx; + return this->_latch.outx; case 0x0C: - return this->_registers.mvolL; + return this->_master.volume[0]; case 0x1C: - return this->_registers.mvolR; + return this->_master.volume[1]; case 0x2C: - return this->_registers.evolL; + return this->_echo.volume[0]; case 0x3C: - return this->_registers.evolR; + return this->_echo.volume[1]; case 0x4C: { uint8_t kon = 0; for (int i = 0; i < 8; i++) - kon |= this->_channels[i].kon << i; + kon |= this->_voices[i].kon << i; return kon; } case 0x5C: { uint8_t kof = 0; for (int i = 0; i < 8; i++) - kof |= this->_channels[i].kof << i; + kof |= this->_voices[i].kof << i; return kof; } - case 0x6C: - return this->_registers.flg; + case 0x6C: { + uint8_t flg = 0; + flg += this->_master.reset << 7; + flg += this->_master.mute << 6; + flg += this->_echo.enabled << 5; + flg += this->_noise.clock; + return flg; + } case 0x7C: { uint8_t endx = 0; for (int i = 0; i < 8; i++) - endx |= this->_channels[i].endx << i; + endx |= this->_voices[i].endx << i; return endx; } case 0x0D: - return this->_registers.efb; + return this->_echo.feedback; case 0x1D: - return this->_registers.unused; + return this->_master.unused; case 0x2D: { uint8_t pmon = 0; for (int i = 0; i < 8; i++) - pmon |= this->_channels[i].pmon << i; + pmon |= this->_voices[i].pmon << i; return pmon; } case 0x3D: { uint8_t non = 0; for (int i = 0; i < 8; i++) - non |= this->_channels[i].non << i; + non |= this->_voices[i].non << i; return non; } case 0x4D: { uint8_t eon = 0; for (int i = 0; i < 8; i++) - eon |= this->_channels[i].eon << i; + eon |= this->_voices[i].eon << i; return eon; } case 0x5D: - return this->_registers.dir; + return this->_brr.offset; case 0x6D: - return this->_registers.esa; + return this->_echo.data; case 0x7D: - return this->_registers.edl; + return this->_echo.delay; case 0x0F: - return this->_channels[0].coeff; + return this->_echo.FIR[0]; case 0x1F: - return this->_channels[1].coeff; + return this->_echo.FIR[1]; case 0x2F: - return this->_channels[2].coeff; + return this->_echo.FIR[2]; case 0x3F: - return this->_channels[3].coeff; + return this->_echo.FIR[3]; case 0x4F: - return this->_channels[4].coeff; + return this->_echo.FIR[4]; case 0x5F: - return this->_channels[5].coeff; + return this->_echo.FIR[5]; case 0x6F: - return this->_channels[6].coeff; + return this->_echo.FIR[6]; case 0x7F: - return this->_channels[7].coeff; + return this->_echo.FIR[7]; default: throw InvalidAddress("DSP Registers read", addr); } @@ -257,350 +257,551 @@ namespace ComSquare::APU::DSP { switch (addr) { case 0x00: - this->_channels[0].volL = data; + this->_voices[0].volume[0] = data; break; case 0x10: - this->_channels[1].volL = data; + this->_voices[1].volume[0] = data; break; case 0x20: - this->_channels[2].volL = data; + this->_voices[2].volume[0] = data; break; case 0x30: - this->_channels[3].volL = data; + this->_voices[3].volume[0] = data; break; case 0x40: - this->_channels[4].volL = data; + this->_voices[4].volume[0] = data; break; case 0x50: - this->_channels[5].volL = data; + this->_voices[5].volume[0] = data; break; case 0x60: - this->_channels[6].volL = data; + this->_voices[6].volume[0] = data; break; case 0x70: - this->_channels[7].volL = data; + this->_voices[7].volume[0] = data; break; case 0x01: - this->_channels[0].volR = data; + this->_voices[0].volume[1] = data; break; case 0x11: - this->_channels[1].volR = data; + this->_voices[1].volume[1] = data; break; case 0x21: - this->_channels[2].volR = data; + this->_voices[2].volume[1] = data; break; case 0x31: - this->_channels[3].volR = data; + this->_voices[3].volume[1] = data; break; case 0x41: - this->_channels[4].volR = data; + this->_voices[4].volume[1] = data; break; case 0x51: - this->_channels[5].volR = data; + this->_voices[5].volume[1] = data; break; case 0x61: - this->_channels[6].volR = data; + this->_voices[6].volume[1] = data; break; case 0x71: - this->_channels[7].volR = data; + this->_voices[7].volume[1] = data; break; case 0x02: - this->_channels[0].pitchL = data; + this->_voices[0].pitchL = data; break; case 0x12: - this->_channels[1].pitchL = data; + this->_voices[1].pitchL = data; break; case 0x22: - this->_channels[2].pitchL = data; + this->_voices[2].pitchL = data; break; case 0x32: - this->_channels[3].pitchL = data; + this->_voices[3].pitchL = data; break; case 0x42: - this->_channels[4].pitchL = data; + this->_voices[4].pitchL = data; break; case 0x52: - this->_channels[5].pitchL = data; + this->_voices[5].pitchL = data; break; case 0x62: - this->_channels[6].pitchL = data; + this->_voices[6].pitchL = data; break; case 0x72: - this->_channels[7].pitchL = data; + this->_voices[7].pitchL = data; break; case 0x03: - this->_channels[0].pitchH = data; + this->_voices[0].pitchH = data; break; case 0x13: - this->_channels[1].pitchH = data; + this->_voices[1].pitchH = data; break; case 0x23: - this->_channels[2].pitchH = data; + this->_voices[2].pitchH = data; break; case 0x33: - this->_channels[3].pitchH = data; + this->_voices[3].pitchH = data; break; case 0x43: - this->_channels[4].pitchH = data; + this->_voices[4].pitchH = data; break; case 0x53: - this->_channels[5].pitchH = data; + this->_voices[5].pitchH = data; break; case 0x63: - this->_channels[6].pitchH = data; + this->_voices[6].pitchH = data; break; case 0x73: - this->_channels[7].pitchH = data; + this->_voices[7].pitchH = data; break; case 0x04: - this->_channels[0].srcn = data; + this->_voices[0].srcn = data; break; case 0x14: - this->_channels[1].srcn = data; + this->_voices[1].srcn = data; break; case 0x24: - this->_channels[2].srcn = data; + this->_voices[2].srcn = data; break; case 0x34: - this->_channels[3].srcn = data; + this->_voices[3].srcn = data; break; case 0x44: - this->_channels[4].srcn = data; + this->_voices[4].srcn = data; break; case 0x54: - this->_channels[5].srcn = data; + this->_voices[5].srcn = data; break; case 0x64: - this->_channels[6].srcn = data; + this->_voices[6].srcn = data; break; case 0x74: - this->_channels[7].srcn = data; + this->_voices[7].srcn = data; break; case 0x05: - this->_channels[0].adsr1 = data; + this->_voices[0].adsr1 = data; break; case 0x15: - this->_channels[1].adsr1 = data; + this->_voices[1].adsr1 = data; break; case 0x25: - this->_channels[2].adsr1 = data; + this->_voices[2].adsr1 = data; break; case 0x35: - this->_channels[3].adsr1 = data; + this->_voices[3].adsr1 = data; break; case 0x45: - this->_channels[4].adsr1 = data; + this->_voices[4].adsr1 = data; break; case 0x55: - this->_channels[5].adsr1 = data; + this->_voices[5].adsr1 = data; break; case 0x65: - this->_channels[6].adsr1 = data; + this->_voices[6].adsr1 = data; break; case 0x75: - this->_channels[7].adsr1 = data; + this->_voices[7].adsr1 = data; break; case 0x06: - this->_channels[0].adsr2 = data; + this->_voices[0].adsr2 = data; break; case 0x16: - this->_channels[1].adsr2 = data; + this->_voices[1].adsr2 = data; break; case 0x26: - this->_channels[2].adsr2 = data; + this->_voices[2].adsr2 = data; break; case 0x36: - this->_channels[3].adsr2 = data; + this->_voices[3].adsr2 = data; break; case 0x46: - this->_channels[4].adsr2 = data; + this->_voices[4].adsr2 = data; break; case 0x56: - this->_channels[5].adsr2 = data; + this->_voices[5].adsr2 = data; break; case 0x66: - this->_channels[6].adsr2 = data; + this->_voices[6].adsr2 = data; break; case 0x76: - this->_channels[7].adsr2 = data; + this->_voices[7].adsr2 = data; break; case 0x07: - this->_channels[0].gain = data; + this->_voices[0].gain = data; break; case 0x17: - this->_channels[1].gain = data; + this->_voices[1].gain = data; break; case 0x27: - this->_channels[2].gain = data; + this->_voices[2].gain = data; break; case 0x37: - this->_channels[3].gain = data; + this->_voices[3].gain = data; break; case 0x47: - this->_channels[4].gain = data; + this->_voices[4].gain = data; break; case 0x57: - this->_channels[5].gain = data; + this->_voices[5].gain = data; break; case 0x67: - this->_channels[6].gain = data; + this->_voices[6].gain = data; break; case 0x77: - this->_channels[7].gain = data; + this->_voices[7].gain = data; break; case 0x08: - this->_channels[0].envx = data; + this->_voices[0].envx = data; break; case 0x18: - this->_channels[1].envx = data; + this->_voices[1].envx = data; break; case 0x28: - this->_channels[2].envx = data; + this->_voices[2].envx = data; break; case 0x38: - this->_channels[3].envx = data; + this->_voices[3].envx = data; break; case 0x48: - this->_channels[4].envx = data; + this->_voices[4].envx = data; break; case 0x58: - this->_channels[5].envx = data; + this->_voices[5].envx = data; break; case 0x68: - this->_channels[6].envx = data; + this->_voices[6].envx = data; break; case 0x78: - this->_channels[7].envx = data; + this->_voices[7].envx = data; break; case 0x09: - this->_channels[0].outx = data; - break; case 0x19: - this->_channels[1].outx = data; - break; case 0x29: - this->_channels[2].outx = data; - break; case 0x39: - this->_channels[3].outx = data; - break; case 0x49: - this->_channels[4].outx = data; - break; case 0x59: - this->_channels[5].outx = data; - break; case 0x69: - this->_channels[6].outx = data; - break; case 0x79: - this->_channels[7].outx = data; + this->_latch.outx = data; break; case 0x0C: - this->_registers.mvolL = data; + this->_master.volume[0] = data; break; case 0x1C: - this->_registers.mvolR = data; + this->_master.volume[1] = data; break; case 0x2C: - this->_registers.evolL = data; + this->_echo.volume[0] = data; break; case 0x3C: - this->_registers.evolR = data; + this->_echo.volume[1] = data; break; case 0x4C: - for (int i = 0; i < 8; i++) - this->_channels[i].kon |= data << i; + for (int i = 0; i < 8; i++) { + this->_voices[i].kon |= data << i; + } break; case 0x5C: for (int i = 0; i < 8; i++) - this->_channels[i].kof |= data << i; + this->_voices[i].kof |= data << i; break; case 0x6C: - this->_registers.flg = data; + this->_master.reset = data >> 7; + this->_master.mute = (data >> 6) & 0b1; + this->_echo.enabled = (data >> 5) & 0b1; + this->_noise.clock = data & 0b1111; break; case 0x7C: for (int i = 0; i < 8; i++) - this->_channels[i].endx |= data << i; + this->_voices[i].endx |= data << i; break; case 0x0D: - this->_registers.efb = data; + this->_echo.feedback = data; break; case 0x1D: - this->_registers.unused = data; + this->_master.unused = data; break; case 0x2D: for (int i = 0; i < 8; i++) - this->_channels[i].pmon |= data << i; + this->_voices[i].pmon |= data << i; break; case 0x3D: for (int i = 0; i < 8; i++) - this->_channels[i].non |= data << i; + this->_voices[i].non |= data << i; break; case 0x4D: for (int i = 0; i < 8; i++) - this->_channels[i].eon |= data << i; + this->_voices[i].eon |= data << i; break; case 0x5D: - this->_registers.dir = data; + this->_brr.offset = data; break; case 0x6D: - this->_registers.esa = data; + this->_echo.data = data; break; case 0x7D: - this->_registers.edl = data; + this->_echo.delay = data; break; case 0x0F: - this->_channels[0].coeff = data; + this->_echo.FIR[0] = data; break; case 0x1F: - this->_channels[1].coeff = data; + this->_echo.FIR[1] = data; break; case 0x2F: - this->_channels[2].coeff = data; + this->_echo.FIR[2] = data; break; case 0x3F: - this->_channels[3].coeff = data; + this->_echo.FIR[3] = data; break; case 0x4F: - this->_channels[4].coeff = data; + this->_echo.FIR[4] = data; break; case 0x5F: - this->_channels[5].coeff = data; + this->_echo.FIR[5] = data; break; case 0x6F: - this->_channels[6].coeff = data; + this->_echo.FIR[6] = data; break; case 0x7F: - this->_channels[7].coeff = data; + this->_echo.FIR[7] = data; break; default: throw InvalidAddress("DSP Registers write", addr); } } + uint8_t DSP::_readRAM(uint24_t addr) + { + if (!this->_map.lock()) + throw std::runtime_error("DSP read : MemoryMap inaccessible"); + switch (addr) { + case 0x0000 ... 0x00EF: + return this->_map.lock()->Page0.read(addr); + case 0x0100 ... 0x01FF: + return this->_map.lock()->Page1.read(addr - 0x0100); + case 0x0200 ... 0xFFBF: + return this->_map.lock()->Memory.read(addr - 0x200); + case 0xFFC0 ... 0xFFFF: + return this->_map.lock()->IPL.read(addr - 0xFFC0); + default: + throw InvalidAddress("DSP read", addr); + } + } + void DSP::_writeRAM(uint24_t addr, uint8_t data) + { + if (!this->_map.lock()) + throw std::runtime_error("DSP write : MemoryMap inaccessible"); + switch (addr) { + case 0x0000 ... 0x00EF: + this->_map.lock()->Page0.write(addr, data); + break; + case 0x0100 ... 0x01FF: + this->_map.lock()->Page1.write(addr - 0x0100, data); + break; + case 0x0200 ... 0xFFBF: + this->_map.lock()->Memory.write(addr - 0x200, data); + break; + case 0xFFC0 ... 0xFFFF: + this->_map.lock()->IPL.write(addr - 0xFFC0, data); + break; + default: + throw InvalidAddress("DSP write", addr); + } + } + + void DSP::update() + { + switch (this->_state.voice) { + case 0: + this->voice5(this->_voices[0]); + this->voice2(this->_voices[1]); + break; + case 1: + this->voice6(this->_voices[0]); + this->voice3(this->_voices[1]); + break; + case 2: + this->voice7(this->_voices[0]); + this->voice4(this->_voices[1]); + this->voice1(this->_voices[3]); + break; + case 3: + this->voice8(this->_voices[0]); + this->voice5(this->_voices[1]); + this->voice2(this->_voices[2]); + break; + case 4: + this->voice9(this->_voices[0]); + this->voice6(this->_voices[1]); + this->voice3(this->_voices[2]); + break; + case 5: + this->voice7(this->_voices[1]); + this->voice4(this->_voices[2]); + this->voice1(this->_voices[4]); + break; + case 6: + this->voice8(this->_voices[1]); + this->voice5(this->_voices[2]); + this->voice2(this->_voices[3]); + break; + case 7: + this->voice9(this->_voices[1]); + this->voice6(this->_voices[2]); + this->voice3(this->_voices[3]); + break; + case 8: + this->voice7(this->_voices[2]); + this->voice4(this->_voices[3]); + this->voice1(this->_voices[5]); + break; + case 9: + this->voice8(this->_voices[2]); + this->voice5(this->_voices[3]); + this->voice2(this->_voices[4]); + break; + case 10: + this->voice9(this->_voices[2]); + this->voice6(this->_voices[3]); + this->voice3(this->_voices[4]); + break; + case 11: + this->voice7(this->_voices[3]); + this->voice4(this->_voices[4]); + this->voice1(this->_voices[6]); + break; + case 12: + this->voice8(this->_voices[3]); + this->voice5(this->_voices[4]); + this->voice2(this->_voices[5]); + break; + case 13: + this->voice9(this->_voices[3]); + this->voice6(this->_voices[4]); + this->voice3(this->_voices[5]); + break; + case 14: + this->voice7(this->_voices[4]); + this->voice4(this->_voices[5]); + this->voice1(this->_voices[7]); + break; + case 15: + this->voice8(this->_voices[4]); + this->voice5(this->_voices[5]); + this->voice2(this->_voices[6]); + break; + case 16: + this->voice9(this->_voices[4]); + this->voice6(this->_voices[5]); + this->voice3(this->_voices[6]); + break; + case 17: + this->voice1(this->_voices[0]); + this->voice7(this->_voices[5]); + this->voice4(this->_voices[6]); + break; + case 18: + this->voice8(this->_voices[5]); + this->voice5(this->_voices[6]); + this->voice2(this->_voices[7]); + break; + case 19: + this->voice9(this->_voices[5]); + this->voice6(this->_voices[6]); + this->voice3(this->_voices[7]); + break; + case 20: + this->voice1(this->_voices[1]); + this->voice7(this->_voices[6]); + this->voice4(this->_voices[7]); + break; + case 21: + this->voice8(this->_voices[6]); + this->voice5(this->_voices[7]); + this->voice2(this->_voices[0]); + break; + case 22: + this->voice3a(this->_voices[0]); + this->voice9(this->_voices[6]); + this->voice6(this->_voices[7]); + echo22(); + break; + case 23: + this->voice7(this->_voices[7]); + echo23(); + break; + case 24: + this->voice8(this->_voices[7]); + echo24(); + break; + case 25: + this->voice3b(this->_voices[0]); + this->voice9(this->_voices[7]); + echo25(); + break; + case 26: + echo26(); + break; + case 27: + this->misc27(); + this->echo27(); + break; + case 28: + this->misc28(); + this->echo28(); + break; + case 29: + this->misc29(); + this->echo29(); + break; + case 30: + this->misc30(); + this->voice3c(this->_voices[0]); + this->echo30(); + break; + case 31: + this->voice4(this->_voices[0]); + this->voice1(this->_voices[2]); + break; + } + this->_state.voice = (this->_state.voice + 1) % 32; + } uint24_t DSP::getSize() const { return 0x7F; } - Registers DSP::getRegisters() + const std::array &DSP::getVoices() const { - return this->_registers; + return this->_voices; } - std::array DSP::getChannels() + const Master &DSP::getMaster() const { - return this->_channels; + return this->_master; } - std::string DSP::getName() const + const Echo &DSP::getEcho() const { - return "DSP"; + return this->_echo; } - Component DSP::getComponent() const + const Noise &DSP::getNoise() const { - return Apu; + return this->_noise; + } + + const BRR &DSP::getBrr() const + { + return this->_brr; + } + + const Latch &DSP::getLatch() const + { + return this->_latch; + } + + int32_t DSP::getSamplesCount() const + { + return this->_state.bufferOffset; } } \ No newline at end of file diff --git a/sources/APU/DSP/DSP.hpp b/sources/APU/DSP/DSP.hpp index 04d6265..216df36 100644 --- a/sources/APU/DSP/DSP.hpp +++ b/sources/APU/DSP/DSP.hpp @@ -9,61 +9,113 @@ #include #include "../../Memory/AMemory.hpp" +namespace ComSquare::APU +{ + class APU; + struct MemoryMap; +} + namespace ComSquare::APU::DSP { - //! @brief All the registers of the DSP - struct Registers { - //! @brief Left output of the Main Volume register - uint8_t mvolL; - //! @brief Right output of the Main Volume register - uint8_t mvolR; - - //! @brief Left output of the Echo Volume register - uint8_t evolL; - //! @brief Right output of the Echo Volume register - uint8_t evolR; - - //! @brief Flags register - uint8_t flg; - - //! @brief Echo feedback register - uint8_t efb; - - //! @brief Not used register - uint8_t unused; - - //! @brief Source Directory offset register - uint8_t dir; - - //! @brief Echo data start register - uint8_t esa; - //! @brief Echo delay size register - uint8_t edl; + //! @brief The 4 states of volume envelope adjustment + enum Envelope : uint { + //! @brief The voice is keyed off or a BRR end-without-loop block is reached + Release, + //! @brief The voice is keyed on + Attack, + //! @brief When the Envelope adjustment method bits exceeds 0x7ff + Decay, + //! @brief When the upper 3 bits of Envelope adjustment method bits equal the Sustain Level + Sustain }; - struct Channel { - //! @brief BRR Header - union { - struct { - //! @brief Shift value range - unsigned range : 4; - //! @brief Decompression filter - unsigned filter : 2; - //! @brief Flag if the sample loops - bool loop : 1; - //! @brief Stop the sample (or restart from loop point) - bool end : 1; - }; - uint8_t brrHead; - }; - //! @brief Sample data inside BRR - uint64_t brrData; + struct Master { + //! @brief Main Volume register (MVOL) + std::array volume; + //! @brief Mutes all channel (6th bit FLG) + bool mute : 1; + //! @brief Soft reset DSP (7th bit FLG) + bool reset : 1; + //! @brief Current sound produced + std::array output; + //! @brief Not used register + uint8_t unused; + }; - //! @brief Left channel volume register - uint8_t volL; - //! @brief Left channel volume register - uint8_t volR; + struct Echo { + //! @brief Echo Volume register (EVOL) + std::array volume; + //! @brief Echo feedback register (EFB) + uint8_t feedback; + //! @brief Echo FIR filter coefficients (COEF) + std::array FIR; + //! @brief Echo data start register (ESA) + uint8_t data; + //! @brief Offset position after data start + uint16_t offset; + //! @brief offset maximum + uint16_t length; + //! @brief Echo delay size register (EDL) + uint8_t delay; + //! @brief Echo enabled (5th bit FLG) + bool enabled = true; + //! @brief Application of enabled to channels. + bool toggle; + //! @brief Last sound produced for each voice in each channel + std::array, 2> history; + //! @brief Current position inside history + uint8_t historyOffset; + //! @brief Address of the current echo + uint16_t address; + //! @brief Current of value of the echo + uint8_t value; + //! @brief Current sound to echo + std::array input; + //! @brief Current sound echoed produced + std::array output; + }; + struct Noise { + //! @brief Frequency of white noise (the first 4 bits FLG) + uint8_t clock : 5; + //! @brief Linear feedback shift register used to shift final output + uint16_t lfsr = 0x4000; + }; + + struct BRR { + //! @brief Offset pointing to sample directory in external RAM (DIR) + uint8_t offset; + //! @brief Address of the offset + uint8_t offsetAddr; + //! @brief Current address of the BRR in APU's RAM + uint16_t address; + //! @brief Next address of the BRR in APU's RAM + uint16_t nextAddress; + //! @brief Current value inside BRR + uint8_t value; + //! @brief Current header of BRR + uint8_t header; + //! @brief Current value of Voice ADSR1 loaded + uint8_t source; + }; + + struct Latch { + //! @brief Current voice's adsr1 in use + uint8_t adsr1; + //! @brief Envelope value register (ENVX) + uint8_t envx; + //! @brief Wave height register (OUTX) + uint8_t outx; + //! @brief Current voice's pitch in use + uint16_t pitch; + //! @brief Output currently being modified + uint16_t output; + }; + + struct Voice { + //! @brief Volume register (VOL) + std::array volume; + //! @brief Pitch register (P) union { struct { //! @brief Lower 8 bits of pitch register @@ -73,83 +125,249 @@ namespace ComSquare::APU::DSP }; uint16_t pitch; }; - - //! @brief Key On register - bool kon : 1; - //! @brief Key Off register - bool kof : 1; - - //! @brief Sample end register - bool endx : 1; - - //! @brief Noise enable register - bool non : 1; - //! @brief Echo enable register - bool eon : 1; - - //! @brief Pitch modulation register - bool pmon : 1; - - //! @brief Source number register + //! @brief Source number register (SRCN) uint8_t srcn; - union { struct { - //! @brief Envelope register + //! @brief Envelope register (ADSR) uint8_t adsr1; - //! @brief Envelope controllers register + //! @brief Envelope controllers register (ADSR) uint8_t adsr2; }; - uint16_t envelope; + uint16_t adsr; }; - //! @brief Gain register + //! @brief Gain register (GAIN) uint8_t gain; - //! @brief Envelope value register + //! @brief envelope associated with this voice uint8_t envx; - //! @brief Wave height register + //! @brief Wave height associated with this voice uint8_t outx; + //! @brief Sample end register (ENDX) + bool endx : 1; - //! @brief Echo FIR filter coefficients - uint8_t coeff; + //! @brief Key On register (KON) + bool kon : 1; + //! @brief Key Off register (KOF) + bool kof : 1; + //! @brief Pitch modulation register (PMON) + bool pmon : 1; + //! @brief Noise enable register (NON) + bool non : 1; + //! @brief Echo enable register (EON) + bool eon : 1; + + //! @brief Check if voice is in setup phase + uint8_t konDelay; + //! @brief Check if the output will be echoed + bool echo; + //! @brief Check if this voice will be looped + bool loop; + //! @brief Current BRR associated with this voice + uint16_t brrAddress; + //! @brief Current Offset in the BRR block + uint8_t brrOffset = 1; + //! @brief Previous modulation + bool prevPmon : 1; + //! @brief temporary NON register value + bool tempNon : 1; + //! @brief temporary Key On register value + bool tempKon : 1; + //! @brief temporary Key Off register value + bool tempKof : 1; + //! @brief all samples Decoded from BRR + std::array samples; + //! @brief Offset of current sample in samples buffer + uint8_t sampleOffset; + //! @brief Current envelope level + uint16_t envelope; + //! @brief Second envelope level used to make "special" waveforms + uint16_t hiddenEnvelope; + //! @brief current envelope Mode + Envelope envelopeMode; + //! @brief Relative fractional position in sample + uint16_t gaussOffset; }; - class DSP : public Memory::AMemory { - private: - //! @brief All registers of the DSP - Registers _registers{}; + //! @brief Current state of the DSP + struct State + { + State(std::array &array) : buffer(array) {}; - //! @brief 8x channels of sample used to make sound - std::array _channels{}; + //! @brief Current voice modification to do + uint8_t voice = 0; + //! @brief Current buffer of samples + std::array &buffer; + //! @brief Size of buffer + uint32_t bufferSize; + //! @brief Current position in the buffer of samples + uint32_t bufferOffset; + }; + + struct Timer { + //! @brief Ticks remaining in the timer + uint16_t counter; + //! @brief output every samples + bool sample = true; + }; + + class DSP { + private: + //! @brief Number of samples per counter event + std::array _rateModulus = { + 0, 2048, 1536, 1280, 1024, 768, + 640, 512, 384, 320, 256, 192, + 160, 128, 96, 80, 64, 48, + 40, 32, 24, 20, 16, 12, + 10, 8, 6, 5, 4, 3, + 2, 1 + }; + + //! @brief Counter offset + std::array _counterOffset = { + 0, 0, 1040, 536, 0, 1040, + 536, 0, 1040, 536, 0, 1040, + 536, 0, 1040, 536, 0, 1040, + 536, 0, 1040, 536, 0, 1040, + 536, 0, 1040, 536, 0, 1040, + 0,0 + }; + + //! @brief Gaussian table used for making waves + std::array _gauss = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, + 2, 2, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 5, 5, 5, 5, + 6, 6, 6, 6, 7, 7, 7, 8, 8, 8, 9, 9, 9, 10, 10, 10, + 11, 11, 11, 12, 12, 13, 13, 14, 14, 15, 15, 15, 16, 16, 17, 17, + 18, 19, 19, 20, 20, 21, 21, 22, 23, 23, 24, 24, 25, 26, 27, 27, + 28, 29, 29, 30, 31, 32, 32, 33, 34, 35, 36, 36, 37, 38, 39, 40, + 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, + 58, 59, 60, 61, 62, 64, 65, 66, 67, 69, 70, 71, 73, 74, 76, 77, + 78, 80, 81, 83, 84, 86, 87, 89, 90, 92, 94, 95, 97, 99, 100, 102, + 104, 106, 107, 109, 111, 113, 115, 117, 118, 120, 122, 124, 126, 128, 130, 132, + 134, 137, 139, 141, 143, 145, 147, 150, 152, 154, 156, 159, 161, 163, 166, 168, + 171, 173, 175, 178, 180, 183, 186, 188, 191, 193, 196, 199, 201, 204, 207, 210, + 212, 215, 218, 221, 224, 227, 230, 233, 236, 239, 242, 245, 248, 251, 254, 257, + 260, 263, 267, 270, 273, 276, 280, 283, 286, 290, 293, 297, 300, 304, 307, 311, + 314, 318, 321, 325, 328, 332, 336, 339, 343, 347, 351, 354, 358, 362, 366, 370, + 374, 378, 381, 385, 389, 393, 397, 401, 405, 410, 414, 418, 422, 426, 430, 434, + 439, 443, 447, 451, 456, 460, 464, 469, 473, 477, 482, 486, 491, 495, 499, 504, + 508, 513, 517, 522, 527, 531, 536, 540, 545, 550, 554, 559, 563, 568, 573, 577, + 582, 587, 592, 596, 601, 606, 611, 615, 620, 625, 630, 635, 640, 644, 649, 654, + 659, 664, 669, 674, 678, 683, 688, 693, 698, 703, 708, 713, 718, 723, 728, 732, + 737, 742, 747, 752, 757, 762, 767, 772, 777, 782, 787, 792, 797, 802, 806, 811, + 816, 821, 826, 831, 836, 841, 846, 851, 855, 860, 865, 870, 875, 880, 884, 889, + 894, 899, 904, 908, 913, 918, 923, 927, 932, 937, 941, 946, 951, 955, 960, 965, + 969, 974, 978, 983, 988, 992, 997, 1001, 1005, 1010, 1014, 1019, 1023, 1027, 1032, 1036, + 1040, 1045, 1049, 1053, 1057, 1061, 1066, 1070, 1074, 1078, 1082, 1086, 1090, 1094, 1098, 1102, + 1106, 1109, 1113, 1117, 1121, 1125, 1128, 1132, 1136, 1139, 1143, 1146, 1150, 1153, 1157, 1160, + 1164, 1167, 1170, 1174, 1177, 1180, 1183, 1186, 1190, 1193, 1196, 1199, 1202, 1205, 1207, 1210, + 1213, 1216, 1219, 1221, 1224, 1227, 1229, 1232, 1234, 1237, 1239, 1241, 1244, 1246, 1248, 1251, + 1253, 1255, 1257, 1259, 1261, 1263, 1265, 1267, 1269, 1270, 1272, 1274, 1275, 1277, 1279, 1280, + 1282, 1283, 1284, 1286, 1287, 1288, 1290, 1291, 1292, 1293, 1294, 1295, 1296, 1297, 1297, 1298, + 1299, 1300, 1300, 1301, 1302, 1302, 1303, 1303, 1303, 1304, 1304, 1304, 1304, 1304, 1305, 1305 + }; + + //! @brief 8x voices of sample used to make sound + std::array _voices {}; + Master _master {}; + Echo _echo {}; + Noise _noise {}; + BRR _brr {}; + Latch _latch {}; + State _state; + Timer _timer {}; + + void voiceOutput(Voice &voice, bool channel); + void voice1(Voice &voice); + void voice2(Voice &voice); + void voice3(Voice &voice); + void voice3a(Voice &voice); + void voice3b(Voice &voice); + void voice3c(Voice &voice); + void voice4(Voice &voice); + void voice5(Voice &voice); + void voice6(Voice &voice); + void voice7(Voice &voice); + void voice8(Voice &voice); + void voice9(Voice &voice); + void echo22(); + void echo23(); + void echo24(); + void echo25(); + void echo26(); + void echo27(); + void echo28(); + void echo29(); + void echo30(); + void misc27(); + void misc28(); + void misc29(); + void misc30(); + + //! @brief Interpolate voice samples with gauss table + int32_t interpolate(const Voice &voice); + //! @brief Modify voice samples with its envelope + void runEnvelope(Voice &voice); + + int32_t loadFIR(bool channel, int fir); + void loadEcho(bool channel); + int16_t outputEcho(bool channel); + void writeEcho(bool channel); + + //! @brief Remove one tick from timer + void timerTick(); + //! @brief Check if timer value is equal to rate value + bool timerPoll(uint32_t rate); + + //! @brief Transform BRR value to samples + void decodeBRR(Voice &voice); + + //! @brief Whole APU RAM map + std::weak_ptr _map; + + //! @brief Read inside APU RAM + uint8_t _readRAM(uint24_t addr); + //! @brief Write into APU RAM + void _writeRAM(uint24_t addr, uint8_t data); public: - DSP() = default; + DSP(std::array &buffer, uint32_t size, std::weak_ptr map); DSP(const DSP &) = default; DSP &operator=(const DSP &) = default; - ~DSP() override = default; + ~DSP() = default; - Registers getRegisters(); - - std::array getChannels(); + //! @brief Return all 8 voices from DSP + const std::array &getVoices() const; + const Master &getMaster() const; + const Echo &getEcho() const; + const Noise &getNoise() const; + const BRR &getBrr() const; + const Latch &getLatch() const; //! @brief Read from the internal DSP register. //! @param addr The address to read from. The address 0x0 should refer to the first byte of the register. //! @throw InvalidAddress will be thrown if the address is more than $7F (the number of register). //! @return Return the value of the register. - uint8_t read(uint24_t addr) const override; + uint8_t read(uint24_t addr) const; //! @brief Write data to the internal DSP register. //! @param addr The address to write to. The address 0x0 should refer to the first byte of register. //! @param data The new value of the register. //! @throw InvalidAddress will be thrown if the address is more than $7F (the number of register). - void write(uint24_t addr, uint8_t data) override; + void write(uint24_t addr, uint8_t data); //! @brief Get the name of this accessor (used for debug purpose) - std::string getName() const override; + std::string getName() const; + //! @brief Execute current voice transformation + void update(); //! @brief Get the component of this accessor (used for debug purpose) - Component getComponent() const override; + Component getComponent() const; //! @brief Get the size of the data. This size can be lower than the mapped data. //! @return The number of bytes inside this memory. - uint24_t getSize() const override; + uint24_t getSize() const; + //! @brief Return the number of samples written + int32_t getSamplesCount() const; }; } diff --git a/sources/APU/DSP/Echo.cpp b/sources/APU/DSP/Echo.cpp new file mode 100644 index 0000000..83c013b --- /dev/null +++ b/sources/APU/DSP/Echo.cpp @@ -0,0 +1,168 @@ +// +// Created by melefo on 2/1/21. +// + +#include "DSP.hpp" + +namespace ComSquare::APU::DSP +{ + int32_t DSP::loadFIR(bool channel, int fir) + { + int32_t sample = this->_echo.history[channel][this->_echo.historyOffset + fir + 1]; + + return (sample * this->_echo.FIR[fir]) >> 6; + } + + void DSP::loadEcho(bool channel) + { + uint16_t address = this->_echo.address + channel * 2; + uint8_t low = this->_readRAM(address++); + uint8_t high = this->_readRAM(address); + int16_t echo = (high << 8) + low; + + this->_echo.history[channel][this->_echo.historyOffset] = echo >> 1; + } + + void DSP::writeEcho(bool channel) + { + if (!this->_echo.toggle) { + uint16_t address = this->_echo.address + channel * 2; + int16_t sample = this->_echo.output[channel]; + + this->_writeRAM(address++, sample); + this->_writeRAM(address, sample >> 8); + } + this->_echo.output[channel] = 0; + } + + int16_t DSP::outputEcho(bool channel) + { + int16_t master = this->_master.output[channel] * this->_master.volume[channel] >> 7; + int16_t echo = this->_echo.input[channel] * this->_echo.input[channel] >> 7; + + return master + echo; + } + + void DSP::echo22() + { + this->_echo.historyOffset += 1; + this->_echo.address = (this->_echo.value << 8) + this->_echo.offset; + + this->loadEcho(0); + + this->_echo.input[0] = this->loadFIR(0, 0); + this->_echo.input[1] = this->loadFIR(1, 0); + } + + void DSP::echo23() + { + this->loadEcho(1); + + this->_echo.input[0] += this->loadFIR(0, 1) + this->loadFIR(0, 2); + this->_echo.input[1] += this->loadFIR(1, 1) + this->loadFIR(1, 2); + } + + void DSP::echo24() + { + this->_echo.input[0] += this->loadFIR(0, 3) + this->loadFIR(0, 4) + this->loadFIR(0, 5); + this->_echo.input[1] += this->loadFIR(1, 3) + this->loadFIR(1, 4) + this->loadFIR(1, 5); + } + + void DSP::echo25() + { + this->_echo.input[0] += this->loadFIR(0, 6) + this->loadFIR(0, 7); + this->_echo.input[1] += this->loadFIR(1, 6) + this->loadFIR(1, 7); + } + + void DSP::echo26() + { + this->_master.output[0] = this->outputEcho(0); + + this->_echo.output[0] += this->_echo.input[0] * this->_echo.feedback >> 7; + this->_echo.output[1] += this->_echo.input[1] * this->_echo.feedback >> 7; + } + + void DSP::echo27() + { + int16_t outputLeft = this->_master.output[0]; + int16_t outputRight = this->outputEcho(1); + + this->_master.output[0] = 0; + this->_master.output[1] = 0; + + if (this->_master.mute) { + outputLeft = 0; + outputRight = 0; + } + + this->_state.buffer[this->_state.bufferOffset] = outputLeft; + this->_state.buffer[this->_state.bufferOffset + 1] = outputRight; + this->_state.bufferOffset += 2; + if (this->_state.bufferOffset >= this->_state.bufferSize) + this->_state.bufferOffset = 0; + } + + void DSP::echo28() + { + this->_echo.toggle = this->_echo.enabled; + } + + void DSP::echo29() + { + this->_echo.value = this->_echo.data; + + if (!this->_echo.offset) + this->_echo.length = this->_echo.delay << 11; + + this->_echo.offset += 4; + if (this->_echo.offset >= this->_echo.length) + this->_echo.offset = 0; + + this->writeEcho(0); + + echo28(); + } + + void DSP::echo30() + { + this->writeEcho(1); + } + + void DSP::misc27() + { + for (int i = 0; i < 8; i++) + this->_voices[i].prevPmon = this->_voices[i].pmon; + } + + void DSP::misc28() + { + for (int i = 0; i < 8; i++) + this->_voices[i].tempNon = this->_voices[i].non; + this->_brr.offsetAddr = this->_brr.offset; + } + + void DSP::misc29() + { + this->_timer.sample = !this->_timer.sample; + if (this->_timer.sample) { + for (int i = 0; i < 8; i++) + this->_voices[i].kon = 0; + } + } + + void DSP::misc30() + { + if (this->_timer.sample) { + for (int i = 0; i < 8; i++) + this->_voices[i].kof = 0; + } + + this->timerTick(); + + if (!this->timerPoll(this->_noise.clock)) + return; + int32_t feedback = this->_noise.lfsr << 13 ^ this->_noise.lfsr << 14; + + this->_noise.lfsr = (feedback & 0x4000) ^ this->_noise.lfsr >> 1; + } +} \ No newline at end of file diff --git a/sources/APU/DSP/Envelope.cpp b/sources/APU/DSP/Envelope.cpp new file mode 100644 index 0000000..ca8b506 --- /dev/null +++ b/sources/APU/DSP/Envelope.cpp @@ -0,0 +1,78 @@ +// +// Created by melefo on 2/3/21. +// + +#include "DSP.hpp" + +namespace ComSquare::APU::DSP +{ + void DSP::runEnvelope(Voice &voice) + { + int32_t envelope = voice.envelope; + + if (voice.envelopeMode == Envelope::Release) { + envelope -= 0x08; + if (envelope < 0) + envelope = 0; + voice.envelope = envelope; + return; + } + + int32_t rate; + int32_t mode; + int32_t data = voice.adsr2; + if (this->_latch.adsr1 & 0b10000000) { + if (voice.envelopeMode >= Envelope::Decay) { + envelope -= 1; + envelope -= envelope >> 8; + rate = data & 0b11111; + if (voice.envelopeMode == Envelope::Decay) + rate = ((this->_latch.adsr1 >> 3) & 0x0E) + 0x10; + } + else { + rate = ((this->_latch.adsr1 & 0b1111) << 1) + 1; + if (rate < 0b11111) + envelope += 0x20; + else + envelope += 0x400; + } + } + else { + data = voice.gain; + mode = data >> 5; + if (mode < 4) { + envelope = data << 4; + rate = 0b11111; + } + else { + rate = data & 0b11111; + if (mode == 4) + envelope -= 0x20; + else if (mode < 6) { + envelope -= 1; + envelope -= envelope >> 8; + } + else { + envelope += 0x20; + if (mode > 6 && voice.hiddenEnvelope >= 0x600) + envelope += 0x08 - 0x20; + } + } + } + + if (envelope >> 8 == (data >> 5) && voice.envelopeMode == Envelope::Decay) + voice.envelopeMode = Envelope::Sustain; + voice.hiddenEnvelope = envelope; + if (static_cast(envelope) > 0x7FF) { + if (envelope < 0) + envelope = 0; + else + envelope = 0x7FF; + if (voice.envelopeMode == Envelope::Attack) + voice.envelopeMode = Envelope::Decay; + } + + if (this->timerPoll(rate)) + voice.envelope = envelope; + } +} \ No newline at end of file diff --git a/sources/APU/DSP/Gauss.cpp b/sources/APU/DSP/Gauss.cpp new file mode 100644 index 0000000..0b8003a --- /dev/null +++ b/sources/APU/DSP/Gauss.cpp @@ -0,0 +1,30 @@ +// +// Created by melefo on 2/3/21. +// + +#include +#include +#include "DSP.hpp" + +namespace ComSquare::APU::DSP +{ + int32_t DSP::interpolate(const Voice &voice) + { + int32_t interpolated; + uint8_t offset = voice.gaussOffset >> 4; + int forward = 255 - offset; + int reverse = offset; + + offset = (voice.sampleOffset + (voice.gaussOffset >> 12)) % 12; + interpolated = this->_gauss[forward] * voice.samples[offset++] >> 11; + offset %= 12; + interpolated += this->_gauss[forward + 256] * voice.samples[offset++] >> 11; + offset %= 12; + interpolated += this->_gauss[reverse + 256] * voice.samples[offset++] >> 11; + offset %= 12; + interpolated = static_cast(interpolated); + interpolated += this->_gauss[reverse] * voice.samples[offset] >> 11; + + return std::clamp(interpolated, 0, 16) & ~1; + } +} \ No newline at end of file diff --git a/sources/APU/DSP/Timer.cpp b/sources/APU/DSP/Timer.cpp new file mode 100644 index 0000000..1b060d4 --- /dev/null +++ b/sources/APU/DSP/Timer.cpp @@ -0,0 +1,22 @@ +// +// Created by melefo on 2/3/21. +// + +#include "DSP.hpp" + +namespace ComSquare::APU::DSP +{ + void DSP::timerTick() + { + if (!this->_timer.counter) + this->_timer.counter = 0x7800; + this->_timer.counter -= 1; + } + + bool DSP::timerPoll(uint32_t rate) + { + if (!rate) + return false; + return (this->_timer.counter + this->_counterOffset[rate]) % this->_rateModulus[rate] == 0; + } +} \ No newline at end of file diff --git a/sources/APU/DSP/Voice.cpp b/sources/APU/DSP/Voice.cpp new file mode 100644 index 0000000..324e07e --- /dev/null +++ b/sources/APU/DSP/Voice.cpp @@ -0,0 +1,156 @@ +// +// Created by melefo on 2/1/21. +// + +#include "DSP.hpp" +#include "../APU.hpp" +#include + +namespace ComSquare::APU::DSP +{ + void DSP::voiceOutput(Voice &voice, bool channel) + { + int out = this->_latch.output * voice.volume[channel] >> 7; + + this->_master.output[channel] += out; + if (!voice.echo) + return; + this->_echo.volume[channel] += out; + } + + void DSP::voice1(Voice &voice) + { + this->_brr.address = (this->_brr.offsetAddr << 8) + (this->_brr.source << 2); + this->_brr.source = voice.srcn; + } + + void DSP::voice2(Voice &voice) + { + uint16_t addr = this->_brr.address; + + if (!voice.konDelay) + addr += 2; + this->_brr.nextAddress = this->_readRAM(addr++); + this->_brr.nextAddress += this->_readRAM(addr++) << 8; + this->_latch.adsr1 = voice.adsr1; + this->_latch.pitch = voice.pitch & 0xFF; + } + + void DSP::voice3(Voice &voice) + { + this->voice3a(voice); + this->voice3b(voice); + this->voice3c(voice); + } + + void DSP::voice3a(Voice &voice) + { + this->_latch.pitch |= (voice.pitchH & 0x3F) << 8; + } + + void DSP::voice3b(Voice &voice) + { + this->_brr.header = this->_readRAM(this->_brr.address); + this->_brr.value = this->_readRAM(this->_brr.address + voice.brrOffset); + } + + void DSP::voice3c(Voice &voice) + { + if (voice.prevPmon) + this->_latch.pitch += (this->_latch.output >> 5) * this->_latch.pitch >> 10; + + if (voice.konDelay) { + if (voice.konDelay == 5) { + voice.brrAddress = this->_brr.nextAddress; + voice.brrOffset = 1; + voice.sampleOffset = 0; + this->_brr.header = 0; + } + + voice.envelope = 0; + voice.hiddenEnvelope = 0; + voice.gaussOffset = 0; + voice.konDelay -= 1; + if (voice.konDelay & 3) + voice.gaussOffset = 0x4000; + this->_latch.pitch = 0; + } + + int32_t interpolated = this->interpolate(voice); + + if (voice.tempNon) + interpolated = this->_noise.lfsr << 1; + + this->_latch.output = interpolated * voice.envelope >> 11 & ~1; + voice.envx = voice.envelope >> 4; + + if (this->_master.reset || (this->_brr.header & 3) == 1) { + voice.envelope = 0; + voice.envelopeMode = Envelope::Release; + } + + if (this->_timer.sample) + { + if (voice.tempKof) + voice.envelopeMode = Envelope::Release; + if (voice.tempKon) { + voice.konDelay = 5; + voice.envelopeMode = Envelope::Attack; + } + } + + if (!voice.konDelay) + this->runEnvelope(voice); + } + + void DSP::voice4(Voice &voice) + { + voice.loop = false; + if (voice.gaussOffset >= 0x4000) { + this->decodeBRR(voice); + voice.brrOffset += 2; + if (voice.brrOffset >= 9) { + voice.brrOffset = voice.brrAddress + 9; + if (this->_brr.header & 0b1) { + voice.brrAddress = this->_brr.nextAddress; + voice.loop = true; + } + voice.brrOffset = 1; + } + } + + voice.gaussOffset = (voice.gaussOffset & 0x3FFF) + this->_latch.pitch; + if (voice.gaussOffset > 0x7FFF) + voice.gaussOffset = 0x7FFF; + this->voiceOutput(voice, 0); + } + + void DSP::voice5(Voice &voice) + { + this->voiceOutput(voice, 1); + + voice.endx |= voice.loop; + if (voice.konDelay == 5) + voice.endx = 0x00; + } + + void DSP::voice6(Voice &) + { + this->_latch.outx = this->_latch.output >> 8; + } + + void DSP::voice7(Voice &voice) + { + this->_latch.envx = voice.envx; + } + + void DSP::voice8(Voice &voice) + { + voice.outx = this->_latch.outx; + } + + void DSP::voice9(Voice &voice) + { + voice.envx = this->_latch.envx; + } +} \ No newline at end of file diff --git a/sources/APU/Instructions/16bitDataTransmission.cpp b/sources/APU/Instructions/16bitDataTransmission.cpp index cfa8009..5a28ce6 100644 --- a/sources/APU/Instructions/16bitDataTransmission.cpp +++ b/sources/APU/Instructions/16bitDataTransmission.cpp @@ -11,9 +11,11 @@ namespace ComSquare::APU uint24_t addr2 = addr + 1 + (this->_internalRegisters.p ? 0x0100 : 0); if (to_ya) { - uint16_t value = ((this->_internalRead(addr2) << 8u) | this->_internalRead(addr)); + uint8_t tmp = this->_internalRead(addr2); + uint16_t value = (tmp << 8) | this->_internalRead(addr); - this->_internalRegisters.ya = value; + this->_internalRegisters.a = value; + this->_internalRegisters.y = (value >> 8); this->_setNZflags(value); } else { diff --git a/sources/CPU/CPU.cpp b/sources/CPU/CPU.cpp index 6ccfc60..7a45b5a 100644 --- a/sources/CPU/CPU.cpp +++ b/sources/CPU/CPU.cpp @@ -222,7 +222,7 @@ namespace ComSquare::CPU unsigned CPU::update() { unsigned cycles = 0; - const unsigned maxCycles = 0x17; + const unsigned maxCycles = 0x0C; for (DMA &channel : this->_dmaChannels) { if (!channel.enabled) diff --git a/sources/CPU/Instruction.hpp b/sources/CPU/Instruction.hpp index f6ef811..59115c1 100644 --- a/sources/CPU/Instruction.hpp +++ b/sources/CPU/Instruction.hpp @@ -55,11 +55,6 @@ namespace ComSquare::CPU std::string name = ""; AddressingMode addressingMode = Implied; int size = 0; - - Instruction() = default; - Instruction(const Instruction &) = default; - Instruction &operator=(const Instruction &) = default; - ~Instruction() = default; }; } #endif //COMSQUARE_INSTRUCTION_HPP diff --git a/sources/Debugger/APUDebug.cpp b/sources/Debugger/APUDebug.cpp index 62e2519..3222f8d 100644 --- a/sources/Debugger/APUDebug.cpp +++ b/sources/Debugger/APUDebug.cpp @@ -47,8 +47,8 @@ namespace ComSquare::Debugger this->_ui.dSPRegAddresshexaLineEdit->setText(Utility::to_hex(this->_registers.dspregAddr).c_str()); this->_ui.dSPRegAddressLineEdit->setText(Utility::to_binary(this->_registers.dspregAddr).c_str()); - this->_ui.dSPRegDatahexaLineEdit->setText(Utility::to_hex(this->_registers.dspregData).c_str()); - this->_ui.dSPRegDataLineEdit->setText(Utility::to_binary(this->_registers.dspregData).c_str()); + this->_ui.dSPRegDatahexaLineEdit->setText(Utility::to_hex(this->_dsp.read(this->_registers.dspregAddr)).c_str()); + this->_ui.dSPRegDataLineEdit->setText(Utility::to_binary(this->_dsp.read(this->_registers.dspregAddr)).c_str()); this->_ui.timer0hexaLineEdit->setText(Utility::to_hex(this->_registers.timer0).c_str()); this->_ui.timer0LineEdit->setText(Utility::to_binary(this->_registers.timer0).c_str()); @@ -84,143 +84,156 @@ namespace ComSquare::Debugger this->_ui.programCounterLineEdit->setText(Utility::to_hex(this->_internalRegisters.pc + 0x0001u).c_str()); this->_ui.programStatusWordLineEdit->setText(this->_getPSWString().c_str()); - this->_ui.mvolLprogressBar->setValue(this->_dsp->getRegisters().mvolL); - this->_ui.mvolRprogressBar->setValue(this->_dsp->getRegisters().mvolR); - this->_ui.evolLprogressBar->setValue(this->_dsp->getRegisters().evolL); - this->_ui.evolRprogressBar->setValue(this->_dsp->getRegisters().evolR); - this->_ui.echoprogressBar->setValue(this->_dsp->getRegisters().efb); - this->_ui.flagslineEdit->setText(Utility::to_binary(this->_dsp->getRegisters().flg).c_str()); - this->_ui.sourceDirectoryLineEdit->setText(Utility::to_hex(this->_dsp->getRegisters().dir).c_str()); - this->_ui.echoBufferOffsetLineEdit->setText(Utility::to_hex(this->_dsp->getRegisters().esa).c_str()); - this->_ui.echoDelayLineEdit->setText(Utility::to_hex(this->_dsp->getRegisters().edl).c_str()); + auto voices = this->_dsp.getVoices(); + auto master = this->_dsp.getMaster(); + auto echo = this->_dsp.getEcho(); + auto noise = this->_dsp.getNoise(); + auto brr = this->_dsp.getBrr(); + auto latch = this->_dsp.getLatch(); - this->_ui.VolumeLprogressBar->setValue(this->_dsp->getChannels()[0].volL); - this->_ui.VolumeRprogressBar->setValue(this->_dsp->getChannels()[0].volR); - this->_ui.WaveHeightprogressBar->setValue(this->_dsp->getChannels()[0].outx); - this->_ui.EchoFIRCoeffprogressBar->setValue(this->_dsp->getChannels()[0].coeff); - this->_ui.PitchlineEdit->setText(Utility::to_hex(this->_dsp->getChannels()[0].pitch).c_str()); - this->_ui.sourceNumberLineEdit->setText(Utility::to_hex(this->_dsp->getChannels()[0].srcn).c_str()); - this->_ui.GainlineEdit->setText(Utility::to_hex(this->_dsp->getChannels()[0].gain).c_str()); - this->_ui.EnvelopelineEdit->setText(Utility::to_hex(this->_dsp->getChannels()[0].envelope).c_str()); - this->_ui.EnvelopeValueLineEdit->setText(Utility::to_hex(this->_dsp->getChannels()[0].envx).c_str()); - this->_ui.KeyOncheckBox->setChecked(this->_dsp->getChannels()[0].kon); - this->_ui.KeyOffcheckBox->setChecked(this->_dsp->getChannels()[0].kof); - this->_ui.NoisecheckBox->setChecked(this->_dsp->getChannels()[0].non); - this->_ui.EchocheckBox->setChecked(this->_dsp->getChannels()[0].eon); - this->_ui.SampleEndcheckBox->setChecked(this->_dsp->getChannels()[0].endx); - this->_ui.PitchModulationcheckBox->setChecked(this->_dsp->getChannels()[0].pmon); + this->_ui.mvolLprogressBar->setValue(master.volume[0]); + this->_ui.mvolRprogressBar->setValue(master.volume[1]); + this->_ui.evolLprogressBar->setValue(echo.volume[0]); + this->_ui.evolRprogressBar->setValue(echo.volume[1]); + this->_ui.echoprogressBar->setValue(echo.feedback); - this->_ui.VolumeLprogressBar_2->setValue(this->_dsp->getChannels()[1].volL); - this->_ui.VolumeRprogressBar_2->setValue(this->_dsp->getChannels()[1].volR); - this->_ui.WaveHeightprogressBar_2->setValue(this->_dsp->getChannels()[1].outx); - this->_ui.EchoFIRCoeffprogressBar_2->setValue(this->_dsp->getChannels()[1].coeff); - this->_ui.PitchlineEdit_2->setText(Utility::to_hex(this->_dsp->getChannels()[1].pitch).c_str()); - this->_ui.sourceNumberLineEdit_2->setText(Utility::to_hex(this->_dsp->getChannels()[1].srcn).c_str()); - this->_ui.GainlineEdit_2->setText(Utility::to_hex(this->_dsp->getChannels()[1].gain).c_str()); - this->_ui.EnvelopelineEdit_2->setText(Utility::to_hex(this->_dsp->getChannels()[1].envelope).c_str()); - this->_ui.EnvelopeValueLineEdit_2->setText(Utility::to_hex(this->_dsp->getChannels()[1].envx).c_str()); - this->_ui.KeyOncheckBox_2->setChecked(this->_dsp->getChannels()[1].kon); - this->_ui.KeyOffcheckBox_2->setChecked(this->_dsp->getChannels()[1].kof); - this->_ui.NoisecheckBox_2->setChecked(this->_dsp->getChannels()[1].non); - this->_ui.EchocheckBox_2->setChecked(this->_dsp->getChannels()[1].eon); - this->_ui.SampleEndcheckBox_2->setChecked(this->_dsp->getChannels()[1].endx); - this->_ui.PitchModulationcheckBox_2->setChecked(this->_dsp->getChannels()[1].pmon); + uint8_t flg = 0; + flg += master.reset << 7; + flg += master.mute << 6; + flg += echo.enabled << 5; + flg += noise.clock; + this->_ui.flagslineEdit->setText(Utility::to_binary(flg).c_str()); + this->_ui.sourceDirectoryLineEdit->setText(Utility::to_hex(brr.offset).c_str()); + this->_ui.echoBufferOffsetLineEdit->setText(Utility::to_hex(echo.data).c_str()); + this->_ui.echoDelayLineEdit->setText(Utility::to_hex(echo.delay).c_str()); - this->_ui.VolumeLprogressBar_3->setValue(this->_dsp->getChannels()[2].volL); - this->_ui.VolumeRprogressBar_3->setValue(this->_dsp->getChannels()[2].volR); - this->_ui.WaveHeightprogressBar_3->setValue(this->_dsp->getChannels()[2].outx); - this->_ui.EchoFIRCoeffprogressBar_3->setValue(this->_dsp->getChannels()[2].coeff); - this->_ui.PitchlineEdit_3->setText(Utility::to_hex(this->_dsp->getChannels()[2].pitch).c_str()); - this->_ui.sourceNumberLineEdit_3->setText(Utility::to_hex(this->_dsp->getChannels()[2].srcn).c_str()); - this->_ui.GainlineEdit_3->setText(Utility::to_hex(this->_dsp->getChannels()[2].gain).c_str()); - this->_ui.EnvelopelineEdit_3->setText(Utility::to_hex(this->_dsp->getChannels()[2].envelope).c_str()); - this->_ui.EnvelopeValueLineEdit_3->setText(Utility::to_hex(this->_dsp->getChannels()[2].envx).c_str()); - this->_ui.KeyOncheckBox_3->setChecked(this->_dsp->getChannels()[2].kon); - this->_ui.KeyOffcheckBox_3->setChecked(this->_dsp->getChannels()[2].kof); - this->_ui.NoisecheckBox_3->setChecked(this->_dsp->getChannels()[2].non); - this->_ui.EchocheckBox_3->setChecked(this->_dsp->getChannels()[2].eon); - this->_ui.SampleEndcheckBox_3->setChecked(this->_dsp->getChannels()[2].endx); - this->_ui.PitchModulationcheckBox_3->setChecked(this->_dsp->getChannels()[2].pmon); + this->_ui.VolumeLprogressBar->setValue(voices[0].volume[0]); + this->_ui.VolumeRprogressBar->setValue(voices[0].volume[1]); + this->_ui.WaveHeightprogressBar->setValue(latch.outx); + this->_ui.EchoFIRCoeffprogressBar->setValue(echo.FIR[0]); + this->_ui.PitchlineEdit->setText(Utility::to_hex(voices[0].pitch).c_str()); + this->_ui.sourceNumberLineEdit->setText(Utility::to_hex(voices[0].srcn).c_str()); + this->_ui.GainlineEdit->setText(Utility::to_hex(voices[0].gain).c_str()); + this->_ui.EnvelopelineEdit->setText(Utility::to_hex(voices[0].envelope).c_str()); + this->_ui.EnvelopeValueLineEdit->setText(Utility::to_hex(voices[0].envx).c_str()); + this->_ui.KeyOncheckBox->setChecked(voices[0].kon); + this->_ui.KeyOffcheckBox->setChecked(voices[0].kof); + this->_ui.NoisecheckBox->setChecked(voices[0].non); + this->_ui.EchocheckBox->setChecked(voices[0].eon); + this->_ui.SampleEndcheckBox->setChecked(voices[0].endx); + this->_ui.PitchModulationcheckBox->setChecked(voices[0].pmon); - this->_ui.VolumeLprogressBar_4->setValue(this->_dsp->getChannels()[3].volL); - this->_ui.VolumeRprogressBar_4->setValue(this->_dsp->getChannels()[3].volR); - this->_ui.WaveHeightprogressBar_4->setValue(this->_dsp->getChannels()[3].outx); - this->_ui.EchoFIRCoeffprogressBar_4->setValue(this->_dsp->getChannels()[3].coeff); - this->_ui.PitchlineEdit_4->setText(Utility::to_hex(this->_dsp->getChannels()[3].pitch).c_str()); - this->_ui.sourceNumberLineEdit_4->setText(Utility::to_hex(this->_dsp->getChannels()[3].srcn).c_str()); - this->_ui.GainlineEdit_4->setText(Utility::to_hex(this->_dsp->getChannels()[3].gain).c_str()); - this->_ui.EnvelopelineEdit_4->setText(Utility::to_hex(this->_dsp->getChannels()[3].envelope).c_str()); - this->_ui.EnvelopeValueLineEdit_4->setText(Utility::to_hex(this->_dsp->getChannels()[3].envx).c_str()); - this->_ui.KeyOncheckBox_4->setChecked(this->_dsp->getChannels()[3].kon); - this->_ui.KeyOffcheckBox_4->setChecked(this->_dsp->getChannels()[3].kof); - this->_ui.NoisecheckBox_4->setChecked(this->_dsp->getChannels()[3].non); - this->_ui.EchocheckBox_4->setChecked(this->_dsp->getChannels()[3].eon); - this->_ui.SampleEndcheckBox_4->setChecked(this->_dsp->getChannels()[3].endx); - this->_ui.PitchModulationcheckBox_4->setChecked(this->_dsp->getChannels()[3].pmon); + this->_ui.VolumeLprogressBar_2->setValue(voices[1].volume[0]); + this->_ui.VolumeRprogressBar_2->setValue(voices[1].volume[1]); + this->_ui.WaveHeightprogressBar_2->setValue(latch.outx); + this->_ui.EchoFIRCoeffprogressBar_2->setValue(echo.FIR[1]); + this->_ui.PitchlineEdit_2->setText(Utility::to_hex(voices[1].pitch).c_str()); + this->_ui.sourceNumberLineEdit_2->setText(Utility::to_hex(voices[1].srcn).c_str()); + this->_ui.GainlineEdit_2->setText(Utility::to_hex(voices[1].gain).c_str()); + this->_ui.EnvelopelineEdit_2->setText(Utility::to_hex(voices[1].envelope).c_str()); + this->_ui.EnvelopeValueLineEdit_2->setText(Utility::to_hex(voices[1].envx).c_str()); + this->_ui.KeyOncheckBox_2->setChecked(voices[1].kon); + this->_ui.KeyOffcheckBox_2->setChecked(voices[1].kof); + this->_ui.NoisecheckBox_2->setChecked(voices[1].non); + this->_ui.EchocheckBox_2->setChecked(voices[1].eon); + this->_ui.SampleEndcheckBox_2->setChecked(voices[1].endx); + this->_ui.PitchModulationcheckBox_2->setChecked(voices[1].pmon); - this->_ui.VolumeLprogressBar_5->setValue(this->_dsp->getChannels()[4].volL); - this->_ui.VolumeRprogressBar_5->setValue(this->_dsp->getChannels()[4].volR); - this->_ui.WaveHeightprogressBar_5->setValue(this->_dsp->getChannels()[4].outx); - this->_ui.EchoFIRCoeffprogressBar_5->setValue(this->_dsp->getChannels()[4].coeff); - this->_ui.PitchlineEdit_5->setText(Utility::to_hex(this->_dsp->getChannels()[4].pitch).c_str()); - this->_ui.sourceNumberLineEdit_5->setText(Utility::to_hex(this->_dsp->getChannels()[4].srcn).c_str()); - this->_ui.GainlineEdit_5->setText(Utility::to_hex(this->_dsp->getChannels()[4].gain).c_str()); - this->_ui.EnvelopelineEdit_5->setText(Utility::to_hex(this->_dsp->getChannels()[4].envelope).c_str()); - this->_ui.EnvelopeValueLineEdit_5->setText(Utility::to_hex(this->_dsp->getChannels()[4].envx).c_str()); - this->_ui.KeyOncheckBox_5->setChecked(this->_dsp->getChannels()[4].kon); - this->_ui.KeyOffcheckBox_5->setChecked(this->_dsp->getChannels()[4].kof); - this->_ui.NoisecheckBox_5->setChecked(this->_dsp->getChannels()[4].non); - this->_ui.EchocheckBox_5->setChecked(this->_dsp->getChannels()[4].eon); - this->_ui.SampleEndcheckBox_5->setChecked(this->_dsp->getChannels()[4].endx); - this->_ui.PitchModulationcheckBox_5->setChecked(this->_dsp->getChannels()[4].pmon); + this->_ui.VolumeLprogressBar_3->setValue(voices[2].volume[0]); + this->_ui.VolumeRprogressBar_3->setValue(voices[2].volume[1]); + this->_ui.WaveHeightprogressBar_3->setValue(latch.outx); + this->_ui.EchoFIRCoeffprogressBar_3->setValue(echo.FIR[2]); + this->_ui.PitchlineEdit_3->setText(Utility::to_hex(voices[2].pitch).c_str()); + this->_ui.sourceNumberLineEdit_3->setText(Utility::to_hex(voices[2].srcn).c_str()); + this->_ui.GainlineEdit_3->setText(Utility::to_hex(voices[2].gain).c_str()); + this->_ui.EnvelopelineEdit_3->setText(Utility::to_hex(voices[2].envelope).c_str()); + this->_ui.EnvelopeValueLineEdit_3->setText(Utility::to_hex(voices[2].envx).c_str()); + this->_ui.KeyOncheckBox_3->setChecked(voices[2].kon); + this->_ui.KeyOffcheckBox_3->setChecked(voices[2].kof); + this->_ui.NoisecheckBox_3->setChecked(voices[2].non); + this->_ui.EchocheckBox_3->setChecked(voices[2].eon); + this->_ui.SampleEndcheckBox_3->setChecked(voices[2].endx); + this->_ui.PitchModulationcheckBox_3->setChecked(voices[2].pmon); - this->_ui.VolumeLprogressBar_6->setValue(this->_dsp->getChannels()[5].volL); - this->_ui.VolumeRprogressBar_6->setValue(this->_dsp->getChannels()[5].volR); - this->_ui.WaveHeightprogressBar_6->setValue(this->_dsp->getChannels()[5].outx); - this->_ui.EchoFIRCoeffprogressBar_6->setValue(this->_dsp->getChannels()[5].coeff); - this->_ui.PitchlineEdit_6->setText(Utility::to_hex(this->_dsp->getChannels()[5].pitch).c_str()); - this->_ui.sourceNumberLineEdit_6->setText(Utility::to_hex(this->_dsp->getChannels()[5].srcn).c_str()); - this->_ui.GainlineEdit_6->setText(Utility::to_hex(this->_dsp->getChannels()[5].gain).c_str()); - this->_ui.EnvelopelineEdit_6->setText(Utility::to_hex(this->_dsp->getChannels()[5].envelope).c_str()); - this->_ui.EnvelopeValueLineEdit_6->setText(Utility::to_hex(this->_dsp->getChannels()[5].envx).c_str()); - this->_ui.KeyOncheckBox_6->setChecked(this->_dsp->getChannels()[5].kon); - this->_ui.KeyOffcheckBox_6->setChecked(this->_dsp->getChannels()[5].kof); - this->_ui.NoisecheckBox_6->setChecked(this->_dsp->getChannels()[5].non); - this->_ui.EchocheckBox_6->setChecked(this->_dsp->getChannels()[5].eon); - this->_ui.SampleEndcheckBox_6->setChecked(this->_dsp->getChannels()[5].endx); - this->_ui.PitchModulationcheckBox_6->setChecked(this->_dsp->getChannels()[5].pmon); + this->_ui.VolumeLprogressBar_4->setValue(voices[3].volume[0]); + this->_ui.VolumeRprogressBar_4->setValue(voices[3].volume[1]); + this->_ui.WaveHeightprogressBar_4->setValue(latch.outx); + this->_ui.EchoFIRCoeffprogressBar_4->setValue(echo.FIR[3]); + this->_ui.PitchlineEdit_4->setText(Utility::to_hex(voices[3].pitch).c_str()); + this->_ui.sourceNumberLineEdit_4->setText(Utility::to_hex(voices[3].srcn).c_str()); + this->_ui.GainlineEdit_4->setText(Utility::to_hex(voices[3].gain).c_str()); + this->_ui.EnvelopelineEdit_4->setText(Utility::to_hex(voices[3].envelope).c_str()); + this->_ui.EnvelopeValueLineEdit_4->setText(Utility::to_hex(voices[3].envx).c_str()); + this->_ui.KeyOncheckBox_4->setChecked(voices[3].kon); + this->_ui.KeyOffcheckBox_4->setChecked(voices[3].kof); + this->_ui.NoisecheckBox_4->setChecked(voices[3].non); + this->_ui.EchocheckBox_4->setChecked(voices[3].eon); + this->_ui.SampleEndcheckBox_4->setChecked(voices[3].endx); + this->_ui.PitchModulationcheckBox_4->setChecked(voices[3].pmon); - this->_ui.VolumeLprogressBar_7->setValue(this->_dsp->getChannels()[6].volL); - this->_ui.VolumeRprogressBar_7->setValue(this->_dsp->getChannels()[6].volR); - this->_ui.WaveHeightprogressBar_7->setValue(this->_dsp->getChannels()[6].outx); - this->_ui.EchoFIRCoeffprogressBar_7->setValue(this->_dsp->getChannels()[6].coeff); - this->_ui.PitchlineEdit_7->setText(Utility::to_hex(this->_dsp->getChannels()[6].pitch).c_str()); - this->_ui.sourceNumberLineEdit_7->setText(Utility::to_hex(this->_dsp->getChannels()[6].srcn).c_str()); - this->_ui.GainlineEdit_7->setText(Utility::to_hex(this->_dsp->getChannels()[6].gain).c_str()); - this->_ui.EnvelopelineEdit_7->setText(Utility::to_hex(this->_dsp->getChannels()[6].envelope).c_str()); - this->_ui.EnvelopeValueLineEdit_7->setText(Utility::to_hex(this->_dsp->getChannels()[6].envx).c_str()); - this->_ui.KeyOncheckBox_7->setChecked(this->_dsp->getChannels()[6].kon); - this->_ui.KeyOffcheckBox_7->setChecked(this->_dsp->getChannels()[6].kof); - this->_ui.NoisecheckBox_7->setChecked(this->_dsp->getChannels()[6].non); - this->_ui.EchocheckBox_7->setChecked(this->_dsp->getChannels()[6].eon); - this->_ui.SampleEndcheckBox_7->setChecked(this->_dsp->getChannels()[6].endx); - this->_ui.PitchModulationcheckBox_7->setChecked(this->_dsp->getChannels()[6].pmon); + this->_ui.VolumeLprogressBar_5->setValue(voices[4].volume[0]); + this->_ui.VolumeRprogressBar_5->setValue(voices[4].volume[1]); + this->_ui.WaveHeightprogressBar_5->setValue(latch.outx); + this->_ui.EchoFIRCoeffprogressBar_5->setValue(echo.FIR[4]); + this->_ui.PitchlineEdit_5->setText(Utility::to_hex(voices[4].pitch).c_str()); + this->_ui.sourceNumberLineEdit_5->setText(Utility::to_hex(voices[4].srcn).c_str()); + this->_ui.GainlineEdit_5->setText(Utility::to_hex(voices[4].gain).c_str()); + this->_ui.EnvelopelineEdit_5->setText(Utility::to_hex(voices[4].envelope).c_str()); + this->_ui.EnvelopeValueLineEdit_5->setText(Utility::to_hex(voices[4].envx).c_str()); + this->_ui.KeyOncheckBox_5->setChecked(voices[4].kon); + this->_ui.KeyOffcheckBox_5->setChecked(voices[4].kof); + this->_ui.NoisecheckBox_5->setChecked(voices[4].non); + this->_ui.EchocheckBox_5->setChecked(voices[4].eon); + this->_ui.SampleEndcheckBox_5->setChecked(voices[4].endx); + this->_ui.PitchModulationcheckBox_5->setChecked(voices[4].pmon); - this->_ui.VolumeLprogressBar_8->setValue(this->_dsp->getChannels()[7].volL); - this->_ui.VolumeRprogressBar_8->setValue(this->_dsp->getChannels()[7].volR); - this->_ui.WaveHeightprogressBar_8->setValue(this->_dsp->getChannels()[7].outx); - this->_ui.EchoFIRCoeffprogressBar_8->setValue(this->_dsp->getChannels()[7].coeff); - this->_ui.PitchlineEdit_8->setText(Utility::to_hex(this->_dsp->getChannels()[7].pitch).c_str()); - this->_ui.sourceNumberLineEdit_8->setText(Utility::to_hex(this->_dsp->getChannels()[7].srcn).c_str()); - this->_ui.GainlineEdit_8->setText(Utility::to_hex(this->_dsp->getChannels()[7].gain).c_str()); - this->_ui.EnvelopelineEdit_8->setText(Utility::to_hex(this->_dsp->getChannels()[7].envelope).c_str()); - this->_ui.EnvelopeValueLineEdit_8->setText(Utility::to_hex(this->_dsp->getChannels()[7].envx).c_str()); - this->_ui.KeyOncheckBox_8->setChecked(this->_dsp->getChannels()[7].kon); - this->_ui.KeyOffcheckBox_8->setChecked(this->_dsp->getChannels()[7].kof); - this->_ui.NoisecheckBox_8->setChecked(this->_dsp->getChannels()[7].non); - this->_ui.EchocheckBox_8->setChecked(this->_dsp->getChannels()[7].eon); - this->_ui.SampleEndcheckBox_8->setChecked(this->_dsp->getChannels()[7].endx); - this->_ui.PitchModulationcheckBox_8->setChecked(this->_dsp->getChannels()[7].pmon); + this->_ui.VolumeLprogressBar_6->setValue(voices[5].volume[0]); + this->_ui.VolumeRprogressBar_6->setValue(voices[5].volume[1]); + this->_ui.WaveHeightprogressBar_6->setValue(latch.outx); + this->_ui.EchoFIRCoeffprogressBar_6->setValue(echo.FIR[5]); + this->_ui.PitchlineEdit_6->setText(Utility::to_hex(voices[5].pitch).c_str()); + this->_ui.sourceNumberLineEdit_6->setText(Utility::to_hex(voices[5].srcn).c_str()); + this->_ui.GainlineEdit_6->setText(Utility::to_hex(voices[5].gain).c_str()); + this->_ui.EnvelopelineEdit_6->setText(Utility::to_hex(voices[5].envelope).c_str()); + this->_ui.EnvelopeValueLineEdit_6->setText(Utility::to_hex(voices[5].envx).c_str()); + this->_ui.KeyOncheckBox_6->setChecked(voices[5].kon); + this->_ui.KeyOffcheckBox_6->setChecked(voices[5].kof); + this->_ui.NoisecheckBox_6->setChecked(voices[5].non); + this->_ui.EchocheckBox_6->setChecked(voices[5].eon); + this->_ui.SampleEndcheckBox_6->setChecked(voices[5].endx); + this->_ui.PitchModulationcheckBox_6->setChecked(voices[5].pmon); + + this->_ui.VolumeLprogressBar_7->setValue(voices[6].volume[0]); + this->_ui.VolumeRprogressBar_7->setValue(voices[6].volume[1]); + this->_ui.WaveHeightprogressBar_7->setValue(latch.outx); + this->_ui.EchoFIRCoeffprogressBar_7->setValue(echo.FIR[6]); + this->_ui.PitchlineEdit_7->setText(Utility::to_hex(voices[6].pitch).c_str()); + this->_ui.sourceNumberLineEdit_7->setText(Utility::to_hex(voices[6].srcn).c_str()); + this->_ui.GainlineEdit_7->setText(Utility::to_hex(voices[6].gain).c_str()); + this->_ui.EnvelopelineEdit_7->setText(Utility::to_hex(voices[6].envelope).c_str()); + this->_ui.EnvelopeValueLineEdit_7->setText(Utility::to_hex(voices[6].envx).c_str()); + this->_ui.KeyOncheckBox_7->setChecked(voices[6].kon); + this->_ui.KeyOffcheckBox_7->setChecked(voices[6].kof); + this->_ui.NoisecheckBox_7->setChecked(voices[6].non); + this->_ui.EchocheckBox_7->setChecked(voices[6].eon); + this->_ui.SampleEndcheckBox_7->setChecked(voices[6].endx); + this->_ui.PitchModulationcheckBox_7->setChecked(voices[6].pmon); + + this->_ui.VolumeLprogressBar_8->setValue(voices[7].volume[0]); + this->_ui.VolumeRprogressBar_8->setValue(voices[7].volume[1]); + this->_ui.WaveHeightprogressBar_8->setValue(latch.outx); + this->_ui.EchoFIRCoeffprogressBar_8->setValue(echo.FIR[7]); + this->_ui.PitchlineEdit_8->setText(Utility::to_hex(voices[7].pitch).c_str()); + this->_ui.sourceNumberLineEdit_8->setText(Utility::to_hex(voices[7].srcn).c_str()); + this->_ui.GainlineEdit_8->setText(Utility::to_hex(voices[7].gain).c_str()); + this->_ui.EnvelopelineEdit_8->setText(Utility::to_hex(voices[7].envelope).c_str()); + this->_ui.EnvelopeValueLineEdit_8->setText(Utility::to_hex(voices[7].envx).c_str()); + this->_ui.KeyOncheckBox_8->setChecked(voices[7].kon); + this->_ui.KeyOffcheckBox_8->setChecked(voices[7].kof); + this->_ui.NoisecheckBox_8->setChecked(voices[7].non); + this->_ui.EchocheckBox_8->setChecked(voices[7].eon); + this->_ui.SampleEndcheckBox_8->setChecked(voices[7].endx); + this->_ui.PitchModulationcheckBox_8->setChecked(voices[7].pmon); } std::string APUDebug::_getPSWString() diff --git a/sources/Renderer/IRenderer.hpp b/sources/Renderer/IRenderer.hpp index fd073f9..a81a4ec 100644 --- a/sources/Renderer/IRenderer.hpp +++ b/sources/Renderer/IRenderer.hpp @@ -6,6 +6,7 @@ #define COMSQUARE_IRENDERER_HPP #include +#include namespace ComSquare { @@ -30,6 +31,11 @@ namespace ComSquare //! @param snes The snes game object (to call the update method). //! @param maxFPS The number of FPS you aim to run on. virtual void createWindow(SNES &snes, int maxFPS) = 0; + + //! @brief Playing all samples from buffer + //! @param samples Buffer containing samples + //! @param sampleCount number of samples inside buffer + virtual void playAudio(std::span samples, uint64_t sampleCount) = 0; }; } } diff --git a/sources/Renderer/NoRenderer.cpp b/sources/Renderer/NoRenderer.cpp index 35fe608..3eec68b 100644 --- a/sources/Renderer/NoRenderer.cpp +++ b/sources/Renderer/NoRenderer.cpp @@ -20,6 +20,10 @@ namespace ComSquare::Renderer (void)rgba; } + void NoRenderer::playAudio(std::span, uint64_t) + { + } + void NoRenderer::getEvents() { } NoRenderer::NoRenderer(unsigned int height, unsigned int width, int maxFPS) diff --git a/sources/Renderer/NoRenderer.hpp b/sources/Renderer/NoRenderer.hpp index 9abf3f7..e5a3d7f 100644 --- a/sources/Renderer/NoRenderer.hpp +++ b/sources/Renderer/NoRenderer.hpp @@ -21,7 +21,11 @@ namespace ComSquare::Renderer //! @param X horizontal index. //! @param Y vertical index. //! @param rgba The color of the pixel. - void putPixel(unsigned y, unsigned x, uint32_t rgba) override ; + void putPixel(unsigned y, unsigned x, uint32_t rgba) override; + //! @brief Playing all samples from buffer + //! @param samples Buffer containing samples + //! @param sampleCount number of samples inside buffer + void playAudio(std::span samples, uint64_t sampleCount) override; //! @brief Get the inputs from the Window void getEvents(); //! @brief Use this function to create the window. diff --git a/sources/Renderer/QtRenderer/QtSFML.cpp b/sources/Renderer/QtRenderer/QtSFML.cpp index adc3f4b..950ca09 100644 --- a/sources/Renderer/QtRenderer/QtSFML.cpp +++ b/sources/Renderer/QtRenderer/QtSFML.cpp @@ -12,7 +12,7 @@ #ifdef Q_WS_X11 #include - #include + #include #endif namespace ComSquare::Renderer @@ -85,6 +85,11 @@ namespace ComSquare::Renderer this->_sfWidget->putPixel(y, x, rgba); } + void QtSFML::playAudio(std::span samples, uint64_t sampleCount) + { + this->_sfWidget->playAudio(samples, sampleCount); + } + void QtSFML::drawScreen() { } void QtSFML::setWindowName(std::string &newWindowName) diff --git a/sources/Renderer/QtRenderer/QtSFML.hpp b/sources/Renderer/QtRenderer/QtSFML.hpp index 4d4e077..2156052 100644 --- a/sources/Renderer/QtRenderer/QtSFML.hpp +++ b/sources/Renderer/QtRenderer/QtSFML.hpp @@ -65,6 +65,10 @@ namespace ComSquare::Renderer void putPixel(unsigned y, unsigned x, uint32_t rgba) override; //! @brief This function doesn't do anything because QT internally handle drawing to the screen. void drawScreen() override; + //! @brief Playing all samples from buffer + //! @param samples Buffer containing samples + //! @param sampleCount number of samples inside buffer + void playAudio(std::span samples, uint64_t sampleCount) override; //! @brief Set a new name to the window, if there is already a name it will be overwrite. //! @param newWindowName new title for the window. void setWindowName(std::string &newWindowName) override; diff --git a/sources/Renderer/SFRenderer.cpp b/sources/Renderer/SFRenderer.cpp index d9de9a5..b051ff7 100644 --- a/sources/Renderer/SFRenderer.cpp +++ b/sources/Renderer/SFRenderer.cpp @@ -18,6 +18,7 @@ namespace ComSquare::Renderer this->_texture.create(width, height); this->_sprite.setTexture(this->_texture); this->_pixelBuffer = new sf::Color[height * width]; + this->_sound.setBuffer(this->_soundBuffer); } void SFRenderer::createWindow(SNES &snes, int maxFPS) @@ -53,6 +54,12 @@ namespace ComSquare::Renderer this->_window.display(); } + void SFRenderer::playAudio(std::span samples, uint64_t sampleCount) + { + this->_soundBuffer.loadFromSamples(samples.data(), sampleCount, 2, 32040); + this->_sound.play(); + } + void SFRenderer::putPixel(unsigned y, unsigned x, uint32_t rgba) { if (x >= this->_videoMode.width) diff --git a/sources/Renderer/SFRenderer.hpp b/sources/Renderer/SFRenderer.hpp index a81712a..8221ea3 100644 --- a/sources/Renderer/SFRenderer.hpp +++ b/sources/Renderer/SFRenderer.hpp @@ -36,6 +36,11 @@ namespace ComSquare::Renderer sf::Sprite _sprite; //! @brief The texture to render the array of pixels sf::Texture _texture; + + //! @brief The buffer containing samples to be played + sf::SoundBuffer _soundBuffer; + //! @brief the sound played + sf::Sound _sound; public: //! @brief Tells to the program if the window has been closed, and therefore if he should stop bool shouldExit = false; @@ -49,6 +54,10 @@ namespace ComSquare::Renderer //! @param Y vertical index. //! @param rgba The color of the pixel. void putPixel(unsigned y, unsigned x, uint32_t rgba) override; + //! @brief Playing all samples from buffer + //! @param samples Buffer containing samples + //! @param sampleCount number of samples inside buffer + void playAudio(std::span samples, uint64_t sampleCount) override; //! @brief Get the inputs from the Window void getEvents(); //! @brief Use this function to create the window. diff --git a/sources/SNES.cpp b/sources/SNES.cpp index 2ea7edb..3ad1cb4 100644 --- a/sources/SNES.cpp +++ b/sources/SNES.cpp @@ -19,10 +19,9 @@ namespace ComSquare cartridge(new Cartridge::Cartridge(romPath)), wram(new Ram::Ram(16384, WRam, "WRam")), sram(new Ram::Ram(this->cartridge->header.sramSize, SRam, "SRam")), - apuRam(new APU::MemoryMap()), cpu(new CPU::CPU(this->bus, cartridge->header)), ppu(new PPU::PPU(renderer)), - apu(new APU::APU(this->apuRam)) + apu(new APU::APU(renderer)) { this->bus->mapComponents(*this); } diff --git a/sources/SNES.hpp b/sources/SNES.hpp index 5f604df..4739600 100644 --- a/sources/SNES.hpp +++ b/sources/SNES.hpp @@ -46,8 +46,6 @@ namespace ComSquare std::shared_ptr wram; //! @brief Save Ram residing inside the Cartridge in a real SNES. std::shared_ptr sram; - //! @brief External Ram used only by the Audio Processing Unit - std::shared_ptr apuRam; //! @brief Central Processing Unit of the SNES. std::shared_ptr cpu; //! @brief Picture Processing Unit of the SNES diff --git a/tests/APU/testAPU.cpp b/tests/APU/testAPU.cpp index 2c54013..767beb5 100644 --- a/tests/APU/testAPU.cpp +++ b/tests/APU/testAPU.cpp @@ -203,15 +203,6 @@ Test(executeInstruction, Valid) cr_assert_eq(result, 2); } -Test(executeInstruction, Invalid) -{ - Init() - auto apu = snes.apu; - - apu->_internalRegisters.pc = 0xFFFF; - cr_assert_throw(apu->_executeInstruction(), InvalidOpcode); -} - /////////////////////// // // // APU::update tests //