Commit 2e8ef6b8 authored by Libretro-Admin's avatar Libretro-Admin
Browse files

Remove SoundTouch/timestretching

parent b03d74ac
......@@ -4,9 +4,6 @@
[submodule "nihstro"]
path = externals/nihstro
url = https://github.com/neobrain/nihstro.git
[submodule "soundtouch"]
path = externals/soundtouch
url = https://github.com/citra-emu/ext-soundtouch.git
[submodule "catch"]
path = externals/catch
url = https://github.com/philsquared/Catch.git
......
......@@ -46,11 +46,6 @@ target_include_directories(nihstro-headers INTERFACE ./nihstro/include)
# Open Source Archives
add_subdirectory(open_source_archives)
# SoundTouch
add_subdirectory(soundtouch)
# The SoundTouch target doesn't export the necessary include paths as properties by default
target_include_directories(SoundTouch INTERFACE ./soundtouch/include)
# Xbyak
if (ARCHITECTURE_x86_64)
# Defined before "dynarmic" above
......
Subproject commit 060181eaf273180d3a7e87349895bd0cb6ccbf4a
......@@ -20,8 +20,6 @@ add_library(audio_core STATIC
sink.h
sink_details.cpp
sink_details.h
time_stretch.cpp
time_stretch.h
libretro_sink.cpp
libretro_sink.h
......@@ -30,6 +28,5 @@ add_library(audio_core STATIC
create_target_directory_groups(audio_core)
target_link_libraries(audio_core PUBLIC common core)
target_link_libraries(audio_core PRIVATE SoundTouch)
target_link_libraries(audio_core PRIVATE libretro)
......@@ -19,7 +19,6 @@ void DspInterface::SetSink(const std::string& sink_id, const std::string& audio_
sink = sink_details.factory(audio_device);
sink->SetCallback(
[this](s16* buffer, std::size_t num_frames) { OutputCallback(buffer, num_frames); });
time_stretcher.SetOutputSampleRate(sink->GetNativeSampleRate());
}
Sink& DspInterface::GetSink() {
......@@ -27,16 +26,6 @@ Sink& DspInterface::GetSink() {
return *sink.get();
}
void DspInterface::EnableStretching(bool enable) {
if (perform_time_stretching == enable)
return;
if (!enable) {
flushing_time_stretcher = true;
}
perform_time_stretching = enable;
}
void DspInterface::OutputFrame(StereoFrame16& frame) {
if (!sink)
return;
......@@ -47,19 +36,7 @@ void DspInterface::OutputFrame(StereoFrame16& frame) {
}
void DspInterface::OutputCallback(s16* buffer, std::size_t num_frames) {
std::size_t frames_written;
if (perform_time_stretching) {
const std::vector<s16> in{fifo.Pop()};
const std::size_t num_in{in.size() / 2};
frames_written = time_stretcher.Process(in.data(), num_in, buffer, num_frames);
} else if (flushing_time_stretcher) {
time_stretcher.Flush();
frames_written = time_stretcher.Process(nullptr, 0, buffer, num_frames);
frames_written += fifo.Pop(buffer, num_frames - frames_written);
flushing_time_stretcher = false;
} else {
frames_written = fifo.Pop(buffer, num_frames);
}
std::size_t frames_written = fifo.Pop(buffer, num_frames);
if (frames_written > 0) {
std::memcpy(&last_frame[0], buffer + 2 * (frames_written - 1), 2 * sizeof(s16));
......
......@@ -7,7 +7,6 @@
#include <memory>
#include <vector>
#include "audio_core/audio_types.h"
#include "audio_core/time_stretch.h"
#include "common/common_types.h"
#include "common/ring_buffer.h"
#include "core/memory.h"
......@@ -74,8 +73,6 @@ public:
void SetSink(const std::string& sink_id, const std::string& audio_device);
/// Get the current sink
Sink& GetSink();
/// Enable/Disable audio stretching.
void EnableStretching(bool enable);
protected:
void OutputFrame(StereoFrame16& frame);
......@@ -85,11 +82,8 @@ private:
void OutputCallback(s16* buffer, std::size_t num_frames);
std::unique_ptr<Sink> sink;
std::atomic<bool> perform_time_stretching = false;
std::atomic<bool> flushing_time_stretcher = false;
Common::RingBuffer<s16, 0x2000, 2> fifo;
std::array<s16, 2> last_frame{};
TimeStretcher time_stretcher;
};
} // namespace AudioCore
// Copyright 2016 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <algorithm>
#include <cmath>
#include <cstddef>
#include <memory>
#include <SoundTouch.h>
#include "audio_core/audio_types.h"
#include "audio_core/time_stretch.h"
#include "common/logging/log.h"
namespace AudioCore {
TimeStretcher::TimeStretcher()
: sample_rate(native_sample_rate), sound_touch(std::make_unique<soundtouch::SoundTouch>()) {
sound_touch->setChannels(2);
sound_touch->setSampleRate(native_sample_rate);
sound_touch->setPitch(1.0);
sound_touch->setTempo(1.0);
}
TimeStretcher::~TimeStretcher() = default;
void TimeStretcher::SetOutputSampleRate(unsigned int sample_rate) {
sound_touch->setSampleRate(sample_rate);
sample_rate = native_sample_rate;
}
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) / sample_rate; // seconds
double current_ratio = static_cast<double>(num_in) / static_cast<double>(num_out);
const double max_latency = 0.25; // seconds
const double max_backlog = sample_rate * max_latency;
const double backlog_fullness = 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.050; // 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);
stretch_ratio += lpf_gain * (current_ratio - 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.
stretch_ratio = std::max(stretch_ratio, 0.05);
sound_touch->setTempo(stretch_ratio);
LOG_DEBUG(Audio, "{:5}/{:5} ratio:{:0.6f} backlog:{:0.6f}", num_in, num_out, stretch_ratio,
backlog_fullness);
sound_touch->putSamples(in, static_cast<u32>(num_in));
return sound_touch->receiveSamples(out, static_cast<u32>(num_out));
}
void TimeStretcher::Clear() {
sound_touch->clear();
}
void TimeStretcher::Flush() {
sound_touch->flush();
}
} // namespace AudioCore
// Copyright 2016 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <array>
#include <cstddef>
#include <memory>
#include "common/common_types.h"
namespace soundtouch {
class SoundTouch;
}
namespace AudioCore {
class TimeStretcher {
public:
TimeStretcher();
~TimeStretcher();
void SetOutputSampleRate(unsigned int sample_rate);
/// @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:
unsigned int sample_rate;
std::unique_ptr<soundtouch::SoundTouch> sound_touch;
double stretch_ratio = 1.0;
};
} // namespace AudioCore
......@@ -181,7 +181,6 @@ System::ResultStatus System::Init(EmuWindow& emu_window, u32 system_mode) {
dsp_core = std::make_unique<AudioCore::DspHle>();
dsp_core->SetSink(Settings::values.sink_id, Settings::values.audio_device_id);
dsp_core->EnableStretching(Settings::values.enable_audio_stretching);
telemetry_session = std::make_unique<Core::TelemetrySession>();
......
......@@ -34,7 +34,6 @@ void Apply() {
if (Core::System::GetInstance().IsPoweredOn()) {
Core::DSP().SetSink(values.sink_id, values.audio_device_id);
Core::DSP().EnableStretching(values.enable_audio_stretching);
}
Service::HID::ReloadInputDevices();
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment