mirror of
https://github.com/wheremyfoodat/Panda3DS.git
synced 2025-04-08 23:25:40 +12:00
Merge pull request #600 from wheremyfoodat/ir
HLE DSP: Initial mixer work
This commit is contained in:
commit
96f684e51c
3 changed files with 94 additions and 7 deletions
|
@ -324,8 +324,8 @@ namespace Audio::HLE {
|
||||||
BitField<15, 1, u32> outputBufferCountDirty;
|
BitField<15, 1, u32> outputBufferCountDirty;
|
||||||
BitField<16, 1, u32> masterVolumeDirty;
|
BitField<16, 1, u32> masterVolumeDirty;
|
||||||
|
|
||||||
BitField<24, 1, u32> auxReturnVolume0Dirty;
|
BitField<24, 1, u32> auxVolume0Dirty;
|
||||||
BitField<25, 1, u32> auxReturnVolume1Dirty;
|
BitField<25, 1, u32> auxVolume1Dirty;
|
||||||
BitField<26, 1, u32> outputFormatDirty;
|
BitField<26, 1, u32> outputFormatDirty;
|
||||||
BitField<27, 1, u32> clippingModeDirty;
|
BitField<27, 1, u32> clippingModeDirty;
|
||||||
BitField<28, 1, u32> headphonesConnectedDirty;
|
BitField<28, 1, u32> headphonesConnectedDirty;
|
||||||
|
@ -337,7 +337,7 @@ namespace Audio::HLE {
|
||||||
/// The DSP has three intermediate audio mixers. This controls the volume level (0.0-1.0) for
|
/// The DSP has three intermediate audio mixers. This controls the volume level (0.0-1.0) for
|
||||||
/// each at the final mixer.
|
/// each at the final mixer.
|
||||||
float_le masterVolume;
|
float_le masterVolume;
|
||||||
std::array<float_le, 2> auxReturnVolume;
|
std::array<float_le, 2> auxVolumes;
|
||||||
|
|
||||||
u16_le outputBufferCount;
|
u16_le outputBufferCount;
|
||||||
u16 pad1[2];
|
u16 pad1[2];
|
||||||
|
@ -422,7 +422,7 @@ namespace Audio::HLE {
|
||||||
|
|
||||||
struct DspStatus {
|
struct DspStatus {
|
||||||
u16_le unknown;
|
u16_le unknown;
|
||||||
u16_le dropped_frames;
|
u16_le droppedFrames;
|
||||||
u16 pad0[0xE];
|
u16 pad0[0xE];
|
||||||
};
|
};
|
||||||
ASSERT_DSP_STRUCT(DspStatus, 32);
|
ASSERT_DSP_STRUCT(DspStatus, 32);
|
||||||
|
|
|
@ -95,8 +95,7 @@ namespace Audio {
|
||||||
DSPSource() { reset(); }
|
DSPSource() { reset(); }
|
||||||
};
|
};
|
||||||
|
|
||||||
class HLE_DSP : public DSPCore {
|
class DSPMixer {
|
||||||
// The audio frame types are public in case we want to use them for unit tests
|
|
||||||
public:
|
public:
|
||||||
template <typename T, usize channelCount = 1>
|
template <typename T, usize channelCount = 1>
|
||||||
using Sample = std::array<T, channelCount>;
|
using Sample = std::array<T, channelCount>;
|
||||||
|
@ -113,6 +112,43 @@ namespace Audio {
|
||||||
template <typename T>
|
template <typename T>
|
||||||
using QuadFrame = Frame<T, 4>;
|
using QuadFrame = Frame<T, 4>;
|
||||||
|
|
||||||
|
private:
|
||||||
|
using ChannelFormat = HLE::DspConfiguration::OutputFormat;
|
||||||
|
// The audio from each DSP voice is converted to quadraphonic and then fed into 3 intermediate mixing stages
|
||||||
|
// Two of these intermediate mixers (second and third) are used for effects, including custom effects done on the CPU
|
||||||
|
static constexpr usize mixerStageCount = 3;
|
||||||
|
|
||||||
|
public:
|
||||||
|
ChannelFormat channelFormat = ChannelFormat::Stereo;
|
||||||
|
std::array<float, mixerStageCount> volumes;
|
||||||
|
std::array<bool, 2> enableAuxStages;
|
||||||
|
|
||||||
|
void reset() {
|
||||||
|
channelFormat = ChannelFormat::Stereo;
|
||||||
|
|
||||||
|
volumes.fill(0.0);
|
||||||
|
enableAuxStages.fill(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class HLE_DSP : public DSPCore {
|
||||||
|
// The audio frame types are public in case we want to use them for unit tests
|
||||||
|
public:
|
||||||
|
template <typename T, usize channelCount = 1>
|
||||||
|
using Sample = DSPMixer::Sample<T, channelCount>;
|
||||||
|
|
||||||
|
template <typename T, usize channelCount>
|
||||||
|
using Frame = DSPMixer::Frame<T, channelCount>;
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
using MonoFrame = DSPMixer::MonoFrame<T>;
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
using StereoFrame = DSPMixer::StereoFrame<T>;
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
using QuadFrame = DSPMixer::QuadFrame<T>;
|
||||||
|
|
||||||
using Source = Audio::DSPSource;
|
using Source = Audio::DSPSource;
|
||||||
using SampleBuffer = Source::SampleBuffer;
|
using SampleBuffer = Source::SampleBuffer;
|
||||||
|
|
||||||
|
@ -131,6 +167,7 @@ namespace Audio {
|
||||||
std::array<Source, Audio::HLE::sourceCount> sources; // DSP voices
|
std::array<Source, Audio::HLE::sourceCount> sources; // DSP voices
|
||||||
Audio::HLE::DspMemory dspRam;
|
Audio::HLE::DspMemory dspRam;
|
||||||
|
|
||||||
|
Audio::DSPMixer mixer;
|
||||||
std::unique_ptr<Audio::AAC::Decoder> aacDecoder;
|
std::unique_ptr<Audio::AAC::Decoder> aacDecoder;
|
||||||
|
|
||||||
void resetAudioPipe();
|
void resetAudioPipe();
|
||||||
|
@ -175,9 +212,12 @@ namespace Audio {
|
||||||
|
|
||||||
void handleAACRequest(const AAC::Message& request);
|
void handleAACRequest(const AAC::Message& request);
|
||||||
void updateSourceConfig(Source& source, HLE::SourceConfiguration::Configuration& config, s16_le* adpcmCoefficients);
|
void updateSourceConfig(Source& source, HLE::SourceConfiguration::Configuration& config, s16_le* adpcmCoefficients);
|
||||||
|
void updateMixerConfig(HLE::SharedMemory& sharedMem);
|
||||||
void generateFrame(StereoFrame<s16>& frame);
|
void generateFrame(StereoFrame<s16>& frame);
|
||||||
void generateFrame(DSPSource& source);
|
void generateFrame(DSPSource& source);
|
||||||
void outputFrame();
|
void outputFrame();
|
||||||
|
// Perform the final mix, mixing the quadraphonic samples from all voices into the output audio frame
|
||||||
|
void performMix(Audio::HLE::SharedMemory& readRegion, Audio::HLE::SharedMemory& writeRegion);
|
||||||
|
|
||||||
// Decode an entire buffer worth of audio
|
// Decode an entire buffer worth of audio
|
||||||
void decodeBuffer(DSPSource& source);
|
void decodeBuffer(DSPSource& source);
|
||||||
|
|
|
@ -76,6 +76,7 @@ namespace Audio {
|
||||||
source.reset();
|
source.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mixer.reset();
|
||||||
// Note: Reset audio pipe AFTER resetting all pipes, otherwise the new data will be yeeted
|
// Note: Reset audio pipe AFTER resetting all pipes, otherwise the new data will be yeeted
|
||||||
resetAudioPipe();
|
resetAudioPipe();
|
||||||
}
|
}
|
||||||
|
@ -250,6 +251,8 @@ namespace Audio {
|
||||||
|
|
||||||
source.isBufferIDDirty = false;
|
source.isBufferIDDirty = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
performMix(read, write);
|
||||||
}
|
}
|
||||||
|
|
||||||
void HLE_DSP::updateSourceConfig(Source& source, HLE::SourceConfiguration::Configuration& config, s16_le* adpcmCoefficients) {
|
void HLE_DSP::updateSourceConfig(Source& source, HLE::SourceConfiguration::Configuration& config, s16_le* adpcmCoefficients) {
|
||||||
|
@ -465,6 +468,50 @@ namespace Audio {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void HLE_DSP::performMix(Audio::HLE::SharedMemory& readRegion, Audio::HLE::SharedMemory& writeRegion) {
|
||||||
|
updateMixerConfig(readRegion);
|
||||||
|
// TODO: Do the actual audio mixing
|
||||||
|
|
||||||
|
auto& dspStatus = writeRegion.dspStatus;
|
||||||
|
// Stub the DSP status. It's unknown what the "unknown" field is but Citra sets it to 0, so we do too to be safe
|
||||||
|
dspStatus.droppedFrames = 0;
|
||||||
|
dspStatus.unknown = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void HLE_DSP::updateMixerConfig(Audio::HLE::SharedMemory& sharedMem) {
|
||||||
|
auto& config = sharedMem.dspConfiguration;
|
||||||
|
// No configs have been changed, so there's nothing to update
|
||||||
|
if (config.dirtyRaw == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (config.outputFormatDirty) {
|
||||||
|
mixer.channelFormat = config.outputFormat;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (config.masterVolumeDirty) {
|
||||||
|
mixer.volumes[0] = config.masterVolume;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (config.auxVolume0Dirty) {
|
||||||
|
mixer.volumes[1] = config.auxVolumes[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (config.auxVolume1Dirty) {
|
||||||
|
mixer.volumes[2] = config.auxVolumes[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (config.auxBusEnable0Dirty) {
|
||||||
|
mixer.enableAuxStages[0] = config.auxBusEnable[0] != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (config.auxBusEnable1Dirty) {
|
||||||
|
mixer.enableAuxStages[1] = config.auxBusEnable[1] != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
config.dirtyRaw = 0;
|
||||||
|
}
|
||||||
|
|
||||||
HLE_DSP::SampleBuffer HLE_DSP::decodePCM8(const u8* data, usize sampleCount, Source& source) {
|
HLE_DSP::SampleBuffer HLE_DSP::decodePCM8(const u8* data, usize sampleCount, Source& source) {
|
||||||
SampleBuffer decodedSamples(sampleCount);
|
SampleBuffer decodedSamples(sampleCount);
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue