Browse Source
Merge pull request #1572 from MerryMage/audio-filter
Merge pull request #1572 from MerryMage/audio-filter
DSP: Implement audio filters (simple, biquad)nce_cpp
5 changed files with 275 additions and 7 deletions
-
3src/audio_core/CMakeLists.txt
-
35src/audio_core/hle/common.h
-
17src/audio_core/hle/dsp.h
-
115src/audio_core/hle/filter.cpp
-
112src/audio_core/hle/filter.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 |
||||
@ -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
|
||||
@ -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 |
||||
Write
Preview
Loading…
Cancel
Save
Reference in new issue