diff options
-rw-r--r-- | src/audio_core/CMakeLists.txt | 3 | ||||
-rw-r--r-- | src/audio_core/hle/common.h | 35 | ||||
-rw-r--r-- | src/audio_core/hle/dsp.h | 17 | ||||
-rw-r--r-- | src/audio_core/hle/filter.cpp | 115 | ||||
-rw-r--r-- | src/audio_core/hle/filter.h | 112 |
5 files changed, 275 insertions, 7 deletions
diff --git a/src/audio_core/CMakeLists.txt b/src/audio_core/CMakeLists.txt index c4bad8cb0..869da5e83 100644 --- a/src/audio_core/CMakeLists.txt +++ b/src/audio_core/CMakeLists.txt @@ -2,13 +2,16 @@ set(SRCS audio_core.cpp codec.cpp hle/dsp.cpp + hle/filter.cpp hle/pipe.cpp ) set(HEADERS audio_core.h codec.h + hle/common.h hle/dsp.h + hle/filter.h hle/pipe.h sink.h ) diff --git a/src/audio_core/hle/common.h b/src/audio_core/hle/common.h new file mode 100644 index 000000000..37d441eb2 --- /dev/null +++ b/src/audio_core/hle/common.h @@ -0,0 +1,35 @@ +// Copyright 2016 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include <algorithm> +#include <array> + +#include "audio_core/audio_core.h" + +#include "common/common_types.h" + +namespace DSP { +namespace HLE { + +/// The final output to the speakers is stereo. Preprocessing output in Source is also stereo. +using StereoFrame16 = std::array<std::array<s16, 2>, AudioCore::samples_per_frame>; + +/// The DSP is quadraphonic internally. +using QuadFrame32 = std::array<std::array<s32, 4>, AudioCore::samples_per_frame>; + +/** + * This performs the filter operation defined by FilterT::ProcessSample on the frame in-place. + * FilterT::ProcessSample is called sequentially on the samples. + */ +template<typename FrameT, typename FilterT> +void FilterFrame(FrameT& frame, FilterT& filter) { + std::transform(frame.begin(), frame.end(), frame.begin(), [&filter](const typename FrameT::value_type& sample) { + return filter.ProcessSample(sample); + }); +} + +} // namespace HLE +} // namespace DSP diff --git a/src/audio_core/hle/dsp.h b/src/audio_core/hle/dsp.h index 376436c29..c15ef0b7a 100644 --- a/src/audio_core/hle/dsp.h +++ b/src/audio_core/hle/dsp.h @@ -126,8 +126,11 @@ struct SourceConfiguration { union { u32_le dirty_raw; + BitField<0, 1, u32_le> format_dirty; + BitField<1, 1, u32_le> mono_or_stereo_dirty; BitField<2, 1, u32_le> adpcm_coefficients_dirty; BitField<3, 1, u32_le> partial_embedded_buffer_dirty; ///< Tends to be set when a looped buffer is queued. + BitField<4, 1, u32_le> partial_reset_flag; BitField<16, 1, u32_le> enable_dirty; BitField<17, 1, u32_le> interpolation_dirty; @@ -143,8 +146,7 @@ struct SourceConfiguration { BitField<27, 1, u32_le> gain_2_dirty; BitField<28, 1, u32_le> sync_dirty; BitField<29, 1, u32_le> reset_flag; - - BitField<31, 1, u32_le> embedded_buffer_dirty; + BitField<30, 1, u32_le> embedded_buffer_dirty; }; // Gain control @@ -175,7 +177,8 @@ struct SourceConfiguration { /** * This is the simplest normalized first-order digital recursive filter. * The transfer function of this filter is: - * H(z) = b0 / (1 + a1 z^-1) + * H(z) = b0 / (1 - a1 z^-1) + * Note the feedbackward coefficient is negated. * Values are signed fixed point with 15 fractional bits. */ struct SimpleFilter { @@ -192,11 +195,11 @@ struct SourceConfiguration { * Values are signed fixed point with 14 fractional bits. */ struct BiquadFilter { - s16_le b0; - s16_le b1; - s16_le b2; - s16_le a1; s16_le a2; + s16_le a1; + s16_le b2; + s16_le b1; + s16_le b0; }; union { diff --git a/src/audio_core/hle/filter.cpp b/src/audio_core/hle/filter.cpp new file mode 100644 index 000000000..2c65ef026 --- /dev/null +++ b/src/audio_core/hle/filter.cpp @@ -0,0 +1,115 @@ +// Copyright 2016 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include <array> +#include <cstddef> + +#include "audio_core/hle/common.h" +#include "audio_core/hle/dsp.h" +#include "audio_core/hle/filter.h" + +#include "common/common_types.h" +#include "common/math_util.h" + +namespace DSP { +namespace HLE { + +void SourceFilters::Reset() { + Enable(false, false); +} + +void SourceFilters::Enable(bool simple, bool biquad) { + simple_filter_enabled = simple; + biquad_filter_enabled = biquad; + + if (!simple) + simple_filter.Reset(); + if (!biquad) + biquad_filter.Reset(); +} + +void SourceFilters::Configure(SourceConfiguration::Configuration::SimpleFilter config) { + simple_filter.Configure(config); +} + +void SourceFilters::Configure(SourceConfiguration::Configuration::BiquadFilter config) { + biquad_filter.Configure(config); +} + +void SourceFilters::ProcessFrame(StereoFrame16& frame) { + if (!simple_filter_enabled && !biquad_filter_enabled) + return; + + if (simple_filter_enabled) { + FilterFrame(frame, simple_filter); + } + + if (biquad_filter_enabled) { + FilterFrame(frame, biquad_filter); + } +} + +// SimpleFilter + +void SourceFilters::SimpleFilter::Reset() { + y1.fill(0); + // Configure as passthrough. + a1 = 0; + b0 = 1 << 15; +} + +void SourceFilters::SimpleFilter::Configure(SourceConfiguration::Configuration::SimpleFilter config) { + a1 = config.a1; + b0 = config.b0; +} + +std::array<s16, 2> SourceFilters::SimpleFilter::ProcessSample(const std::array<s16, 2>& x0) { + std::array<s16, 2> y0; + for (size_t i = 0; i < 2; i++) { + const s32 tmp = (b0 * x0[i] + a1 * y1[i]) >> 15; + y0[i] = MathUtil::Clamp(tmp, -32768, 32767); + } + + y1 = y0; + + return y0; +} + +// BiquadFilter + +void SourceFilters::BiquadFilter::Reset() { + x1.fill(0); + x2.fill(0); + y1.fill(0); + y2.fill(0); + // Configure as passthrough. + a1 = a2 = b1 = b2 = 0; + b0 = 1 << 14; +} + +void SourceFilters::BiquadFilter::Configure(SourceConfiguration::Configuration::BiquadFilter config) { + a1 = config.a1; + a2 = config.a2; + b0 = config.b0; + b1 = config.b1; + b2 = config.b2; +} + +std::array<s16, 2> SourceFilters::BiquadFilter::ProcessSample(const std::array<s16, 2>& x0) { + std::array<s16, 2> y0; + for (size_t i = 0; i < 2; i++) { + const s32 tmp = (b0 * x0[i] + b1 * x1[i] + b2 * x2[i] + a1 * y1[i] + a2 * y2[i]) >> 14; + y0[i] = MathUtil::Clamp(tmp, -32768, 32767); + } + + x2 = x1; + x1 = x0; + y2 = y1; + y1 = y0; + + return y0; +} + +} // namespace HLE +} // namespace DSP diff --git a/src/audio_core/hle/filter.h b/src/audio_core/hle/filter.h new file mode 100644 index 000000000..75738f600 --- /dev/null +++ b/src/audio_core/hle/filter.h @@ -0,0 +1,112 @@ +// Copyright 2016 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include <array> + +#include "audio_core/hle/common.h" +#include "audio_core/hle/dsp.h" + +#include "common/common_types.h" + +namespace DSP { +namespace HLE { + +/// Preprocessing filters. There is an independent set of filters for each Source. +class SourceFilters final { + SourceFilters() { Reset(); } + + /// Reset internal state. + void Reset(); + + /** + * Enable/Disable filters + * See also: SourceConfiguration::Configuration::simple_filter_enabled, + * SourceConfiguration::Configuration::biquad_filter_enabled. + * @param simple If true, enables the simple filter. If false, disables it. + * @param simple If true, enables the biquad filter. If false, disables it. + */ + void Enable(bool simple, bool biquad); + + /** + * Configure simple filter. + * @param config Configuration from DSP shared memory. + */ + void Configure(SourceConfiguration::Configuration::SimpleFilter config); + + /** + * Configure biquad filter. + * @param config Configuration from DSP shared memory. + */ + void Configure(SourceConfiguration::Configuration::BiquadFilter config); + + /** + * Processes a frame in-place. + * @param frame Audio samples to process. Modified in-place. + */ + void ProcessFrame(StereoFrame16& frame); + +private: + bool simple_filter_enabled; + bool biquad_filter_enabled; + + struct SimpleFilter { + SimpleFilter() { Reset(); } + + /// Resets internal state. + void Reset(); + + /** + * Configures this filter with application settings. + * @param config Configuration from DSP shared memory. + */ + void Configure(SourceConfiguration::Configuration::SimpleFilter config); + + /** + * Processes a single stereo PCM16 sample. + * @param x0 Input sample + * @return Output sample + */ + std::array<s16, 2> ProcessSample(const std::array<s16, 2>& x0); + + private: + // Configuration + s32 a1, b0; + // Internal state + std::array<s16, 2> y1; + } simple_filter; + + struct BiquadFilter { + BiquadFilter() { Reset(); } + + /// Resets internal state. + void Reset(); + + /** + * Configures this filter with application settings. + * @param config Configuration from DSP shared memory. + */ + void Configure(SourceConfiguration::Configuration::BiquadFilter config); + + /** + * Processes a single stereo PCM16 sample. + * @param x0 Input sample + * @return Output sample + */ + std::array<s16, 2> ProcessSample(const std::array<s16, 2>& x0); + + private: + // Configuration + s32 a1, a2, b0, b1, b2; + // Internal state + std::array<s16, 2> x1; + std::array<s16, 2> x2; + std::array<s16, 2> y1; + std::array<s16, 2> y2; + } biquad_filter; +}; + +} // namespace HLE +} // namespace DSP |