diff options
| author | 2017-08-03 12:22:51 +0100 | |
|---|---|---|
| committer | 2017-08-28 10:54:41 +0100 | |
| commit | 933508e2a2f7923cebc15d679b78933df8fb9ee5 (patch) | |
| tree | e49faeb30929c03c5490d8ae507ec907060c1068 /src | |
| parent | Merge pull request #2850 from j-selby/fix_invalid_paths (diff) | |
| download | yuzu-933508e2a2f7923cebc15d679b78933df8fb9ee5.tar.gz yuzu-933508e2a2f7923cebc15d679b78933df8fb9ee5.tar.xz yuzu-933508e2a2f7923cebc15d679b78933df8fb9ee5.zip | |
interpolate: Interpolate on a frame-by-frame basis
Diffstat (limited to 'src')
| -rw-r--r-- | src/audio_core/hle/source.cpp | 49 | ||||
| -rw-r--r-- | src/audio_core/interpolate.cpp | 86 | ||||
| -rw-r--r-- | src/audio_core/interpolate.h | 27 |
3 files changed, 74 insertions, 88 deletions
diff --git a/src/audio_core/hle/source.cpp b/src/audio_core/hle/source.cpp index 92484c526..de4e88cae 100644 --- a/src/audio_core/hle/source.cpp +++ b/src/audio_core/hle/source.cpp | |||
| @@ -244,17 +244,27 @@ void Source::GenerateFrame() { | |||
| 244 | break; | 244 | break; |
| 245 | } | 245 | } |
| 246 | 246 | ||
| 247 | const size_t size_to_copy = | 247 | switch (state.interpolation_mode) { |
| 248 | std::min(state.current_buffer.size(), current_frame.size() - frame_position); | 248 | case InterpolationMode::None: |
| 249 | 249 | AudioInterp::None(state.interp_state, state.current_buffer, state.rate_multiplier, | |
| 250 | std::copy(state.current_buffer.begin(), state.current_buffer.begin() + size_to_copy, | 250 | current_frame, frame_position); |
| 251 | current_frame.begin() + frame_position); | 251 | break; |
| 252 | state.current_buffer.erase(state.current_buffer.begin(), | 252 | case InterpolationMode::Linear: |
| 253 | state.current_buffer.begin() + size_to_copy); | 253 | AudioInterp::Linear(state.interp_state, state.current_buffer, state.rate_multiplier, |
| 254 | 254 | current_frame, frame_position); | |
| 255 | frame_position += size_to_copy; | 255 | break; |
| 256 | state.next_sample_number += static_cast<u32>(size_to_copy); | 256 | case InterpolationMode::Polyphase: |
| 257 | // TODO(merry): Implement polyphase interpolation | ||
| 258 | LOG_DEBUG(Audio_DSP, "Polyphase interpolation unimplemented; falling back to linear"); | ||
| 259 | AudioInterp::Linear(state.interp_state, state.current_buffer, state.rate_multiplier, | ||
| 260 | current_frame, frame_position); | ||
| 261 | break; | ||
| 262 | default: | ||
| 263 | UNIMPLEMENTED(); | ||
| 264 | break; | ||
| 265 | } | ||
| 257 | } | 266 | } |
| 267 | state.next_sample_number += frame_position; | ||
| 258 | 268 | ||
| 259 | state.filters.ProcessFrame(current_frame); | 269 | state.filters.ProcessFrame(current_frame); |
| 260 | } | 270 | } |
| @@ -305,25 +315,6 @@ bool Source::DequeueBuffer() { | |||
| 305 | return true; | 315 | return true; |
| 306 | } | 316 | } |
| 307 | 317 | ||
| 308 | switch (state.interpolation_mode) { | ||
| 309 | case InterpolationMode::None: | ||
| 310 | state.current_buffer = | ||
| 311 | AudioInterp::None(state.interp_state, state.current_buffer, state.rate_multiplier); | ||
| 312 | break; | ||
| 313 | case InterpolationMode::Linear: | ||
| 314 | state.current_buffer = | ||
| 315 | AudioInterp::Linear(state.interp_state, state.current_buffer, state.rate_multiplier); | ||
| 316 | break; | ||
| 317 | case InterpolationMode::Polyphase: | ||
| 318 | // TODO(merry): Implement polyphase interpolation | ||
| 319 | state.current_buffer = | ||
| 320 | AudioInterp::Linear(state.interp_state, state.current_buffer, state.rate_multiplier); | ||
| 321 | break; | ||
| 322 | default: | ||
| 323 | UNIMPLEMENTED(); | ||
| 324 | break; | ||
| 325 | } | ||
| 326 | |||
| 327 | // the first playthrough starts at play_position, loops start at the beginning of the buffer | 318 | // the first playthrough starts at play_position, loops start at the beginning of the buffer |
| 328 | state.current_sample_number = (!buf.has_played) ? buf.play_position : 0; | 319 | state.current_sample_number = (!buf.has_played) ? buf.play_position : 0; |
| 329 | state.next_sample_number = state.current_sample_number; | 320 | state.next_sample_number = state.current_sample_number; |
diff --git a/src/audio_core/interpolate.cpp b/src/audio_core/interpolate.cpp index 8a5d4181a..16e68bc5c 100644 --- a/src/audio_core/interpolate.cpp +++ b/src/audio_core/interpolate.cpp | |||
| @@ -13,74 +13,64 @@ namespace AudioInterp { | |||
| 13 | constexpr u64 scale_factor = 1 << 24; | 13 | constexpr u64 scale_factor = 1 << 24; |
| 14 | constexpr u64 scale_mask = scale_factor - 1; | 14 | constexpr u64 scale_mask = scale_factor - 1; |
| 15 | 15 | ||
| 16 | /// Here we step over the input in steps of rate_multiplier, until we consume all of the input. | 16 | /// Here we step over the input in steps of rate, until we consume all of the input. |
| 17 | /// Three adjacent samples are passed to fn each step. | 17 | /// Three adjacent samples are passed to fn each step. |
| 18 | template <typename Function> | 18 | template <typename Function> |
| 19 | static StereoBuffer16 StepOverSamples(State& state, const StereoBuffer16& input, | 19 | static void StepOverSamples(State& state, StereoBuffer16& input, float rate, |
| 20 | float rate_multiplier, Function fn) { | 20 | DSP::HLE::StereoFrame16& output, size_t& outputi, Function fn) { |
| 21 | ASSERT(rate_multiplier > 0); | 21 | ASSERT(rate > 0); |
| 22 | 22 | ||
| 23 | if (input.size() < 2) | 23 | if (input.empty()) |
| 24 | return {}; | 24 | return; |
| 25 | 25 | ||
| 26 | StereoBuffer16 output; | 26 | input.insert(input.begin(), {state.xn2, state.xn1}); |
| 27 | output.reserve(static_cast<size_t>(input.size() / rate_multiplier)); | ||
| 28 | 27 | ||
| 29 | u64 step_size = static_cast<u64>(rate_multiplier * scale_factor); | 28 | const u64 step_size = static_cast<u64>(rate * scale_factor); |
| 29 | u64 fposition = state.fposition; | ||
| 30 | size_t inputi = 0; | ||
| 30 | 31 | ||
| 31 | u64 fposition = 0; | 32 | while (outputi < output.size()) { |
| 32 | const u64 max_fposition = input.size() * scale_factor; | 33 | inputi = static_cast<size_t>(fposition / scale_factor); |
| 33 | 34 | ||
| 34 | while (fposition < 1 * scale_factor) { | 35 | if (inputi + 2 >= input.size()) { |
| 35 | u64 fraction = fposition & scale_mask; | 36 | inputi = input.size() - 2; |
| 36 | 37 | break; | |
| 37 | output.push_back(fn(fraction, state.xn2, state.xn1, input[0])); | 38 | } |
| 38 | |||
| 39 | fposition += step_size; | ||
| 40 | } | ||
| 41 | |||
| 42 | while (fposition < 2 * scale_factor) { | ||
| 43 | u64 fraction = fposition & scale_mask; | ||
| 44 | |||
| 45 | output.push_back(fn(fraction, state.xn1, input[0], input[1])); | ||
| 46 | |||
| 47 | fposition += step_size; | ||
| 48 | } | ||
| 49 | 39 | ||
| 50 | while (fposition < max_fposition) { | ||
| 51 | u64 fraction = fposition & scale_mask; | 40 | u64 fraction = fposition & scale_mask; |
| 52 | 41 | output[outputi++] = fn(fraction, input[inputi], input[inputi + 1], input[inputi + 2]); | |
| 53 | size_t index = static_cast<size_t>(fposition / scale_factor); | ||
| 54 | output.push_back(fn(fraction, input[index - 2], input[index - 1], input[index])); | ||
| 55 | 42 | ||
| 56 | fposition += step_size; | 43 | fposition += step_size; |
| 57 | } | 44 | } |
| 58 | 45 | ||
| 59 | state.xn2 = input[input.size() - 2]; | 46 | state.xn2 = input[inputi]; |
| 60 | state.xn1 = input[input.size() - 1]; | 47 | state.xn1 = input[inputi + 1]; |
| 48 | state.fposition = fposition - inputi * scale_factor; | ||
| 61 | 49 | ||
| 62 | return output; | 50 | input.erase(input.begin(), input.begin() + inputi + 2); |
| 63 | } | 51 | } |
| 64 | 52 | ||
| 65 | StereoBuffer16 None(State& state, const StereoBuffer16& input, float rate_multiplier) { | 53 | void None(State& state, StereoBuffer16& input, float rate, DSP::HLE::StereoFrame16& output, |
| 66 | return StepOverSamples( | 54 | size_t& outputi) { |
| 67 | state, input, rate_multiplier, | 55 | StepOverSamples( |
| 56 | state, input, rate, output, outputi, | ||
| 68 | [](u64 fraction, const auto& x0, const auto& x1, const auto& x2) { return x0; }); | 57 | [](u64 fraction, const auto& x0, const auto& x1, const auto& x2) { return x0; }); |
| 69 | } | 58 | } |
| 70 | 59 | ||
| 71 | StereoBuffer16 Linear(State& state, const StereoBuffer16& input, float rate_multiplier) { | 60 | void Linear(State& state, StereoBuffer16& input, float rate, DSP::HLE::StereoFrame16& output, |
| 61 | size_t& outputi) { | ||
| 72 | // Note on accuracy: Some values that this produces are +/- 1 from the actual firmware. | 62 | // Note on accuracy: Some values that this produces are +/- 1 from the actual firmware. |
| 73 | return StepOverSamples(state, input, rate_multiplier, | 63 | StepOverSamples(state, input, rate, output, outputi, |
| 74 | [](u64 fraction, const auto& x0, const auto& x1, const auto& x2) { | 64 | [](u64 fraction, const auto& x0, const auto& x1, const auto& x2) { |
| 75 | // This is a saturated subtraction. (Verified by black-box fuzzing.) | 65 | // This is a saturated subtraction. (Verified by black-box fuzzing.) |
| 76 | s64 delta0 = MathUtil::Clamp<s64>(x1[0] - x0[0], -32768, 32767); | 66 | s64 delta0 = MathUtil::Clamp<s64>(x1[0] - x0[0], -32768, 32767); |
| 77 | s64 delta1 = MathUtil::Clamp<s64>(x1[1] - x0[1], -32768, 32767); | 67 | s64 delta1 = MathUtil::Clamp<s64>(x1[1] - x0[1], -32768, 32767); |
| 78 | 68 | ||
| 79 | return std::array<s16, 2>{ | 69 | return std::array<s16, 2>{ |
| 80 | static_cast<s16>(x0[0] + fraction * delta0 / scale_factor), | 70 | static_cast<s16>(x0[0] + fraction * delta0 / scale_factor), |
| 81 | static_cast<s16>(x0[1] + fraction * delta1 / scale_factor), | 71 | static_cast<s16>(x0[1] + fraction * delta1 / scale_factor), |
| 82 | }; | 72 | }; |
| 83 | }); | 73 | }); |
| 84 | } | 74 | } |
| 85 | 75 | ||
| 86 | } // namespace AudioInterp | 76 | } // namespace AudioInterp |
diff --git a/src/audio_core/interpolate.h b/src/audio_core/interpolate.h index 19a7b66cb..59f59bc14 100644 --- a/src/audio_core/interpolate.h +++ b/src/audio_core/interpolate.h | |||
| @@ -6,6 +6,7 @@ | |||
| 6 | 6 | ||
| 7 | #include <array> | 7 | #include <array> |
| 8 | #include <vector> | 8 | #include <vector> |
| 9 | #include "audio_core/hle/common.h" | ||
| 9 | #include "common/common_types.h" | 10 | #include "common/common_types.h" |
| 10 | 11 | ||
| 11 | namespace AudioInterp { | 12 | namespace AudioInterp { |
| @@ -14,31 +15,35 @@ namespace AudioInterp { | |||
| 14 | using StereoBuffer16 = std::vector<std::array<s16, 2>>; | 15 | using StereoBuffer16 = std::vector<std::array<s16, 2>>; |
| 15 | 16 | ||
| 16 | struct State { | 17 | struct State { |
| 17 | // Two historical samples. | 18 | /// Two historical samples. |
| 18 | std::array<s16, 2> xn1 = {}; ///< x[n-1] | 19 | std::array<s16, 2> xn1 = {}; ///< x[n-1] |
| 19 | std::array<s16, 2> xn2 = {}; ///< x[n-2] | 20 | std::array<s16, 2> xn2 = {}; ///< x[n-2] |
| 21 | /// Current fractional position. | ||
| 22 | u64 fposition = 0; | ||
| 20 | }; | 23 | }; |
| 21 | 24 | ||
| 22 | /** | 25 | /** |
| 23 | * No interpolation. This is equivalent to a zero-order hold. There is a two-sample predelay. | 26 | * No interpolation. This is equivalent to a zero-order hold. There is a two-sample predelay. |
| 24 | * @param state Interpolation state. | 27 | * @param state Interpolation state. |
| 25 | * @param input Input buffer. | 28 | * @param input Input buffer. |
| 26 | * @param rate_multiplier Stretch factor. Must be a positive non-zero value. | 29 | * @param rate Stretch factor. Must be a positive non-zero value. |
| 27 | * rate_multiplier > 1.0 performs decimation and rate_multipler < 1.0 | 30 | * rate > 1.0 performs decimation and rate < 1.0 performs upsampling. |
| 28 | * performs upsampling. | 31 | * @param output The resampled audio buffer. |
| 29 | * @return The resampled audio buffer. | 32 | * @param outputi The index of output to start writing to. |
| 30 | */ | 33 | */ |
| 31 | StereoBuffer16 None(State& state, const StereoBuffer16& input, float rate_multiplier); | 34 | void None(State& state, StereoBuffer16& input, float rate, DSP::HLE::StereoFrame16& output, |
| 35 | size_t& outputi); | ||
| 32 | 36 | ||
| 33 | /** | 37 | /** |
| 34 | * Linear interpolation. This is equivalent to a first-order hold. There is a two-sample predelay. | 38 | * Linear interpolation. This is equivalent to a first-order hold. There is a two-sample predelay. |
| 35 | * @param state Interpolation state. | 39 | * @param state Interpolation state. |
| 36 | * @param input Input buffer. | 40 | * @param input Input buffer. |
| 37 | * @param rate_multiplier Stretch factor. Must be a positive non-zero value. | 41 | * @param rate Stretch factor. Must be a positive non-zero value. |
| 38 | * rate_multiplier > 1.0 performs decimation and rate_multipler < 1.0 | 42 | * rate > 1.0 performs decimation and rate < 1.0 performs upsampling. |
| 39 | * performs upsampling. | 43 | * @param output The resampled audio buffer. |
| 40 | * @return The resampled audio buffer. | 44 | * @param outputi The index of output to start writing to. |
| 41 | */ | 45 | */ |
| 42 | StereoBuffer16 Linear(State& state, const StereoBuffer16& input, float rate_multiplier); | 46 | void Linear(State& state, StereoBuffer16& input, float rate, DSP::HLE::StereoFrame16& output, |
| 47 | size_t& outputi); | ||
| 43 | 48 | ||
| 44 | } // namespace AudioInterp | 49 | } // namespace AudioInterp |