yuzu-emu
/
yuzu-android
Archived
1
0
Fork 0

audio_core: remove time stretcher

Also drop the SoundTouch dependency
This commit is contained in:
Andrea Pappacoda 2022-04-01 19:54:58 +02:00
parent 550844e5e8
commit faf6a9876c
No known key found for this signature in database
GPG Key ID: A8A128A8AB1CEE49
9 changed files with 3 additions and 144 deletions

3
.gitmodules vendored
View File

@ -7,9 +7,6 @@
[submodule "dynarmic"] [submodule "dynarmic"]
path = externals/dynarmic path = externals/dynarmic
url = https://github.com/MerryMage/dynarmic.git url = https://github.com/MerryMage/dynarmic.git
[submodule "soundtouch"]
path = externals/soundtouch
url = https://github.com/citra-emu/ext-soundtouch.git
[submodule "libressl"] [submodule "libressl"]
path = externals/libressl path = externals/libressl
url = https://github.com/citra-emu/ext-libressl-portable.git url = https://github.com/citra-emu/ext-libressl-portable.git

View File

@ -68,9 +68,6 @@ if (YUZU_USE_EXTERNAL_SDL2)
add_library(SDL2 ALIAS SDL2-static) add_library(SDL2 ALIAS SDL2-static)
endif() endif()
# SoundTouch
add_subdirectory(soundtouch)
# Cubeb # Cubeb
if(ENABLE_CUBEB) if(ENABLE_CUBEB)
set(BUILD_TESTS OFF CACHE BOOL "") set(BUILD_TESTS OFF CACHE BOOL "")

@ -1 +0,0 @@
Subproject commit 060181eaf273180d3a7e87349895bd0cb6ccbf4a

View File

@ -36,8 +36,6 @@ add_library(audio_core STATIC
splitter_context.h splitter_context.h
stream.cpp stream.cpp
stream.h stream.h
time_stretch.cpp
time_stretch.h
voice_context.cpp voice_context.cpp
voice_context.h voice_context.h
@ -63,7 +61,6 @@ if (NOT MSVC)
endif() endif()
target_link_libraries(audio_core PUBLIC common core) target_link_libraries(audio_core PUBLIC common core)
target_link_libraries(audio_core PRIVATE SoundTouch)
if(ENABLE_CUBEB) if(ENABLE_CUBEB)
target_link_libraries(audio_core PRIVATE cubeb) target_link_libraries(audio_core PRIVATE cubeb)

View File

@ -7,7 +7,6 @@
#include <cstring> #include <cstring>
#include "audio_core/cubeb_sink.h" #include "audio_core/cubeb_sink.h"
#include "audio_core/stream.h" #include "audio_core/stream.h"
#include "audio_core/time_stretch.h"
#include "common/assert.h" #include "common/assert.h"
#include "common/logging/log.h" #include "common/logging/log.h"
#include "common/ring_buffer.h" #include "common/ring_buffer.h"
@ -23,8 +22,7 @@ class CubebSinkStream final : public SinkStream {
public: public:
CubebSinkStream(cubeb* ctx_, u32 sample_rate, u32 num_channels_, cubeb_devid output_device, CubebSinkStream(cubeb* ctx_, u32 sample_rate, u32 num_channels_, cubeb_devid output_device,
const std::string& name) const std::string& name)
: ctx{ctx_}, num_channels{std::min(num_channels_, 6u)}, time_stretch{sample_rate, : ctx{ctx_}, num_channels{std::min(num_channels_, 6u)} {
num_channels} {
cubeb_stream_params params{}; cubeb_stream_params params{};
params.rate = sample_rate; params.rate = sample_rate;
@ -131,7 +129,6 @@ private:
Common::RingBuffer<s16, 0x10000> queue; Common::RingBuffer<s16, 0x10000> queue;
std::array<s16, 2> last_frame{}; std::array<s16, 2> last_frame{};
std::atomic<bool> should_flush{}; std::atomic<bool> should_flush{};
TimeStretcher time_stretch;
static long DataCallback(cubeb_stream* stream, void* user_data, const void* input_buffer, static long DataCallback(cubeb_stream* stream, void* user_data, const void* input_buffer,
void* output_buffer, long num_frames); void* output_buffer, long num_frames);
@ -205,25 +202,7 @@ long CubebSinkStream::DataCallback([[maybe_unused]] cubeb_stream* stream, void*
const std::size_t num_channels = impl->GetNumChannels(); const std::size_t num_channels = impl->GetNumChannels();
const std::size_t samples_to_write = num_channels * num_frames; const std::size_t samples_to_write = num_channels * num_frames;
std::size_t samples_written; const std::size_t samples_written = impl->queue.Pop(buffer, samples_to_write);
/*
if (Settings::values.enable_audio_stretching.GetValue()) {
const std::vector<s16> in{impl->queue.Pop()};
const std::size_t num_in{in.size() / num_channels};
s16* const out{reinterpret_cast<s16*>(buffer)};
const std::size_t out_frames =
impl->time_stretch.Process(in.data(), num_in, out, num_frames);
samples_written = out_frames * num_channels;
if (impl->should_flush) {
impl->time_stretch.Flush();
impl->should_flush = false;
}
} else {
samples_written = impl->queue.Pop(buffer, samples_to_write);
}*/
samples_written = impl->queue.Pop(buffer, samples_to_write);
if (samples_written >= num_channels) { if (samples_written >= num_channels) {
std::memcpy(&impl->last_frame[0], buffer + (samples_written - num_channels) * sizeof(s16), std::memcpy(&impl->last_frame[0], buffer + (samples_written - num_channels) * sizeof(s16),

View File

@ -7,7 +7,6 @@
#include <cstring> #include <cstring>
#include "audio_core/sdl2_sink.h" #include "audio_core/sdl2_sink.h"
#include "audio_core/stream.h" #include "audio_core/stream.h"
#include "audio_core/time_stretch.h"
#include "common/assert.h" #include "common/assert.h"
#include "common/logging/log.h" #include "common/logging/log.h"
//#include "common/settings.h" //#include "common/settings.h"
@ -27,7 +26,7 @@ namespace AudioCore {
class SDLSinkStream final : public SinkStream { class SDLSinkStream final : public SinkStream {
public: public:
SDLSinkStream(u32 sample_rate, u32 num_channels_, const std::string& output_device) SDLSinkStream(u32 sample_rate, u32 num_channels_, const std::string& output_device)
: num_channels{std::min(num_channels_, 6u)}, time_stretch{sample_rate, num_channels} { : num_channels{std::min(num_channels_, 6u)} {
SDL_AudioSpec spec; SDL_AudioSpec spec;
spec.freq = sample_rate; spec.freq = sample_rate;
@ -116,7 +115,6 @@ private:
SDL_AudioDeviceID dev = 0; SDL_AudioDeviceID dev = 0;
u32 num_channels{}; u32 num_channels{};
std::atomic<bool> should_flush{}; std::atomic<bool> should_flush{};
TimeStretcher time_stretch;
}; };
SDLSink::SDLSink(std::string_view target_device_name) { SDLSink::SDLSink(std::string_view target_device_name) {

View File

@ -1,68 +0,0 @@
// Copyright 2018 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <algorithm>
#include <cmath>
#include <cstddef>
#include "audio_core/time_stretch.h"
#include "common/logging/log.h"
namespace AudioCore {
TimeStretcher::TimeStretcher(u32 sample_rate, u32 channel_count) : m_sample_rate{sample_rate} {
m_sound_touch.setChannels(channel_count);
m_sound_touch.setSampleRate(sample_rate);
m_sound_touch.setPitch(1.0);
m_sound_touch.setTempo(1.0);
}
void TimeStretcher::Clear() {
m_sound_touch.clear();
}
void TimeStretcher::Flush() {
m_sound_touch.flush();
}
std::size_t TimeStretcher::Process(const s16* in, std::size_t num_in, s16* out,
std::size_t num_out) {
const double time_delta = static_cast<double>(num_out) / m_sample_rate; // seconds
// We were given actual_samples number of samples, and num_samples were requested from us.
double current_ratio = static_cast<double>(num_in) / static_cast<double>(num_out);
const double max_latency = 0.25; // seconds
const double max_backlog = m_sample_rate * max_latency;
const double backlog_fullness = m_sound_touch.numSamples() / max_backlog;
if (backlog_fullness > 4.0) {
// Too many samples in backlog: Don't push anymore on
num_in = 0;
}
// We ideally want the backlog to be about 50% full.
// This gives some headroom both ways to prevent underflow and overflow.
// We tweak current_ratio to encourage this.
constexpr double tweak_time_scale = 0.05; // seconds
const double tweak_correction = (backlog_fullness - 0.5) * (time_delta / tweak_time_scale);
current_ratio *= std::pow(1.0 + 2.0 * tweak_correction, tweak_correction < 0 ? 3.0 : 1.0);
// This low-pass filter smoothes out variance in the calculated stretch ratio.
// The time-scale determines how responsive this filter is.
constexpr double lpf_time_scale = 0.712; // seconds
const double lpf_gain = 1.0 - std::exp(-time_delta / lpf_time_scale);
m_stretch_ratio += lpf_gain * (current_ratio - m_stretch_ratio);
// Place a lower limit of 5% speed. When a game boots up, there will be
// many silence samples. These do not need to be timestretched.
m_stretch_ratio = std::max(m_stretch_ratio, 0.05);
m_sound_touch.setTempo(m_stretch_ratio);
LOG_TRACE(Audio, "{:5}/{:5} ratio:{:0.6f} backlog:{:0.6f}", num_in, num_out, m_stretch_ratio,
backlog_fullness);
m_sound_touch.putSamples(in, static_cast<u32>(num_in));
return m_sound_touch.receiveSamples(out, static_cast<u32>(num_out));
}
} // namespace AudioCore

View File

@ -1,34 +0,0 @@
// Copyright 2018 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <cstddef>
#include <SoundTouch.h>
#include "common/common_types.h"
namespace AudioCore {
class TimeStretcher {
public:
TimeStretcher(u32 sample_rate, u32 channel_count);
/// @param in Input sample buffer
/// @param num_in Number of input frames in `in`
/// @param out Output sample buffer
/// @param num_out Desired number of output frames in `out`
/// @returns Actual number of frames written to `out`
std::size_t Process(const s16* in, std::size_t num_in, s16* out, std::size_t num_out);
void Clear();
void Flush();
private:
u32 m_sample_rate;
soundtouch::SoundTouch m_sound_touch;
double m_stretch_ratio = 1.0;
};
} // namespace AudioCore

View File

@ -342,12 +342,6 @@ fps_cap =
# null: No audio output # null: No audio output
output_engine = output_engine =
# Whether or not to enable the audio-stretching post-processing effect.
# This effect adjusts audio speed to match emulation speed and helps prevent audio stutter,
# at the cost of increasing audio latency.
# 0: No, 1 (default): Yes
enable_audio_stretching =
# Which audio device to use. # Which audio device to use.
# auto (default): Auto-select # auto (default): Auto-select
output_device = output_device =