From 397bd1bb7381eaa34ed7c3bcf18661bd3f66fcbb Mon Sep 17 00:00:00 2001 From: Hamish Milne Date: Sat, 18 Apr 2020 19:52:56 +0100 Subject: [PATCH] Fix MIC_U serialization and timing (#5223) * Fix MIC_U serialization and timing * Better sample rate conversion * Actually should be every 15 samples * Reduce rounding errors --- src/core/core_timing.h | 20 ++++++++-------- src/core/hle/service/mic_u.cpp | 43 ++++++++++++++++++++++++---------- src/core/hle/service/mic_u.h | 3 ++- 3 files changed, 43 insertions(+), 23 deletions(-) diff --git a/src/core/core_timing.h b/src/core/core_timing.h index bb34c79b0..74437c9b1 100644 --- a/src/core/core_timing.h +++ b/src/core/core_timing.h @@ -37,24 +37,24 @@ constexpr u64 BASE_CLOCK_RATE_ARM11 = 268111856; constexpr u64 MAX_VALUE_TO_MULTIPLY = std::numeric_limits::max() / BASE_CLOCK_RATE_ARM11; -inline s64 msToCycles(int ms) { +constexpr s64 msToCycles(int ms) { // since ms is int there is no way to overflow return BASE_CLOCK_RATE_ARM11 * static_cast(ms) / 1000; } -inline s64 msToCycles(float ms) { +constexpr s64 msToCycles(float ms) { return static_cast(BASE_CLOCK_RATE_ARM11 * (0.001f) * ms); } -inline s64 msToCycles(double ms) { +constexpr s64 msToCycles(double ms) { return static_cast(BASE_CLOCK_RATE_ARM11 * (0.001) * ms); } -inline s64 usToCycles(float us) { +constexpr s64 usToCycles(float us) { return static_cast(BASE_CLOCK_RATE_ARM11 * (0.000001f) * us); } -inline s64 usToCycles(int us) { +constexpr s64 usToCycles(int us) { return (BASE_CLOCK_RATE_ARM11 * static_cast(us) / 1000000); } @@ -82,11 +82,11 @@ inline s64 usToCycles(u64 us) { return (BASE_CLOCK_RATE_ARM11 * static_cast(us)) / 1000000; } -inline s64 nsToCycles(float ns) { +constexpr s64 nsToCycles(float ns) { return static_cast(BASE_CLOCK_RATE_ARM11 * (0.000000001f) * ns); } -inline s64 nsToCycles(int ns) { +constexpr s64 nsToCycles(int ns) { return BASE_CLOCK_RATE_ARM11 * static_cast(ns) / 1000000000; } @@ -114,15 +114,15 @@ inline s64 nsToCycles(u64 ns) { return (BASE_CLOCK_RATE_ARM11 * static_cast(ns)) / 1000000000; } -inline u64 cyclesToNs(s64 cycles) { +constexpr u64 cyclesToNs(s64 cycles) { return cycles * 1000000000 / BASE_CLOCK_RATE_ARM11; } -inline s64 cyclesToUs(s64 cycles) { +constexpr s64 cyclesToUs(s64 cycles) { return cycles * 1000000 / BASE_CLOCK_RATE_ARM11; } -inline u64 cyclesToMs(s64 cycles) { +constexpr u64 cyclesToMs(s64 cycles) { return cycles * 1000 / BASE_CLOCK_RATE_ARM11; } diff --git a/src/core/hle/service/mic_u.cpp b/src/core/hle/service/mic_u.cpp index f0017ac4c..de1bfc074 100644 --- a/src/core/hle/service/mic_u.cpp +++ b/src/core/hle/service/mic_u.cpp @@ -63,9 +63,8 @@ constexpr u32 GetSampleRateInHz(SampleRate sample_rate) { } // The 3ds hardware was tested to write to the sharedmem every 15 samples regardless of sample_rate. -// So we can just divide the sample rate by 16 and that'll give the correct timing for the event -constexpr u64 GetBufferUpdateRate(SampleRate sample_rate) { - return GetSampleRateInHz(sample_rate) / 16; +constexpr u64 GetBufferUpdatePeriod(SampleRate sample_rate) { + return 15 * BASE_CLOCK_RATE_ARM11 / GetSampleRateInHz(sample_rate); } // Variables holding the current mic buffer writing state @@ -178,14 +177,23 @@ struct MIC_U::Impl { } // schedule next run - timing.ScheduleEvent(GetBufferUpdateRate(state.sample_rate) - cycles_late, + timing.ScheduleEvent(GetBufferUpdatePeriod(state.sample_rate) - cycles_late, buffer_write_event); } + void StartSampling() { + auto sign = encoding == Encoding::PCM8Signed || encoding == Encoding::PCM16Signed + ? Frontend::Mic::Signedness::Signed + : Frontend::Mic::Signedness::Unsigned; + mic->StartSampling({sign, state.sample_size, state.looped_buffer, + GetSampleRateInHz(state.sample_rate), state.initial_offset, + static_cast(state.size)}); + } + void StartSampling(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx, 0x03, 5, 0}; - Encoding encoding = rp.PopEnum(); + encoding = rp.PopEnum(); SampleRate sample_rate = rp.PopEnum(); u32 audio_buffer_offset = rp.PopRaw(); u32 audio_buffer_size = rp.Pop(); @@ -197,9 +205,6 @@ struct MIC_U::Impl { mic->StopSampling(); } - auto sign = encoding == Encoding::PCM8Signed || encoding == Encoding::PCM16Signed - ? Frontend::Mic::Signedness::Signed - : Frontend::Mic::Signedness::Unsigned; u8 sample_size = encoding == Encoding::PCM8Signed || encoding == Encoding::PCM8 ? 8 : 16; state.offset = state.initial_offset = audio_buffer_offset; state.sample_rate = sample_rate; @@ -207,10 +212,9 @@ struct MIC_U::Impl { state.looped_buffer = audio_buffer_loop; state.size = audio_buffer_size; - mic->StartSampling({sign, sample_size, audio_buffer_loop, GetSampleRateInHz(sample_rate), - audio_buffer_offset, audio_buffer_size}); + StartSampling(); - timing.ScheduleEvent(GetBufferUpdateRate(state.sample_rate), buffer_write_event); + timing.ScheduleEvent(GetBufferUpdatePeriod(state.sample_rate), buffer_write_event); IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); rb.Push(RESULT_SUCCESS); @@ -393,10 +397,11 @@ struct MIC_U::Impl { std::unique_ptr mic; Core::Timing& timing; State state{}; + Encoding encoding{}; private: template - void serialize(Archive& ar, const unsigned int) { + void serialize(Archive& ar, const unsigned int file_version) { ar& change_mic_impl_requested; ar& buffer_full_event; // buffer_write_event set in constructor @@ -406,6 +411,20 @@ private: ar& clamp; // mic interface set in constructor ar& state; + if (file_version > 0) { + // Maintain the internal mic state + ar& encoding; + bool is_sampling = mic && mic->IsSampling(); + ar& is_sampling; + if (Archive::is_loading::value) { + if (is_sampling) { + CreateMic(); + StartSampling(); + } else if (mic) { + mic->StopSampling(); + } + } + } } friend class boost::serialization::access; }; diff --git a/src/core/hle/service/mic_u.h b/src/core/hle/service/mic_u.h index 2ca95e924..1a404f194 100644 --- a/src/core/hle/service/mic_u.h +++ b/src/core/hle/service/mic_u.h @@ -5,7 +5,7 @@ #pragma once #include - +#include #include "core/hle/service/service.h" namespace Core { @@ -204,3 +204,4 @@ void InstallInterfaces(Core::System& system); SERVICE_CONSTRUCT(Service::MIC::MIC_U) BOOST_CLASS_EXPORT_KEY(Service::MIC::MIC_U) +BOOST_CLASS_VERSION(Service::MIC::MIC_U::Impl, 1)