Skip to content

Commit

Permalink
HLE DSP: Initial mixer work
Browse files Browse the repository at this point in the history
  • Loading branch information
wheremyfoodat committed Sep 28, 2024
1 parent a8041bc commit 3e72323
Show file tree
Hide file tree
Showing 3 changed files with 94 additions and 7 deletions.
8 changes: 4 additions & 4 deletions include/audio/dsp_shared_mem.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -324,8 +324,8 @@ namespace Audio::HLE {
BitField<15, 1, u32> outputBufferCountDirty;
BitField<16, 1, u32> masterVolumeDirty;

BitField<24, 1, u32> auxReturnVolume0Dirty;
BitField<25, 1, u32> auxReturnVolume1Dirty;
BitField<24, 1, u32> auxVolume0Dirty;
BitField<25, 1, u32> auxVolume1Dirty;
BitField<26, 1, u32> outputFormatDirty;
BitField<27, 1, u32> clippingModeDirty;
BitField<28, 1, u32> headphonesConnectedDirty;
Expand All @@ -337,7 +337,7 @@ namespace Audio::HLE {
/// The DSP has three intermediate audio mixers. This controls the volume level (0.0-1.0) for
/// each at the final mixer.
float_le masterVolume;
std::array<float_le, 2> auxReturnVolume;
std::array<float_le, 2> auxVolumes;

u16_le outputBufferCount;
u16 pad1[2];
Expand Down Expand Up @@ -422,7 +422,7 @@ namespace Audio::HLE {

struct DspStatus {
u16_le unknown;
u16_le dropped_frames;
u16_le droppedFrames;
u16 pad0[0xE];
};
ASSERT_DSP_STRUCT(DspStatus, 32);
Expand Down
46 changes: 43 additions & 3 deletions include/audio/hle_core.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -95,8 +95,7 @@ namespace Audio {
DSPSource() { reset(); }
};

class HLE_DSP : public DSPCore {
// The audio frame types are public in case we want to use them for unit tests
class DSPMixer {
public:
template <typename T, usize channelCount = 1>
using Sample = std::array<T, channelCount>;
Expand All @@ -113,6 +112,43 @@ namespace Audio {
template <typename T>
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 SampleBuffer = Source::SampleBuffer;

Expand All @@ -131,6 +167,7 @@ namespace Audio {
std::array<Source, Audio::HLE::sourceCount> sources; // DSP voices
Audio::HLE::DspMemory dspRam;

Audio::DSPMixer mixer;
std::unique_ptr<Audio::AAC::Decoder> aacDecoder;

void resetAudioPipe();
Expand Down Expand Up @@ -175,10 +212,13 @@ namespace Audio {

void handleAACRequest(const AAC::Message& request);
void updateSourceConfig(Source& source, HLE::SourceConfiguration::Configuration& config, s16_le* adpcmCoefficients);
void updateMixerConfig(HLE::SharedMemory& sharedMem);
void generateFrame(StereoFrame<s16>& frame);
void generateFrame(DSPSource& source);
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
void decodeBuffer(DSPSource& source);

Expand Down
47 changes: 47 additions & 0 deletions src/core/audio/hle_core.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ namespace Audio {
source.reset();
}

mixer.reset();
// Note: Reset audio pipe AFTER resetting all pipes, otherwise the new data will be yeeted
resetAudioPipe();
}
Expand Down Expand Up @@ -250,6 +251,8 @@ namespace Audio {

source.isBufferIDDirty = false;
}

performMix(read, write);
}

void HLE_DSP::updateSourceConfig(Source& source, HLE::SourceConfiguration::Configuration& config, s16_le* adpcmCoefficients) {
Expand Down Expand Up @@ -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) {
SampleBuffer decodedSamples(sampleCount);

Expand Down

0 comments on commit 3e72323

Please sign in to comment.