mirror of
https://github.com/wheremyfoodat/Panda3DS.git
synced 2025-04-08 23:25:40 +12:00
HLE DSP: Implement per-voice mixing stage
This commit is contained in:
parent
884597615b
commit
07cee43a2b
2 changed files with 45 additions and 3 deletions
|
@ -47,14 +47,17 @@ namespace Audio {
|
||||||
|
|
||||||
// Buffer of decoded PCM16 samples. TODO: Are there better alternatives to use over deque?
|
// Buffer of decoded PCM16 samples. TODO: Are there better alternatives to use over deque?
|
||||||
using SampleBuffer = std::deque<std::array<s16, 2>>;
|
using SampleBuffer = std::deque<std::array<s16, 2>>;
|
||||||
|
|
||||||
using BufferQueue = std::priority_queue<Buffer>;
|
using BufferQueue = std::priority_queue<Buffer>;
|
||||||
|
|
||||||
BufferQueue buffers;
|
BufferQueue buffers;
|
||||||
|
|
||||||
SampleFormat sampleFormat = SampleFormat::ADPCM;
|
SampleFormat sampleFormat = SampleFormat::ADPCM;
|
||||||
SourceType sourceType = SourceType::Stereo;
|
SourceType sourceType = SourceType::Stereo;
|
||||||
|
|
||||||
std::array<float, 3> gain0, gain1, gain2;
|
// There's one gain configuration for each of the 3 intermediate mixing stages
|
||||||
|
// And each gain configuration is composed of 4 gain values, one for each sample in a quad-channel sample
|
||||||
|
std::array<std::array<float, 4>, 3> gains;
|
||||||
|
|
||||||
u32 samplePosition; // Sample number into the current audio buffer
|
u32 samplePosition; // Sample number into the current audio buffer
|
||||||
float rateMultiplier;
|
float rateMultiplier;
|
||||||
u16 syncCount;
|
u16 syncCount;
|
||||||
|
@ -112,6 +115,10 @@ namespace Audio {
|
||||||
template <typename T>
|
template <typename T>
|
||||||
using QuadFrame = Frame<T, 4>;
|
using QuadFrame = Frame<T, 4>;
|
||||||
|
|
||||||
|
// Internally the DSP uses four channels when mixing.
|
||||||
|
// Neatly, QuadFrame<s32> means that every sample is a uint32x4 value, which is particularly nice for SIMD mixing
|
||||||
|
using IntermediateMix = QuadFrame<s32>;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
using ChannelFormat = HLE::DspConfiguration::OutputFormat;
|
using ChannelFormat = HLE::DspConfiguration::OutputFormat;
|
||||||
// The audio from each DSP voice is converted to quadraphonic and then fed into 3 intermediate mixing stages
|
// The audio from each DSP voice is converted to quadraphonic and then fed into 3 intermediate mixing stages
|
||||||
|
@ -151,6 +158,7 @@ namespace Audio {
|
||||||
|
|
||||||
using Source = Audio::DSPSource;
|
using Source = Audio::DSPSource;
|
||||||
using SampleBuffer = Source::SampleBuffer;
|
using SampleBuffer = Source::SampleBuffer;
|
||||||
|
using IntermediateMix = DSPMixer::IntermediateMix;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
enum class DSPState : u32 {
|
enum class DSPState : u32 {
|
||||||
|
|
|
@ -228,6 +228,7 @@ namespace Audio {
|
||||||
// The DSP checks the DSP configuration dirty bits on every frame, applies them, and clears them
|
// The DSP checks the DSP configuration dirty bits on every frame, applies them, and clears them
|
||||||
read.dspConfiguration.dirtyRaw = 0;
|
read.dspConfiguration.dirtyRaw = 0;
|
||||||
read.dspConfiguration.dirtyRaw2 = 0;
|
read.dspConfiguration.dirtyRaw2 = 0;
|
||||||
|
std::array<IntermediateMix, 3> mixes{};
|
||||||
|
|
||||||
for (int i = 0; i < sourceCount; i++) {
|
for (int i = 0; i < sourceCount; i++) {
|
||||||
// Update source configuration from the read region of shared memory
|
// Update source configuration from the read region of shared memory
|
||||||
|
@ -250,6 +251,24 @@ namespace Audio {
|
||||||
status.samplePosition = source.samplePosition;
|
status.samplePosition = source.samplePosition;
|
||||||
|
|
||||||
source.isBufferIDDirty = false;
|
source.isBufferIDDirty = false;
|
||||||
|
|
||||||
|
// If the source is still enabled, mix its output into the intermediate mix buffers
|
||||||
|
if (source.enabled) {
|
||||||
|
for (int mix = 0; mix < mixes.size(); mix++) {
|
||||||
|
IntermediateMix& intermediateMix = mixes[mix];
|
||||||
|
const std::array<float, 4>& gains = source.gains[mix];
|
||||||
|
|
||||||
|
// TODO: SIMD implementations
|
||||||
|
for (usize sampleIndex = 0; sampleIndex < Audio::samplesInFrame; sampleIndex++) {
|
||||||
|
// Mono samples are in the format: (l, r)
|
||||||
|
// When converting to quad, gain0 and gain2 are applied to the left sample, gain1 and gain3 to the right one
|
||||||
|
intermediateMix[sampleIndex][0] += s32(source.currentSamples[sampleIndex][0] * gains[0]);
|
||||||
|
intermediateMix[sampleIndex][1] += s32(source.currentSamples[sampleIndex][1] * gains[1]);
|
||||||
|
intermediateMix[sampleIndex][2] += s32(source.currentSamples[sampleIndex][0] * gains[2]);
|
||||||
|
intermediateMix[sampleIndex][3] += s32(source.currentSamples[sampleIndex][1] * gains[3]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
performMix(read, write);
|
performMix(read, write);
|
||||||
|
@ -374,6 +393,21 @@ namespace Audio {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#define CONFIG_GAIN(index) \
|
||||||
|
if (config.gain##index##Dirty) { \
|
||||||
|
auto& dest = source.gains[index]; \
|
||||||
|
auto& source = config.gain[index]; \
|
||||||
|
\
|
||||||
|
dest[0] = float(source[0]); \
|
||||||
|
dest[1] = float(source[1]); \
|
||||||
|
dest[2] = float(source[2]); \
|
||||||
|
dest[3] = float(source[3]); \
|
||||||
|
}
|
||||||
|
CONFIG_GAIN(0);
|
||||||
|
CONFIG_GAIN(1);
|
||||||
|
CONFIG_GAIN(2);
|
||||||
|
#undef CONFIG_GAIN
|
||||||
|
|
||||||
config.dirtyRaw = 0;
|
config.dirtyRaw = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue