Panda3DS/include/audio/teakra_core.hpp
wheremyfoodat d459cb1d6c
Get audio output working with LLE DSP (#419)
* Implement audio output

* Semi-proper audio output

* Add audio enable and vsync settings

* Add audio enable and vsync settings

* Optimize audio output a bit

* Make max ring buffer timeout smaller

* Make max ring buffer timeout smaller

* Revert to spinlocking for audio sync

* Sleep emulator thread if too many samples queued

* Fix Teakra submodule breaking

* Don't start audio device too soon

* Fix IWYU errors

* Fix compilation errors on GCC/Clang

* Ignore std::hardware_destructive_interference_size on Android NDK

* Fix more IWYU errors
2024-02-24 03:26:23 +02:00

104 lines
3.5 KiB
C++

#pragma once
#include <array>
#include "audio/dsp_core.hpp"
#include "memory.hpp"
#include "swap.hpp"
#include "teakra/teakra.h"
namespace Audio {
class TeakraDSP : public DSPCore {
Teakra::Teakra teakra;
u32 pipeBaseAddr;
bool running; // Is the DSP running?
bool loaded; // Have we finished loading a binary with LoadComponent?
bool signalledData;
bool signalledSemaphore;
uint audioFrameIndex = 0; // Index in our audio frame
std::array<s16, 160 * 2> audioFrame;
// Get a pointer to a data memory address
u8* getDataPointer(u32 address) { return getDspMemory() + Memory::DSP_DATA_MEMORY_OFFSET + address; }
enum class PipeDirection {
DSPtoCPU = 0,
CPUtoDSP = 1,
};
// A lot of Teakra integration code, especially pipe stuff is based on Citra's integration here:
// https://github.com/citra-emu/citra/blob/master/src/audio_core/lle/lle.cpp
struct PipeStatus {
// All addresses and sizes here refer to byte values, NOT 16-bit values.
u16_le address;
u16_le byteSize;
u16_le readPointer;
u16_le writePointer;
u8 slot;
u8 flags;
static constexpr u16 wrapBit = 0x8000;
static constexpr u16 pointerMask = 0x7FFF;
bool isFull() const { return (readPointer ^ writePointer) == wrapBit; }
bool isEmpty() const { return (readPointer ^ writePointer) == 0; }
// isWrapped: Are read and write pointers in different memory passes.
// true: xxxx]----[xxxx (data is wrapping around the end of memory)
// false: ----[xxxx]----
bool isWrapped() const { return (readPointer ^ writePointer) >= wrapBit; }
};
static_assert(sizeof(PipeStatus) == 10, "Teakra: Pipe Status size is wrong");
static constexpr u8 pipeToSlotIndex(u8 pipe, PipeDirection direction) { return (pipe * 2) + u8(direction); }
PipeStatus getPipeStatus(u8 pipe, PipeDirection direction) {
PipeStatus ret;
const u8 index = pipeToSlotIndex(pipe, direction);
std::memcpy(&ret, getDataPointer(pipeBaseAddr * 2 + index * sizeof(PipeStatus)), sizeof(PipeStatus));
return ret;
}
void updatePipeStatus(const PipeStatus& status) {
u8 slot = status.slot;
u8* statusAddress = getDataPointer(pipeBaseAddr * 2 + slot * sizeof(PipeStatus));
if (slot % 2 == 0) {
std::memcpy(statusAddress + 4, &status.readPointer, sizeof(u16));
} else {
std::memcpy(statusAddress + 6, &status.writePointer, sizeof(u16));
}
}
// Run 1 slice of DSP instructions
void runSlice() {
if (running) {
teakra.Run(Audio::lleSlice);
}
}
public:
TeakraDSP(Memory& mem, Scheduler& scheduler, DSPService& dspService);
~TeakraDSP() override {}
void reset() override;
// Run 1 slice of DSP instructions and schedule the next audio frame
void runAudioFrame() override {
runSlice();
scheduler.addEvent(Scheduler::EventType::RunDSP, scheduler.currentTimestamp + Audio::lleSlice * 2);
}
void setAudioEnabled(bool enable) override;
u8* getDspMemory() override { return teakra.GetDspMemory().data(); }
u16 recvData(u32 regId) override { return teakra.RecvData(regId); }
bool recvDataIsReady(u32 regId) override { return teakra.RecvDataIsReady(regId); }
void setSemaphore(u16 value) override { teakra.SetSemaphore(value); }
void setSemaphoreMask(u16 value) override { teakra.MaskSemaphore(value); }
void writeProcessPipe(u32 channel, u32 size, u32 buffer) override;
std::vector<u8> readPipe(u32 channel, u32 peer, u32 size, u32 buffer) override;
void loadComponent(std::vector<u8>& data, u32 programMask, u32 dataMask) override;
void unloadComponent() override;
};
} // namespace Audio