mirror of
https://github.com/wheremyfoodat/Panda3DS.git
synced 2025-04-20 04:29:13 +12:00
Implement audio output
This commit is contained in:
parent
093364f615
commit
21ced6fae7
11 changed files with 299 additions and 9 deletions
3
.gitmodules
vendored
3
.gitmodules
vendored
|
@ -58,3 +58,6 @@
|
||||||
[submodule "third_party/teakra"]
|
[submodule "third_party/teakra"]
|
||||||
path = third_party/teakra
|
path = third_party/teakra
|
||||||
url = https://github.com/wwylele/teakra
|
url = https://github.com/wwylele/teakra
|
||||||
|
[submodule "third_party/miniaudio"]
|
||||||
|
path = third_party/miniaudio
|
||||||
|
url = https://github.com/mackron/miniaudio
|
||||||
|
|
|
@ -54,6 +54,7 @@ include_directories(third_party/xxhash/include)
|
||||||
include_directories(third_party/httplib)
|
include_directories(third_party/httplib)
|
||||||
include_directories(third_party/stb)
|
include_directories(third_party/stb)
|
||||||
include_directories(third_party/opengl)
|
include_directories(third_party/opengl)
|
||||||
|
include_directories(third_party/miniaudio)
|
||||||
include_directories(third_party/mio/single_include)
|
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
|
add_compile_definitions(NOMINMAX) # Make windows.h not define min/max macros because third-party deps don't like it
|
||||||
|
@ -89,7 +90,6 @@ include_directories(third_party/toml11)
|
||||||
include_directories(third_party/glm)
|
include_directories(third_party/glm)
|
||||||
|
|
||||||
add_subdirectory(third_party/cmrc)
|
add_subdirectory(third_party/cmrc)
|
||||||
add_subdirectory(third_party/teakra EXCLUDE_FROM_ALL)
|
|
||||||
|
|
||||||
set(BOOST_ROOT "${CMAKE_SOURCE_DIR}/third_party/boost")
|
set(BOOST_ROOT "${CMAKE_SOURCE_DIR}/third_party/boost")
|
||||||
set(Boost_INCLUDE_DIR "${CMAKE_SOURCE_DIR}/third_party/boost")
|
set(Boost_INCLUDE_DIR "${CMAKE_SOURCE_DIR}/third_party/boost")
|
||||||
|
@ -150,12 +150,13 @@ if(HOST_X64 OR HOST_ARM64)
|
||||||
else()
|
else()
|
||||||
message(FATAL_ERROR "Currently unsupported CPU architecture")
|
message(FATAL_ERROR "Currently unsupported CPU architecture")
|
||||||
endif()
|
endif()
|
||||||
|
add_subdirectory(third_party/teakra EXCLUDE_FROM_ALL)
|
||||||
|
|
||||||
set(SOURCE_FILES src/emulator.cpp src/io_file.cpp src/config.cpp
|
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/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/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/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(CRYPTO_SOURCE_FILES src/core/crypto/aes_engine.cpp)
|
||||||
set(KERNEL_SOURCE_FILES src/core/kernel/kernel.cpp src/core/kernel/resource_limits.cpp
|
set(KERNEL_SOURCE_FILES src/core/kernel/kernel.cpp src/core/kernel/resource_limits.cpp
|
||||||
|
@ -192,7 +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
|
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
|
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)
|
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)
|
set(RENDERER_SW_SOURCE_FILES src/core/renderer_sw/renderer_sw.cpp)
|
||||||
|
|
||||||
# Frontend source files
|
# Frontend source files
|
||||||
|
@ -250,6 +253,7 @@ set(HEADER_FILES include/emulator.hpp include/helpers.hpp include/termcolor.hpp
|
||||||
include/fs/archive_system_save_data.hpp include/lua_manager.hpp include/memory_mapped_file.hpp include/hydra_icon.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/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/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(
|
cmrc_add_resource_library(
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
#include "helpers.hpp"
|
#include "helpers.hpp"
|
||||||
#include "logger.hpp"
|
#include "logger.hpp"
|
||||||
#include "scheduler.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
|
// The DSP core must have access to the DSP service to be able to trigger interrupts properly
|
||||||
class DSPService;
|
class DSPService;
|
||||||
|
@ -23,16 +24,21 @@ namespace Audio {
|
||||||
static constexpr u64 lleSlice = 16384;
|
static constexpr u64 lleSlice = 16384;
|
||||||
|
|
||||||
class DSPCore {
|
class DSPCore {
|
||||||
|
using Samples = Common::RingBuffer<s16, 1024>;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
Memory& mem;
|
Memory& mem;
|
||||||
Scheduler& scheduler;
|
Scheduler& scheduler;
|
||||||
DSPService& dspService;
|
DSPService& dspService;
|
||||||
|
|
||||||
|
Samples sampleBuffer;
|
||||||
|
|
||||||
MAKE_LOG_FUNCTION(log, dspLogger)
|
MAKE_LOG_FUNCTION(log, dspLogger)
|
||||||
|
|
||||||
public:
|
public:
|
||||||
enum class Type { Null, Teakra };
|
enum class Type { Null, Teakra };
|
||||||
DSPCore(Memory& mem, Scheduler& scheduler, DSPService& dspService) : mem(mem), scheduler(scheduler), dspService(dspService) {}
|
DSPCore(Memory& mem, Scheduler& scheduler, DSPService& dspService)
|
||||||
|
: mem(mem), scheduler(scheduler), dspService(dspService) {}
|
||||||
|
|
||||||
virtual void reset() = 0;
|
virtual void reset() = 0;
|
||||||
virtual void runAudioFrame() = 0;
|
virtual void runAudioFrame() = 0;
|
||||||
|
@ -49,6 +55,7 @@ namespace Audio {
|
||||||
|
|
||||||
static Audio::DSPCore::Type typeFromString(std::string inString);
|
static Audio::DSPCore::Type typeFromString(std::string inString);
|
||||||
static const char* typeToString(Audio::DSPCore::Type type);
|
static const char* typeToString(Audio::DSPCore::Type type);
|
||||||
|
Samples& getSamples() { return sampleBuffer; }
|
||||||
};
|
};
|
||||||
|
|
||||||
std::unique_ptr<DSPCore> makeDSPCore(DSPCore::Type type, Memory& mem, Scheduler& scheduler, DSPService& dspService);
|
std::unique_ptr<DSPCore> makeDSPCore(DSPCore::Type type, Memory& mem, Scheduler& scheduler, DSPService& dspService);
|
||||||
|
|
28
include/audio/miniaudio_device.hpp
Normal file
28
include/audio/miniaudio_device.hpp
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
#pragma once
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "miniaudio.h"
|
||||||
|
#include "ring_buffer.hpp"
|
||||||
|
|
||||||
|
class MiniAudioDevice {
|
||||||
|
using Samples = Common::RingBuffer<ma_int16, 1024>;
|
||||||
|
// static constexpr ma_uint32 sampleRateIn = 32768; // 3DS sample rate
|
||||||
|
// static constexpr ma_uint32 sampleRateOut = 44100; // Output sample rate
|
||||||
|
|
||||||
|
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();
|
||||||
|
};
|
|
@ -8,6 +8,7 @@
|
||||||
|
|
||||||
#include "PICA/gpu.hpp"
|
#include "PICA/gpu.hpp"
|
||||||
#include "audio/dsp_core.hpp"
|
#include "audio/dsp_core.hpp"
|
||||||
|
#include "audio/miniaudio_device.hpp"
|
||||||
#include "cheats.hpp"
|
#include "cheats.hpp"
|
||||||
#include "config.hpp"
|
#include "config.hpp"
|
||||||
#include "cpu.hpp"
|
#include "cpu.hpp"
|
||||||
|
@ -47,6 +48,7 @@ class Emulator {
|
||||||
Scheduler scheduler;
|
Scheduler scheduler;
|
||||||
|
|
||||||
Crypto::AESEngine aesEngine;
|
Crypto::AESEngine aesEngine;
|
||||||
|
MiniAudioDevice audioDevice;
|
||||||
Cheats cheats;
|
Cheats cheats;
|
||||||
|
|
||||||
// Variables to keep track of whether the user is controlling the 3DS analog stick with their keyboard
|
// Variables to keep track of whether the user is controlling the 3DS analog stick with their keyboard
|
||||||
|
|
117
include/ring_buffer.hpp
Normal file
117
include/ring_buffer.hpp
Normal file
|
@ -0,0 +1,117 @@
|
||||||
|
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <array>
|
||||||
|
#include <atomic>
|
||||||
|
#include <cstddef>
|
||||||
|
#include <cstring>
|
||||||
|
#include <new>
|
||||||
|
#include <span>
|
||||||
|
#include <type_traits>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace Common {
|
||||||
|
|
||||||
|
/// SPSC ring buffer
|
||||||
|
/// @tparam T Element type
|
||||||
|
/// @tparam capacity Number of slots in ring buffer
|
||||||
|
template <typename T, std::size_t capacity>
|
||||||
|
class RingBuffer {
|
||||||
|
/// A "slot" is made of a single `T`.
|
||||||
|
static constexpr std::size_t slot_size = sizeof(T);
|
||||||
|
// T must be safely memcpy-able and have a trivial default constructor.
|
||||||
|
static_assert(std::is_trivial_v<T>);
|
||||||
|
// Ensure capacity is sensible.
|
||||||
|
static_assert(capacity < std::numeric_limits<std::size_t>::max() / 2);
|
||||||
|
static_assert((capacity & (capacity - 1)) == 0, "capacity must be a power of two");
|
||||||
|
// Ensure lock-free.
|
||||||
|
static_assert(std::atomic_size_t::is_always_lock_free);
|
||||||
|
|
||||||
|
public:
|
||||||
|
/// Pushes slots into the ring buffer
|
||||||
|
/// @param new_slots Pointer to the slots to push
|
||||||
|
/// @param slot_count Number of slots to push
|
||||||
|
/// @returns The number of slots actually pushed
|
||||||
|
std::size_t push(const void* new_slots, std::size_t slot_count) {
|
||||||
|
const std::size_t write_index = m_write_index.load();
|
||||||
|
const std::size_t slots_free = capacity + m_read_index.load() - write_index;
|
||||||
|
const std::size_t push_count = std::min(slot_count, slots_free);
|
||||||
|
|
||||||
|
const std::size_t pos = write_index % capacity;
|
||||||
|
const std::size_t first_copy = std::min(capacity - pos, push_count);
|
||||||
|
const std::size_t second_copy = push_count - first_copy;
|
||||||
|
|
||||||
|
const char* in = static_cast<const char*>(new_slots);
|
||||||
|
std::memcpy(m_data.data() + pos, in, first_copy * slot_size);
|
||||||
|
in += first_copy * slot_size;
|
||||||
|
std::memcpy(m_data.data(), in, second_copy * slot_size);
|
||||||
|
|
||||||
|
m_write_index.store(write_index + push_count);
|
||||||
|
|
||||||
|
return push_count;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::size_t push(std::span<const T> input) {
|
||||||
|
return push(input.data(), input.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Pops slots from the ring buffer
|
||||||
|
/// @param output Where to store the popped slots
|
||||||
|
/// @param max_slots Maximum number of slots to pop
|
||||||
|
/// @returns The number of slots actually popped
|
||||||
|
std::size_t pop(void* output, std::size_t max_slots = ~std::size_t(0)) {
|
||||||
|
const std::size_t read_index = m_read_index.load();
|
||||||
|
const std::size_t slots_filled = m_write_index.load() - read_index;
|
||||||
|
const std::size_t pop_count = std::min(slots_filled, max_slots);
|
||||||
|
|
||||||
|
const std::size_t pos = read_index % capacity;
|
||||||
|
const std::size_t first_copy = std::min(capacity - pos, pop_count);
|
||||||
|
const std::size_t second_copy = pop_count - first_copy;
|
||||||
|
|
||||||
|
char* out = static_cast<char*>(output);
|
||||||
|
std::memcpy(out, m_data.data() + pos, first_copy * slot_size);
|
||||||
|
out += first_copy * slot_size;
|
||||||
|
std::memcpy(out, m_data.data(), second_copy * slot_size);
|
||||||
|
|
||||||
|
m_read_index.store(read_index + pop_count);
|
||||||
|
|
||||||
|
return pop_count;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<T> pop(std::size_t max_slots = ~std::size_t(0)) {
|
||||||
|
std::vector<T> out(std::min(max_slots, capacity));
|
||||||
|
const std::size_t count = Pop(out.data(), out.size());
|
||||||
|
out.resize(count);
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @returns Number of slots used
|
||||||
|
[[nodiscard]] std::size_t size() const {
|
||||||
|
return m_write_index.load() - m_read_index.load();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @returns Maximum size of ring buffer
|
||||||
|
[[nodiscard]] constexpr std::size_t Capacity() const {
|
||||||
|
return capacity;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
// It is important to align the below variables for performance reasons:
|
||||||
|
// Having them on the same cache-line would result in false-sharing between them.
|
||||||
|
// TODO: Remove this ifdef whenever clang and GCC support
|
||||||
|
// std::hardware_destructive_interference_size.
|
||||||
|
#ifdef __cpp_lib_hardware_interference_size
|
||||||
|
alignas(std::hardware_destructive_interference_size) std::atomic_size_t m_read_index{0};
|
||||||
|
alignas(std::hardware_destructive_interference_size) std::atomic_size_t m_write_index{0};
|
||||||
|
#else
|
||||||
|
alignas(128) std::atomic_size_t m_read_index{0};
|
||||||
|
alignas(128) std::atomic_size_t m_write_index{0};
|
||||||
|
#endif
|
||||||
|
|
||||||
|
std::array<T, capacity> m_data;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Common
|
119
src/core/audio/miniaudio_device.cpp
Normal file
119
src/core/audio/miniaudio_device.cpp
Normal file
|
@ -0,0 +1,119 @@
|
||||||
|
#include "audio/miniaudio_device.hpp"
|
||||||
|
|
||||||
|
#include "helpers.hpp"
|
||||||
|
|
||||||
|
MiniAudioDevice::MiniAudioDevice() : initialized(false), running(false), samples(nullptr) {}
|
||||||
|
|
||||||
|
void MiniAudioDevice::init(Samples& samples, bool safe) {
|
||||||
|
this->samples = &samples;
|
||||||
|
|
||||||
|
// Probe for device and available backends and initialize audio
|
||||||
|
ma_backend backends[ma_backend_null + 1];
|
||||||
|
unsigned count = 0;
|
||||||
|
|
||||||
|
if (safe) {
|
||||||
|
backends[0] = ma_backend_null;
|
||||||
|
count = 1;
|
||||||
|
} else {
|
||||||
|
bool found = false;
|
||||||
|
for (uint i = 0; i <= ma_backend_null; i++) {
|
||||||
|
ma_backend backend = ma_backend(i);
|
||||||
|
if (!ma_is_backend_enabled(backend)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
backends[count++] = backend;
|
||||||
|
printf("Found audio backend: %s\n", ma_get_backend_name(backend));
|
||||||
|
|
||||||
|
// TODO: Make backend selectable here
|
||||||
|
found = true;
|
||||||
|
//count = 1;
|
||||||
|
//backends[0] = backend;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!found) {
|
||||||
|
initialized = false;
|
||||||
|
|
||||||
|
Helpers::warn("No valid audio backend found\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ma_context_init(backends, count, nullptr, &context) != MA_SUCCESS) {
|
||||||
|
initialized = false;
|
||||||
|
|
||||||
|
Helpers::warn("Unable to initialize audio context");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
audioDevices.clear();
|
||||||
|
|
||||||
|
struct UserContext {
|
||||||
|
MiniAudioDevice* miniAudio;
|
||||||
|
ma_device_config& config;
|
||||||
|
bool found = false;
|
||||||
|
};
|
||||||
|
UserContext userContext = {.miniAudio = this, .config = deviceConfig};
|
||||||
|
|
||||||
|
ma_context_enumerate_devices(
|
||||||
|
&context,
|
||||||
|
[](ma_context* pContext, ma_device_type deviceType, const ma_device_info* pInfo, void* pUserData) -> ma_bool32 {
|
||||||
|
if (deviceType != ma_device_type_playback) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
UserContext* userContext = reinterpret_cast<UserContext*>(pUserData);
|
||||||
|
userContext->miniAudio->audioDevices.push_back(pInfo->name);
|
||||||
|
|
||||||
|
// TODO: Check if this is the device we want here
|
||||||
|
userContext->config.playback.pDeviceID = &pInfo->id;
|
||||||
|
userContext->found = true;
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
&userContext
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!userContext.found) {
|
||||||
|
Helpers::warn("MiniAudio: Device not found");
|
||||||
|
}
|
||||||
|
|
||||||
|
deviceConfig = ma_device_config_init(ma_device_type_playback);
|
||||||
|
// The 3DS outputs s16 stereo audio @ 32768 Hz
|
||||||
|
deviceConfig.playback.format = ma_format_s16;
|
||||||
|
deviceConfig.playback.channels = 2;
|
||||||
|
deviceConfig.sampleRate = 32768;
|
||||||
|
//deviceConfig.periodSizeInFrames = 64;
|
||||||
|
//deviceConfig.periods = 2;
|
||||||
|
deviceConfig.pUserData = this;
|
||||||
|
deviceConfig.aaudio.usage = ma_aaudio_usage_game;
|
||||||
|
deviceConfig.wasapi.noAutoConvertSRC = true;
|
||||||
|
|
||||||
|
deviceConfig.dataCallback = [](ma_device* device, void* out, const void* input, ma_uint32 frameCount) {
|
||||||
|
auto self = reinterpret_cast<MiniAudioDevice*>(device->pUserData);
|
||||||
|
s16* output = reinterpret_cast<ma_int16*>(out);
|
||||||
|
|
||||||
|
self->samples->pop(output, frameCount);
|
||||||
|
};
|
||||||
|
|
||||||
|
if (ma_device_init(&context, &deviceConfig, &device) != MA_SUCCESS) {
|
||||||
|
Helpers::warn("Unable to initialize audio device");
|
||||||
|
initialized = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
initialized = true;
|
||||||
|
start();
|
||||||
|
}
|
||||||
|
|
||||||
|
void MiniAudioDevice::start() {
|
||||||
|
if (!initialized) {
|
||||||
|
Helpers::warn("MiniAudio device not initialize, won't start");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ma_device_start(&device) == MA_SUCCESS) {
|
||||||
|
running = true;
|
||||||
|
} else {
|
||||||
|
running = false;
|
||||||
|
Helpers::warn("Failed to start audio device");
|
||||||
|
}
|
||||||
|
}
|
|
@ -6,6 +6,10 @@
|
||||||
#include "services/dsp.hpp"
|
#include "services/dsp.hpp"
|
||||||
|
|
||||||
using namespace Audio;
|
using namespace Audio;
|
||||||
|
static constexpr u32 sampleRate = 32768;
|
||||||
|
static constexpr u32 duration = 30;
|
||||||
|
static s16 samples[sampleRate * duration * 2];
|
||||||
|
static uint sampleIndex = 0;
|
||||||
|
|
||||||
struct Dsp1 {
|
struct Dsp1 {
|
||||||
// All sizes are in bytes unless otherwise specified
|
// All sizes are in bytes unless otherwise specified
|
||||||
|
@ -51,10 +55,7 @@ TeakraDSP::TeakraDSP(Memory& mem, Scheduler& scheduler, DSPService& dspService)
|
||||||
ahbm.write32 = [&](u32 addr, u32 value) { *(u32*)&mem.getFCRAM()[addr - PhysicalAddrs::FCRAM] = value; };
|
ahbm.write32 = [&](u32 addr, u32 value) { *(u32*)&mem.getFCRAM()[addr - PhysicalAddrs::FCRAM] = value; };
|
||||||
|
|
||||||
teakra.SetAHBMCallback(ahbm);
|
teakra.SetAHBMCallback(ahbm);
|
||||||
teakra.SetAudioCallback([=](std::array<s16, 2> sample) {
|
teakra.SetAudioCallback([=](std::array<s16, 2> sample) { sampleBuffer.push(sample.data(), 2); });
|
||||||
//printf("%d %d\n", sample[0], sample[1]);
|
|
||||||
// NOP for now
|
|
||||||
});
|
|
||||||
|
|
||||||
// Set up event handlers. These handlers forward a hardware interrupt to the DSP service, which is responsible
|
// Set up event handlers. These handlers forward a hardware interrupt to the DSP service, which is responsible
|
||||||
// For triggering the appropriate DSP kernel events
|
// For triggering the appropriate DSP kernel events
|
||||||
|
|
|
@ -25,10 +25,11 @@ Emulator::Emulator()
|
||||||
#endif
|
#endif
|
||||||
{
|
{
|
||||||
DSPService& dspService = kernel.getServiceManager().getDSP();
|
DSPService& dspService = kernel.getServiceManager().getDSP();
|
||||||
|
|
||||||
dsp = Audio::makeDSPCore(config.dspType, memory, scheduler, dspService);
|
dsp = Audio::makeDSPCore(config.dspType, memory, scheduler, dspService);
|
||||||
dspService.setDSPCore(dsp.get());
|
dspService.setDSPCore(dsp.get());
|
||||||
|
|
||||||
|
audioDevice.init(dsp->getSamples());
|
||||||
|
|
||||||
#ifdef PANDA3DS_ENABLE_DISCORD_RPC
|
#ifdef PANDA3DS_ENABLE_DISCORD_RPC
|
||||||
if (config.discordRpcEnabled) {
|
if (config.discordRpcEnabled) {
|
||||||
discordRpc.init();
|
discordRpc.init();
|
||||||
|
|
7
src/miniaudio.cpp
Normal file
7
src/miniaudio.cpp
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
// We do not need the ability to be able to encode or decode audio files for the time being
|
||||||
|
// So we disable said functionality to make the executable smaller
|
||||||
|
#define MA_NO_DECODING
|
||||||
|
#define MA_NO_ENCODING
|
||||||
|
#define MINIAUDIO_IMPLEMENTATION
|
||||||
|
|
||||||
|
#include "miniaudio.h"
|
1
third_party/miniaudio
vendored
Submodule
1
third_party/miniaudio
vendored
Submodule
|
@ -0,0 +1 @@
|
||||||
|
Subproject commit 4a5b74bef029b3592c54b6048650ee5f972c1a48
|
Loading…
Add table
Add a link
Reference in a new issue