mirror of
https://github.com/wheremyfoodat/Panda3DS.git
synced 2025-06-03 12:27:21 +12:00
Add audio interpolation helpers
This commit is contained in:
parent
9be353a9b4
commit
69e8e1c2c4
6 changed files with 188 additions and 43 deletions
58
include/audio/audio_interpolation.hpp
Normal file
58
include/audio/audio_interpolation.hpp
Normal file
|
@ -0,0 +1,58 @@
|
|||
// Copyright 2016 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <deque>
|
||||
|
||||
#include "audio/hle_mixer.hpp"
|
||||
#include "helpers.hpp"
|
||||
|
||||
namespace Audio::Interpolation {
|
||||
// A variable length buffer of signed PCM16 stereo samples.
|
||||
using StereoBuffer16 = std::deque<std::array<s16, 2>>;
|
||||
using StereoFrame16 = Audio::DSPMixer::StereoFrame<s16>;
|
||||
|
||||
struct State {
|
||||
// Two historical samples.
|
||||
std::array<s16, 2> xn1 = {}; //< x[n-1]
|
||||
std::array<s16, 2> xn2 = {}; //< x[n-2]
|
||||
// Current fractional position.
|
||||
u64 fposition = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* No interpolation. This is equivalent to a zero-order hold. There is a two-sample predelay.
|
||||
* @param state Interpolation state.
|
||||
* @param input Input buffer.
|
||||
* @param rate Stretch factor. Must be a positive non-zero value.
|
||||
* rate > 1.0 performs decimation and rate < 1.0 performs upsampling.
|
||||
* @param output The resampled audio buffer.
|
||||
* @param outputi The index of output to start writing to.
|
||||
*/
|
||||
void none(State& state, StereoBuffer16& input, float rate, StereoFrame16& output, usize& outputi);
|
||||
|
||||
/**
|
||||
* Linear interpolation. This is equivalent to a first-order hold. There is a two-sample predelay.
|
||||
* @param state Interpolation state.
|
||||
* @param input Input buffer.
|
||||
* @param rate Stretch factor. Must be a positive non-zero value.
|
||||
* rate > 1.0 performs decimation and rate < 1.0 performs upsampling.
|
||||
* @param output The resampled audio buffer.
|
||||
* @param outputi The index of output to start writing to.
|
||||
*/
|
||||
void linear(State& state, StereoBuffer16& input, float rate, StereoFrame16& output, usize& outputi);
|
||||
|
||||
/**
|
||||
* Polyphase interpolation. This is currently stubbed to just perform linear interpolation
|
||||
* @param state Interpolation state.
|
||||
* @param input Input buffer.
|
||||
* @param rate Stretch factor. Must be a positive non-zero value.
|
||||
* rate > 1.0 performs decimation and rate < 1.0 performs upsampling.
|
||||
* @param output The resampled audio buffer.
|
||||
* @param outputi The index of output to start writing to.
|
||||
*/
|
||||
void polyphase(State& state, StereoBuffer16& input, float rate, StereoFrame16& output, usize& outputi);
|
||||
} // namespace Audio::Interpolation
|
|
@ -8,54 +8,13 @@
|
|||
|
||||
#include "audio/aac.hpp"
|
||||
#include "audio/aac_decoder.hpp"
|
||||
#include "audio/audio_interpolation.hpp"
|
||||
#include "audio/dsp_core.hpp"
|
||||
#include "audio/dsp_shared_mem.hpp"
|
||||
#include "audio/hle_mixer.hpp"
|
||||
#include "memory.hpp"
|
||||
|
||||
namespace Audio {
|
||||
using SampleFormat = HLE::SourceConfiguration::Configuration::Format;
|
||||
using SourceType = HLE::SourceConfiguration::Configuration::MonoOrStereo;
|
||||
|
||||
class DSPMixer {
|
||||
public:
|
||||
template <typename T, usize channelCount = 1>
|
||||
using Sample = std::array<T, channelCount>;
|
||||
|
||||
template <typename T, usize channelCount>
|
||||
using Frame = std::array<Sample<T, channelCount>, 160>;
|
||||
|
||||
template <typename T>
|
||||
using MonoFrame = Frame<T, 1>;
|
||||
|
||||
template <typename T>
|
||||
using StereoFrame = Frame<T, 2>;
|
||||
|
||||
template <typename T>
|
||||
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:
|
||||
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);
|
||||
}
|
||||
};
|
||||
|
||||
struct DSPSource {
|
||||
// Audio buffer information
|
||||
// https://www.3dbrew.org/wiki/DSP_Memory_Region
|
||||
|
@ -89,6 +48,7 @@ namespace Audio {
|
|||
using SampleBuffer = std::deque<std::array<s16, 2>>;
|
||||
using BufferQueue = std::priority_queue<Buffer>;
|
||||
using InterpolationMode = HLE::SourceConfiguration::Configuration::InterpolationMode;
|
||||
using InterpolationState = Audio::Interpolation::State;
|
||||
|
||||
DSPMixer::StereoFrame<s16> currentFrame;
|
||||
BufferQueue buffers;
|
||||
|
@ -96,6 +56,7 @@ namespace Audio {
|
|||
SampleFormat sampleFormat = SampleFormat::ADPCM;
|
||||
SourceType sourceType = SourceType::Stereo;
|
||||
InterpolationMode interpolationMode = InterpolationMode::Linear;
|
||||
InterpolationState interpolationState;
|
||||
|
||||
// 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
|
||||
|
|
50
include/audio/hle_mixer.hpp
Normal file
50
include/audio/hle_mixer.hpp
Normal file
|
@ -0,0 +1,50 @@
|
|||
#pragma once
|
||||
#include <array>
|
||||
|
||||
#include "audio/dsp_shared_mem.hpp"
|
||||
#include "helpers.hpp"
|
||||
|
||||
namespace Audio {
|
||||
using SampleFormat = HLE::SourceConfiguration::Configuration::Format;
|
||||
using SourceType = HLE::SourceConfiguration::Configuration::MonoOrStereo;
|
||||
|
||||
class DSPMixer {
|
||||
public:
|
||||
template <typename T, usize channelCount = 1>
|
||||
using Sample = std::array<T, channelCount>;
|
||||
|
||||
template <typename T, usize channelCount>
|
||||
using Frame = std::array<Sample<T, channelCount>, 160>;
|
||||
|
||||
template <typename T>
|
||||
using MonoFrame = Frame<T, 1>;
|
||||
|
||||
template <typename T>
|
||||
using StereoFrame = Frame<T, 2>;
|
||||
|
||||
template <typename T>
|
||||
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:
|
||||
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);
|
||||
}
|
||||
};
|
||||
} // namespace Audio
|
Loading…
Add table
Add a link
Reference in a new issue