mirror of
https://github.com/wheremyfoodat/Panda3DS.git
synced 2025-04-07 22:55:40 +12:00
Merge pull request #642 from wheremyfoodat/more-dsp
Better audio playback code
This commit is contained in:
commit
842634e64e
8 changed files with 175 additions and 157 deletions
|
@ -1,7 +1,8 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
#include "PICA/float_types.hpp"
|
|
||||||
#include <array>
|
#include <array>
|
||||||
|
|
||||||
|
#include "PICA/float_types.hpp"
|
||||||
|
|
||||||
namespace PICA {
|
namespace PICA {
|
||||||
// A representation of the output vertex as it comes out of the vertex shader, with padding and all
|
// A representation of the output vertex as it comes out of the vertex shader, with padding and all
|
||||||
struct Vertex {
|
struct Vertex {
|
||||||
|
|
|
@ -8,8 +8,8 @@
|
||||||
|
|
||||||
#include "helpers.hpp"
|
#include "helpers.hpp"
|
||||||
#include "logger.hpp"
|
#include "logger.hpp"
|
||||||
#include "scheduler.hpp"
|
|
||||||
#include "ring_buffer.hpp"
|
#include "ring_buffer.hpp"
|
||||||
|
#include "scheduler.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;
|
||||||
|
@ -24,7 +24,8 @@ namespace Audio {
|
||||||
static constexpr u64 lleSlice = 16384;
|
static constexpr u64 lleSlice = 16384;
|
||||||
|
|
||||||
class DSPCore {
|
class DSPCore {
|
||||||
using Samples = Common::RingBuffer<s16, 1024>;
|
// 0x2000 stereo (= 2 channel) samples
|
||||||
|
using Samples = Common::RingBuffer<s16, 0x2000 * 2>;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
Memory& mem;
|
Memory& mem;
|
||||||
|
@ -38,8 +39,7 @@ namespace Audio {
|
||||||
|
|
||||||
public:
|
public:
|
||||||
enum class Type { Null, Teakra, HLE };
|
enum class Type { Null, Teakra, HLE };
|
||||||
DSPCore(Memory& mem, Scheduler& scheduler, DSPService& dspService)
|
DSPCore(Memory& mem, Scheduler& scheduler, DSPService& dspService) : mem(mem), scheduler(scheduler), dspService(dspService) {}
|
||||||
: mem(mem), scheduler(scheduler), dspService(dspService) {}
|
|
||||||
virtual ~DSPCore() {}
|
virtual ~DSPCore() {}
|
||||||
|
|
||||||
virtual void reset() = 0;
|
virtual void reset() = 0;
|
||||||
|
|
|
@ -3,11 +3,12 @@
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
#include "helpers.hpp"
|
||||||
#include "miniaudio.h"
|
#include "miniaudio.h"
|
||||||
#include "ring_buffer.hpp"
|
#include "ring_buffer.hpp"
|
||||||
|
|
||||||
class MiniAudioDevice {
|
class MiniAudioDevice {
|
||||||
using Samples = Common::RingBuffer<ma_int16, 1024>;
|
using Samples = Common::RingBuffer<ma_int16, 0x2000 * 2>;
|
||||||
static constexpr ma_uint32 sampleRate = 32768; // 3DS sample rate
|
static constexpr ma_uint32 sampleRate = 32768; // 3DS sample rate
|
||||||
static constexpr ma_uint32 channelCount = 2; // Audio output is stereo
|
static constexpr ma_uint32 channelCount = 2; // Audio output is stereo
|
||||||
|
|
||||||
|
@ -20,6 +21,8 @@ class MiniAudioDevice {
|
||||||
bool initialized = false;
|
bool initialized = false;
|
||||||
bool running = false;
|
bool running = false;
|
||||||
|
|
||||||
|
// Store the last stereo sample we output. We play this when underruning to avoid pops.
|
||||||
|
std::array<s16, 2> lastStereoSample;
|
||||||
std::vector<std::string> audioDevices;
|
std::vector<std::string> audioDevices;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
|
@ -9,7 +9,7 @@ enum class Regions : u32 {
|
||||||
Australia = 3,
|
Australia = 3,
|
||||||
China = 4,
|
China = 4,
|
||||||
Korea = 5,
|
Korea = 5,
|
||||||
Taiwan = 6
|
Taiwan = 6,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Used for the language field in the NAND user data
|
// Used for the language field in the NAND user data
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
#include "audio/miniaudio_device.hpp"
|
#include "audio/miniaudio_device.hpp"
|
||||||
|
|
||||||
|
#include <cstring>
|
||||||
|
|
||||||
#include "helpers.hpp"
|
#include "helpers.hpp"
|
||||||
|
|
||||||
MiniAudioDevice::MiniAudioDevice() : initialized(false), running(false), samples(nullptr) {}
|
MiniAudioDevice::MiniAudioDevice() : initialized(false), running(false), samples(nullptr) {}
|
||||||
|
@ -87,20 +89,34 @@ void MiniAudioDevice::init(Samples& samples, bool safe) {
|
||||||
deviceConfig.aaudio.usage = ma_aaudio_usage_game;
|
deviceConfig.aaudio.usage = ma_aaudio_usage_game;
|
||||||
deviceConfig.wasapi.noAutoConvertSRC = true;
|
deviceConfig.wasapi.noAutoConvertSRC = true;
|
||||||
|
|
||||||
|
lastStereoSample = {0, 0};
|
||||||
|
|
||||||
deviceConfig.dataCallback = [](ma_device* device, void* out, const void* input, ma_uint32 frameCount) {
|
deviceConfig.dataCallback = [](ma_device* device, void* out, const void* input, ma_uint32 frameCount) {
|
||||||
auto self = reinterpret_cast<MiniAudioDevice*>(device->pUserData);
|
auto self = reinterpret_cast<MiniAudioDevice*>(device->pUserData);
|
||||||
s16* output = reinterpret_cast<ma_int16*>(out);
|
|
||||||
const usize maxSamples = std::min(self->samples->Capacity(), usize(frameCount * channelCount));
|
|
||||||
|
|
||||||
// Wait until there's enough samples to pop
|
|
||||||
while (self->samples->size() < maxSamples) {
|
|
||||||
// If audio output is disabled from the emulator thread, make sure that this callback will return and not hang
|
|
||||||
if (!self->running) {
|
if (!self->running) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
s16* output = reinterpret_cast<ma_int16*>(out);
|
||||||
|
usize samplesWritten = 0;
|
||||||
|
samplesWritten += self->samples->pop(output, frameCount * channelCount);
|
||||||
|
|
||||||
|
// Get the last sample for underrun handling
|
||||||
|
if (samplesWritten != 0) {
|
||||||
|
std::memcpy(&self->lastStereoSample[0], &output[(samplesWritten - 1) * 2], sizeof(lastStereoSample));
|
||||||
}
|
}
|
||||||
|
|
||||||
self->samples->pop(output, maxSamples);
|
// If underruning, copy the last output sample
|
||||||
|
{
|
||||||
|
s16* pointer = &output[samplesWritten * 2];
|
||||||
|
s16 l = self->lastStereoSample[0];
|
||||||
|
s16 r = self->lastStereoSample[1];
|
||||||
|
|
||||||
|
for (usize i = samplesWritten; i < frameCount; i++) {
|
||||||
|
*pointer++ = l;
|
||||||
|
*pointer++ = r;
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
if (ma_device_init(&context, &deviceConfig, &device) != MA_SUCCESS) {
|
if (ma_device_init(&context, &deviceConfig, &device) != MA_SUCCESS) {
|
||||||
|
|
|
@ -120,9 +120,7 @@ namespace Audio {
|
||||||
dspService.triggerPipeEvent(DSPPipeType::Audio);
|
dspService.triggerPipeEvent(DSPPipeType::Audio);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case StateChange::Shutdown:
|
case StateChange::Shutdown: dspState = DSPState::Off; break;
|
||||||
dspState = DSPState::Off;
|
|
||||||
break;
|
|
||||||
|
|
||||||
default: Helpers::panic("Unimplemented DSP audio pipe state change %d", state);
|
default: Helpers::panic("Unimplemented DSP audio pipe state change %d", state);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue