summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorGravatar MerryMage2018-09-08 16:49:04 +0100
committerGravatar MerryMage2018-09-08 18:56:38 +0100
commit1aa195a9c0416c986c8224d9dc66d9d5e45401a0 (patch)
tree4cd60ccf28c9ab8aeaddce0eb16be3a831ba9196 /src
parentaudio_core: Add audio stretcher (diff)
downloadyuzu-1aa195a9c0416c986c8224d9dc66d9d5e45401a0.tar.gz
yuzu-1aa195a9c0416c986c8224d9dc66d9d5e45401a0.tar.xz
yuzu-1aa195a9c0416c986c8224d9dc66d9d5e45401a0.zip
cubeb_sink: Perform audio stretching
Diffstat (limited to '')
-rw-r--r--src/audio_core/cubeb_sink.cpp37
-rw-r--r--src/audio_core/time_stretch.cpp12
-rw-r--r--src/audio_core/time_stretch.h1
3 files changed, 26 insertions, 24 deletions
diff --git a/src/audio_core/cubeb_sink.cpp b/src/audio_core/cubeb_sink.cpp
index 3c129122f..7982306b3 100644
--- a/src/audio_core/cubeb_sink.cpp
+++ b/src/audio_core/cubeb_sink.cpp
@@ -6,8 +6,10 @@
6#include <cstring> 6#include <cstring>
7#include "audio_core/cubeb_sink.h" 7#include "audio_core/cubeb_sink.h"
8#include "audio_core/stream.h" 8#include "audio_core/stream.h"
9#include "audio_core/time_stretch.h"
9#include "common/logging/log.h" 10#include "common/logging/log.h"
10#include "common/ring_buffer.h" 11#include "common/ring_buffer.h"
12#include "core/settings.h"
11 13
12namespace AudioCore { 14namespace AudioCore {
13 15
@@ -15,14 +17,8 @@ class CubebSinkStream final : public SinkStream {
15public: 17public:
16 CubebSinkStream(cubeb* ctx, u32 sample_rate, u32 num_channels_, cubeb_devid output_device, 18 CubebSinkStream(cubeb* ctx, u32 sample_rate, u32 num_channels_, cubeb_devid output_device,
17 const std::string& name) 19 const std::string& name)
18 : ctx{ctx}, num_channels{num_channels_} { 20 : ctx{ctx}, is_6_channel{num_channels_ == 6}, num_channels{std::min(num_channels_, 2u)},
19 21 time_stretch{sample_rate, num_channels} {
20 if (num_channels == 6) {
21 // 6-channel audio does not seem to work with cubeb + SDL, so we downsample this to 2
22 // channel for now
23 is_6_channel = true;
24 num_channels = 2;
25 }
26 22
27 cubeb_stream_params params{}; 23 cubeb_stream_params params{};
28 params.rate = sample_rate; 24 params.rate = sample_rate;
@@ -89,10 +85,6 @@ public:
89 return num_channels; 85 return num_channels;
90 } 86 }
91 87
92 u32 GetNumChannelsInQueue() const {
93 return num_channels == 1 ? 1 : 2;
94 }
95
96private: 88private:
97 std::vector<std::string> device_list; 89 std::vector<std::string> device_list;
98 90
@@ -103,6 +95,7 @@ private:
103 95
104 Common::RingBuffer<s16, 0x10000> queue; 96 Common::RingBuffer<s16, 0x10000> queue;
105 std::array<s16, 2> last_frame; 97 std::array<s16, 2> last_frame;
98 TimeStretcher time_stretch;
106 99
107 static long DataCallback(cubeb_stream* stream, void* user_data, const void* input_buffer, 100 static long DataCallback(cubeb_stream* stream, void* user_data, const void* input_buffer,
108 void* output_buffer, long num_frames); 101 void* output_buffer, long num_frames);
@@ -153,7 +146,7 @@ SinkStream& CubebSink::AcquireSinkStream(u32 sample_rate, u32 num_channels,
153} 146}
154 147
155long CubebSinkStream::DataCallback(cubeb_stream* stream, void* user_data, const void* input_buffer, 148long CubebSinkStream::DataCallback(cubeb_stream* stream, void* user_data, const void* input_buffer,
156 void* output_buffer, long num_frames) { 149 void* output_buffer, long num_frames) {
157 CubebSinkStream* impl = static_cast<CubebSinkStream*>(user_data); 150 CubebSinkStream* impl = static_cast<CubebSinkStream*>(user_data);
158 u8* buffer = reinterpret_cast<u8*>(output_buffer); 151 u8* buffer = reinterpret_cast<u8*>(output_buffer);
159 152
@@ -161,9 +154,19 @@ long CubebSinkStream::DataCallback(cubeb_stream* stream, void* user_data, const
161 return {}; 154 return {};
162 } 155 }
163 156
164 const size_t num_channels = impl->GetNumChannelsInQueue(); 157 const size_t num_channels = impl->GetNumChannels();
165 const size_t max_samples_to_write = num_channels * num_frames; 158 const size_t samples_to_write = num_channels * num_frames;
166 const size_t samples_written = impl->queue.Pop(buffer, max_samples_to_write); 159 size_t samples_written;
160
161 if (Settings::values.enable_audio_stretching) {
162 const std::vector<s16> in{impl->queue.Pop()};
163 const size_t num_in{in.size() / num_channels};
164 s16* const out{reinterpret_cast<s16*>(buffer)};
165 const size_t out_frames = impl->time_stretch.Process(in.data(), num_in, out, num_frames);
166 samples_written = out_frames * num_channels;
167 } else {
168 samples_written = impl->queue.Pop(buffer, samples_to_write);
169 }
167 170
168 if (samples_written >= num_channels) { 171 if (samples_written >= num_channels) {
169 std::memcpy(&impl->last_frame[0], buffer + (samples_written - num_channels) * sizeof(s16), 172 std::memcpy(&impl->last_frame[0], buffer + (samples_written - num_channels) * sizeof(s16),
@@ -171,7 +174,7 @@ long CubebSinkStream::DataCallback(cubeb_stream* stream, void* user_data, const
171 } 174 }
172 175
173 // Fill the rest of the frames with last_frame 176 // Fill the rest of the frames with last_frame
174 for (size_t i = samples_written; i < max_samples_to_write; i += num_channels) { 177 for (size_t i = samples_written; i < samples_to_write; i += num_channels) {
175 std::memcpy(buffer + i * sizeof(s16), &impl->last_frame[0], num_channels * sizeof(s16)); 178 std::memcpy(buffer + i * sizeof(s16), &impl->last_frame[0], num_channels * sizeof(s16));
176 } 179 }
177 180
diff --git a/src/audio_core/time_stretch.cpp b/src/audio_core/time_stretch.cpp
index 17e128323..d2e3391c1 100644
--- a/src/audio_core/time_stretch.cpp
+++ b/src/audio_core/time_stretch.cpp
@@ -28,8 +28,8 @@ size_t TimeStretcher::Process(const s16* in, size_t num_in, s16* out, size_t num
28 // We were given actual_samples number of samples, and num_samples were requested from us. 28 // We were given actual_samples number of samples, and num_samples were requested from us.
29 double current_ratio = static_cast<double>(num_in) / static_cast<double>(num_out); 29 double current_ratio = static_cast<double>(num_in) / static_cast<double>(num_out);
30 30
31 const double max_latency = 0.3; // seconds 31 const double max_latency = 1.0; // seconds
32 const double max_backlog = m_sample_rate * max_latency / 1000.0 / m_stretch_ratio; 32 const double max_backlog = m_sample_rate * max_latency;
33 const double backlog_fullness = m_sound_touch.numSamples() / max_backlog; 33 const double backlog_fullness = m_sound_touch.numSamples() / max_backlog;
34 if (backlog_fullness > 5.0) { 34 if (backlog_fullness > 5.0) {
35 // Too many samples in backlog: Don't push anymore on 35 // Too many samples in backlog: Don't push anymore on
@@ -49,13 +49,13 @@ size_t TimeStretcher::Process(const s16* in, size_t num_in, s16* out, size_t num
49 const double lpf_gain = 1.0 - std::exp(-time_delta / lpf_time_scale); 49 const double lpf_gain = 1.0 - std::exp(-time_delta / lpf_time_scale);
50 m_stretch_ratio += lpf_gain * (current_ratio - m_stretch_ratio); 50 m_stretch_ratio += lpf_gain * (current_ratio - m_stretch_ratio);
51 51
52 // Place a lower limit of 10% speed. When a game boots up, there will be 52 // Place a lower limit of 5% speed. When a game boots up, there will be
53 // many silence samples. These do not need to be timestretched. 53 // many silence samples. These do not need to be timestretched.
54 m_stretch_ratio = std::max(m_stretch_ratio, 0.1); 54 m_stretch_ratio = std::max(m_stretch_ratio, 0.05);
55 m_sound_touch.setTempo(m_stretch_ratio); 55 m_sound_touch.setTempo(m_stretch_ratio);
56 56
57 LOG_DEBUG(Audio, "Audio Stretching: samples:{}/{} ratio:{} backlog:{} gain: {}", num_in, num_out, 57 LOG_DEBUG(Audio, "{:5}/{:5} ratio:{:0.6f} backlog:{:0.6f}", num_in, num_out, m_stretch_ratio,
58 m_stretch_ratio, backlog_fullness, lpf_gain); 58 backlog_fullness);
59 59
60 m_sound_touch.putSamples(in, num_in); 60 m_sound_touch.putSamples(in, num_in);
61 return m_sound_touch.receiveSamples(out, num_out); 61 return m_sound_touch.receiveSamples(out, num_out);
diff --git a/src/audio_core/time_stretch.h b/src/audio_core/time_stretch.h
index cdead34a2..0322b8b78 100644
--- a/src/audio_core/time_stretch.h
+++ b/src/audio_core/time_stretch.h
@@ -27,7 +27,6 @@ public:
27private: 27private:
28 u32 m_sample_rate; 28 u32 m_sample_rate;
29 u32 m_channel_count; 29 u32 m_channel_count;
30 std::array<s16, 2> m_last_stretched_sample = {};
31 soundtouch::SoundTouch m_sound_touch; 30 soundtouch::SoundTouch m_sound_touch;
32 double m_stretch_ratio = 1.0; 31 double m_stretch_ratio = 1.0;
33}; 32};