Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

try #33

Closed
wants to merge 28 commits into from
Closed

try #33

Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
0a51a80
Add DSP emulation interface
wheremyfoodat Feb 16, 2024
363c71e
Hook DSP interface to DSP service HLE
wheremyfoodat Feb 16, 2024
5dd3c02
Moar Teak LLE
wheremyfoodat Feb 17, 2024
d3dc9e2
Forward interrupts properly in Teakra DSP core
wheremyfoodat Feb 17, 2024
f58354a
Update DSP semaphore on semaphore event signal
wheremyfoodat Feb 18, 2024
33eb096
Better DSP scheduling
wheremyfoodat Feb 18, 2024
7a5bb28
Make DSP backends properly configurable
wheremyfoodat Feb 19, 2024
f7c6ec3
Properly handle DSP interrupts in HLE
wheremyfoodat Feb 19, 2024
093364f
Merge pull request #410 from wheremyfoodat/dsp
wheremyfoodat Feb 19, 2024
fa4cc35
CMake: Include teakra after xbyak for better support with the JIT branch
wheremyfoodat Feb 19, 2024
505abbe
Stub NWM_UDS::Initialize to fail
wheremyfoodat Feb 20, 2024
267b1b3
Add proper proguard rules (#412)
Ishan09811 Feb 21, 2024
7b580ac
Test stripping Android shared library (#413)
wheremyfoodat Feb 21, 2024
6279ed6
Store program ID and expose it in Lua (#414)
wheremyfoodat Feb 22, 2024
cc1e7e2
Bind more Lua functions
wheremyfoodat Feb 22, 2024
de980f9
Merge pull request #415 from wheremyfoodat/wheremyfoodat-patch-3
wheremyfoodat Feb 22, 2024
9110176
Bind LoadROM to Lua
wheremyfoodat Feb 22, 2024
85161b0
Update lua.cpp
wheremyfoodat Feb 22, 2024
89a800d
Merge pull request #416 from wheremyfoodat/wheremyfoodat-patch-3
wheremyfoodat Feb 22, 2024
4b46b6e
Bind input to Lua
wheremyfoodat Feb 22, 2024
d010d95
Merge pull request #417 from wheremyfoodat/wheremyfoodat-patch-4
wheremyfoodat Feb 22, 2024
8bca988
Fix compilation errors
wheremyfoodat Feb 23, 2024
d459cb1
Get audio output working with LLE DSP (#419)
wheremyfoodat Feb 24, 2024
9d8868c
Don't start audio device on resume if audio is disabled
wheremyfoodat Feb 25, 2024
31fc241
fxaa-try
Ishan09811 Feb 26, 2024
ff4a0fe
fixes
Ishan09811 Feb 26, 2024
8130061
more fixes
Ishan09811 Feb 26, 2024
c53ffae
proper shader
Ishan09811 Feb 26, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 8 additions & 2 deletions .github/workflows/Android_Build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,11 @@ jobs:
git apply ./.github/gles.patch
# Build the project with CMake
cmake --build ${{github.workspace}}/build --config ${{ env.BUILD_TYPE }}
# Move the generated library to the appropriate location

# Strip the generated library and move it to the appropriate location
${ANDROID_NDK_ROOT}/toolchains/llvm/prebuilt/linux-x86_64/bin/llvm-strip --strip-unneeded ./build/libAlber.so
mv ./build/libAlber.so ./src/pandroid/app/src/main/jniLibs/x86_64/

# Build the Android app with Gradle
cd src/pandroid
./gradlew assemble${{ env.BUILD_TYPE }}
Expand Down Expand Up @@ -97,8 +100,11 @@ jobs:
git apply ./.github/gles.patch
# Build the project with CMake
cmake --build ${{github.workspace}}/build --config ${{ env.BUILD_TYPE }}
# Move the generated library to the appropriate location

# Strip the generated library and move it to the appropriate location
${ANDROID_NDK_ROOT}/toolchains/llvm/prebuilt/linux-x86_64/bin/llvm-strip --strip-unneeded ./build/libAlber.so
mv ./build/libAlber.so ./src/pandroid/app/src/main/jniLibs/arm64-v8a/

# Build the Android app with Gradle
cd src/pandroid
./gradlew assemble${{ env.BUILD_TYPE }}
Expand Down
6 changes: 6 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
Expand Up @@ -55,3 +55,9 @@
[submodule "third_party/libuv"]
path = third_party/libuv
url = https://github.com/libuv/libuv
[submodule "third_party/miniaudio"]
path = third_party/miniaudio
url = https://github.com/mackron/miniaudio
[submodule "third_party/teakra"]
path = third_party/teakra
url = https://github.com/wwylele/teakra
14 changes: 11 additions & 3 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ include_directories(third_party/xxhash/include)
include_directories(third_party/httplib)
include_directories(third_party/stb)
include_directories(third_party/opengl)
include_directories(third_party/miniaudio)
include_directories(third_party/mio/single_include)

add_compile_definitions(NOMINMAX) # Make windows.h not define min/max macros because third-party deps don't like it
Expand Down Expand Up @@ -149,12 +150,13 @@ if(HOST_X64 OR HOST_ARM64)
else()
message(FATAL_ERROR "Currently unsupported CPU architecture")
endif()
add_subdirectory(third_party/teakra EXCLUDE_FROM_ALL)

set(SOURCE_FILES src/emulator.cpp src/io_file.cpp src/config.cpp
src/core/CPU/cpu_dynarmic.cpp src/core/CPU/dynarmic_cycles.cpp
src/core/memory.cpp src/renderer.cpp src/core/renderer_null/renderer_null.cpp
src/http_server.cpp src/stb_image_write.c src/core/cheats.cpp src/core/action_replay.cpp
src/discord_rpc.cpp src/lua.cpp src/memory_mapped_file.cpp
src/discord_rpc.cpp src/lua.cpp src/memory_mapped_file.cpp src/miniaudio.cpp
)
set(CRYPTO_SOURCE_FILES src/core/crypto/aes_engine.cpp)
set(KERNEL_SOURCE_FILES src/core/kernel/kernel.cpp src/core/kernel/resource_limits.cpp
Expand Down Expand Up @@ -191,6 +193,9 @@ set(FS_SOURCE_FILES src/core/fs/archive_self_ncch.cpp src/core/fs/archive_save_d
set(APPLET_SOURCE_FILES src/core/applets/applet.cpp src/core/applets/mii_selector.cpp src/core/applets/software_keyboard.cpp src/core/applets/applet_manager.cpp
src/core/applets/error_applet.cpp
)
set(AUDIO_SOURCE_FILES src/core/audio/dsp_core.cpp src/core/audio/null_core.cpp src/core/audio/teakra_core.cpp
src/core/audio/miniaudio_device.cpp
)
set(RENDERER_SW_SOURCE_FILES src/core/renderer_sw/renderer_sw.cpp)

# Frontend source files
Expand Down Expand Up @@ -247,6 +252,8 @@ set(HEADER_FILES include/emulator.hpp include/helpers.hpp include/termcolor.hpp
include/services/amiibo_device.hpp include/services/nfc_types.hpp include/swap.hpp include/services/csnd.hpp include/services/nwm_uds.hpp
include/fs/archive_system_save_data.hpp include/lua_manager.hpp include/memory_mapped_file.hpp include/hydra_icon.hpp
include/PICA/dynapica/shader_rec_emitter_arm64.hpp include/scheduler.hpp include/applets/error_applet.hpp
include/audio/dsp_core.hpp include/audio/null_core.hpp include/audio/teakra_core.hpp
include/audio/miniaudio_device.hpp include/ring_buffer.hpp
)

cmrc_add_resource_library(
Expand Down Expand Up @@ -299,6 +306,7 @@ source_group("Source Files\\Core\\Loader" FILES ${LOADER_SOURCE_FILES})
source_group("Source Files\\Core\\Services" FILES ${SERVICE_SOURCE_FILES})
source_group("Source Files\\Core\\Applets" FILES ${APPLET_SOURCE_FILES})
source_group("Source Files\\Core\\PICA" FILES ${PICA_SOURCE_FILES})
source_group("Source Files\\Core\\Audio" FILES ${AUDIO_SOURCE_FILES})
source_group("Source Files\\Core\\Software Renderer" FILES ${RENDERER_SW_SOURCE_FILES})
source_group("Source Files\\Third Party" FILES ${THIRD_PARTY_SOURCE_FILES})

Expand Down Expand Up @@ -397,7 +405,7 @@ endif()
source_group("Header Files\\Core" FILES ${HEADER_FILES})
set(ALL_SOURCES ${SOURCE_FILES} ${FRONTEND_SOURCE_FILES} ${FS_SOURCE_FILES} ${CRYPTO_SOURCE_FILES} ${KERNEL_SOURCE_FILES}
${LOADER_SOURCE_FILES} ${SERVICE_SOURCE_FILES} ${APPLET_SOURCE_FILES} ${RENDERER_SW_SOURCE_FILES} ${PICA_SOURCE_FILES} ${THIRD_PARTY_SOURCE_FILES}
${HEADER_FILES} ${FRONTEND_HEADER_FILES})
${AUDIO_SOURCE_FILES} ${HEADER_FILES} ${FRONTEND_HEADER_FILES})

if(ENABLE_OPENGL)
# Add the OpenGL source files to ALL_SOURCES
Expand Down Expand Up @@ -429,7 +437,7 @@ if(ENABLE_LTO OR ENABLE_USER_BUILD)
set_target_properties(Alber PROPERTIES INTERPROCEDURAL_OPTIMIZATION TRUE)
endif()

target_link_libraries(Alber PRIVATE dynarmic cryptopp glad resources_console_fonts)
target_link_libraries(Alber PRIVATE dynarmic cryptopp glad resources_console_fonts teakra)

if(NOT ANDROID)
target_link_libraries(Alber PRIVATE SDL2-static)
Expand Down
66 changes: 66 additions & 0 deletions include/audio/dsp_core.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
#pragma once
#include <array>
#include <functional>
#include <memory>
#include <optional>
#include <string>
#include <vector>

#include "helpers.hpp"
#include "logger.hpp"
#include "scheduler.hpp"
#include "ring_buffer.hpp"

// The DSP core must have access to the DSP service to be able to trigger interrupts properly
class DSPService;
class Memory;

namespace Audio {
// There are 160 stereo samples in 1 audio frame, so 320 samples total
static constexpr u64 samplesInFrame = 160;
// 1 frame = 4096 DSP cycles = 8192 ARM11 cycles
static constexpr u64 cyclesPerFrame = samplesInFrame * 8192;
// For LLE DSP cores, we run the DSP for N cycles at a time, every N*2 arm11 cycles since the ARM11 runs twice as fast
static constexpr u64 lleSlice = 16384;

class DSPCore {
using Samples = Common::RingBuffer<s16, 1024>;

protected:
Memory& mem;
Scheduler& scheduler;
DSPService& dspService;

Samples sampleBuffer;
bool audioEnabled = false;

MAKE_LOG_FUNCTION(log, dspLogger)

public:
enum class Type { Null, Teakra };
DSPCore(Memory& mem, Scheduler& scheduler, DSPService& dspService)
: mem(mem), scheduler(scheduler), dspService(dspService) {}
virtual ~DSPCore() {}

virtual void reset() = 0;
virtual void runAudioFrame() = 0;
virtual u8* getDspMemory() = 0;

virtual u16 recvData(u32 regId) = 0;
virtual bool recvDataIsReady(u32 regId) = 0;
virtual void setSemaphore(u16 value) = 0;
virtual void writeProcessPipe(u32 channel, u32 size, u32 buffer) = 0;
virtual std::vector<u8> readPipe(u32 channel, u32 peer, u32 size, u32 buffer) = 0;
virtual void loadComponent(std::vector<u8>& data, u32 programMask, u32 dataMask) = 0;
virtual void unloadComponent() = 0;
virtual void setSemaphoreMask(u16 value) = 0;

static Audio::DSPCore::Type typeFromString(std::string inString);
static const char* typeToString(Audio::DSPCore::Type type);

Samples& getSamples() { return sampleBuffer; }
virtual void setAudioEnabled(bool enable) { audioEnabled = enable; }
};

std::unique_ptr<DSPCore> makeDSPCore(DSPCore::Type type, Memory& mem, Scheduler& scheduler, DSPService& dspService);
} // namespace Audio
31 changes: 31 additions & 0 deletions include/audio/miniaudio_device.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
#pragma once
#include <atomic>
#include <string>
#include <vector>

#include "miniaudio.h"
#include "ring_buffer.hpp"

class MiniAudioDevice {
using Samples = Common::RingBuffer<ma_int16, 1024>;
static constexpr ma_uint32 sampleRate = 32768; // 3DS sample rate
static constexpr ma_uint32 channelCount = 2; // Audio output is stereo

ma_context context;
ma_device_config deviceConfig;
ma_device device;
ma_resampler resampler;
Samples* samples = nullptr;

bool initialized = false;
bool running = false;

std::vector<std::string> audioDevices;
public:
MiniAudioDevice();
// If safe is on, we create a null audio device
void init(Samples& samples, bool safe = false);

void start();
void stop();
};
46 changes: 46 additions & 0 deletions include/audio/null_core.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
#pragma once
#include <array>

#include "audio/dsp_core.hpp"
#include "memory.hpp"

namespace Audio {
class NullDSP : public DSPCore {
enum class DSPState : u32 {
Off,
On,
Slep,
};

// Number of DSP pipes
static constexpr size_t pipeCount = 8;
DSPState dspState;

std::array<std::vector<u8>, pipeCount> pipeData; // The data of each pipe
std::array<u8, Memory::DSP_RAM_SIZE> dspRam;

void resetAudioPipe();
bool loaded = false; // Have we loaded a component?

public:
NullDSP(Memory& mem, Scheduler& scheduler, DSPService& dspService) : DSPCore(mem, scheduler, dspService) {}
~NullDSP() override {}

void reset() override;
void runAudioFrame() override;

u8* getDspMemory() override { return dspRam.data(); }

u16 recvData(u32 regId) override;
bool recvDataIsReady(u32 regId) override { return true; } // Treat data as always ready
void writeProcessPipe(u32 channel, u32 size, u32 buffer) override;
std::vector<u8> readPipe(u32 channel, u32 peer, u32 size, u32 buffer) override;

// NOPs for null DSP core
void loadComponent(std::vector<u8>& data, u32 programMask, u32 dataMask) override;
void unloadComponent() override;
void setSemaphore(u16 value) override {}
void setSemaphoreMask(u16 value) override {}
};

} // namespace Audio
104 changes: 104 additions & 0 deletions include/audio/teakra_core.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
#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
5 changes: 5 additions & 0 deletions include/config.hpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#pragma once
#include <filesystem>

#include "audio/dsp_core.hpp"
#include "renderer.hpp"

// Remember to initialize every field here to its default value otherwise bad things will happen
Expand All @@ -15,11 +16,15 @@ struct EmulatorConfig {
bool shaderJitEnabled = shaderJitDefault;
bool discordRpcEnabled = false;
RendererType rendererType = RendererType::OpenGL;
Audio::DSPCore::Type dspType = Audio::DSPCore::Type::Null;

bool sdCardInserted = true;
bool sdWriteProtected = false;
bool usePortableBuild = false;

bool audioEnabled = false;
bool vsyncEnabled = true;

bool chargerPlugged = true;
// Default to 3% battery to make users suffer
int batteryPercentage = 3;
Expand Down
Loading
Loading