2
1
Fork 0
mirror of https://github.com/yuzu-emu/yuzu.git synced 2024-07-04 23:31:19 +01:00

audio_core: Interpolate

This commit is contained in:
MerryMage 2018-08-12 19:32:39 +01:00
parent 56300f2928
commit 4b44b8b4fb
5 changed files with 121 additions and 0 deletions

View file

@ -1,6 +1,8 @@
add_library(audio_core STATIC add_library(audio_core STATIC
algorithm/filter.cpp algorithm/filter.cpp
algorithm/filter.h algorithm/filter.h
algorithm/interpolate.cpp
algorithm/interpolate.h
audio_out.cpp audio_out.cpp
audio_out.h audio_out.h
audio_renderer.cpp audio_renderer.cpp

View file

@ -0,0 +1,71 @@
// Copyright 2018 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#define _USE_MATH_DEFINES
#include <algorithm>
#include <cmath>
#include <vector>
#include "audio_core/algorithm/interpolate.h"
#include "common/common_types.h"
#include "common/logging/log.h"
namespace AudioCore {
/// The Lanczos kernel
static double Lanczos(size_t a, double x) {
if (x == 0.0)
return 1.0;
const double px = M_PI * x;
return a * std::sin(px) * std::sin(px / a) / (px * px);
}
std::vector<s16> Interpolate(InterpolationState& state, std::vector<s16> input, double ratio) {
if (input.size() < 2)
return {};
if (ratio <= 0) {
LOG_CRITICAL(Audio, "Nonsensical interpolation ratio {}", ratio);
ratio = 1.0;
}
if (ratio != state.current_ratio) {
const double cutoff_frequency = std::min(0.5 / ratio, 0.5 * ratio);
state.nyquist = CascadingFilter::LowPass(std::clamp(cutoff_frequency, 0.0, 0.4), 3);
state.current_ratio = ratio;
}
state.nyquist.Process(input);
constexpr size_t taps = InterpolationState::lanczos_taps;
const size_t num_frames = input.size() / 2;
std::vector<s16> output;
output.reserve(static_cast<size_t>(input.size() / ratio + 4));
double& pos = state.position;
auto& h = state.history;
for (size_t i = 0; i < num_frames; ++i) {
std::rotate(h.begin(), h.end() - 1, h.end());
h[0][0] = input[i * 2 + 0];
h[0][1] = input[i * 2 + 1];
while (pos <= 1.0) {
double l = 0.0;
double r = 0.0;
for (size_t j = 0; j < h.size(); j++) {
l += Lanczos(taps, pos + j - taps + 1) * h[j][0];
r += Lanczos(taps, pos + j - taps + 1) * h[j][1];
}
output.emplace_back(static_cast<s16>(std::clamp(l, -32768.0, 32767.0)));
output.emplace_back(static_cast<s16>(std::clamp(r, -32768.0, 32767.0)));
pos += ratio;
}
pos -= 1.0;
}
return output;
}
} // namespace AudioCore

View file

@ -0,0 +1,43 @@
// Copyright 2018 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <array>
#include <vector>
#include "audio_core/algorithm/filter.h"
#include "common/common_types.h"
namespace AudioCore {
struct InterpolationState {
static constexpr size_t lanczos_taps = 4;
static constexpr size_t history_size = lanczos_taps * 2 - 1;
double current_ratio = 0.0;
CascadingFilter nyquist;
std::array<std::array<s16, 2>, history_size> history = {};
double position = 0;
};
/// Interpolates input signal to produce output signal.
/// @param input The signal to interpolate.
/// @param ratio Interpolation ratio.
/// ratio > 1.0 results in fewer output samples.
/// ratio < 1.0 results in more output samples.
/// @returns Output signal.
std::vector<s16> Interpolate(InterpolationState& state, std::vector<s16> input, double ratio);
/// Interpolates input signal to produce output signal.
/// @param input The signal to interpolate.
/// @param input_rate The sample rate of input.
/// @param output_rate The desired sample rate of the output.
/// @returns Output signal.
inline std::vector<s16> Interpolate(InterpolationState& state, std::vector<s16> input,
u32 input_rate, u32 output_rate) {
const double ratio = static_cast<double>(input_rate) / static_cast<double>(output_rate);
return Interpolate(state, std::move(input), ratio);
}
} // namespace AudioCore

View file

@ -2,6 +2,7 @@
// Licensed under GPLv2 or any later version // Licensed under GPLv2 or any later version
// Refer to the license.txt file included. // Refer to the license.txt file included.
#include "audio_core/algorithm/interpolate.h"
#include "audio_core/audio_renderer.h" #include "audio_core/audio_renderer.h"
#include "common/assert.h" #include "common/assert.h"
#include "common/logging/log.h" #include "common/logging/log.h"
@ -199,6 +200,8 @@ void AudioRenderer::VoiceState::RefreshBuffer() {
break; break;
} }
samples = Interpolate(interp_state, std::move(samples), Info().sample_rate, STREAM_SAMPLE_RATE);
is_refresh_pending = false; is_refresh_pending = false;
} }

View file

@ -8,6 +8,7 @@
#include <memory> #include <memory>
#include <vector> #include <vector>
#include "audio_core/algorithm/interpolate.h"
#include "audio_core/audio_out.h" #include "audio_core/audio_out.h"
#include "audio_core/codec.h" #include "audio_core/codec.h"
#include "audio_core/stream.h" #include "audio_core/stream.h"
@ -194,6 +195,7 @@ private:
size_t wave_index{}; size_t wave_index{};
size_t offset{}; size_t offset{};
Codec::ADPCMState adpcm_state{}; Codec::ADPCMState adpcm_state{};
InterpolationState interp_state{};
std::vector<s16> samples; std::vector<s16> samples;
VoiceOutStatus out_status{}; VoiceOutStatus out_status{};
VoiceInfo info{}; VoiceInfo info{};