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
This commit is contained in:
		| @@ -37,24 +37,24 @@ | |||||||
| constexpr u64 BASE_CLOCK_RATE_ARM11 = 268111856; | constexpr u64 BASE_CLOCK_RATE_ARM11 = 268111856; | ||||||
| constexpr u64 MAX_VALUE_TO_MULTIPLY = std::numeric_limits<s64>::max() / BASE_CLOCK_RATE_ARM11; | constexpr u64 MAX_VALUE_TO_MULTIPLY = std::numeric_limits<s64>::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 |     // since ms is int there is no way to overflow | ||||||
|     return BASE_CLOCK_RATE_ARM11 * static_cast<s64>(ms) / 1000; |     return BASE_CLOCK_RATE_ARM11 * static_cast<s64>(ms) / 1000; | ||||||
| } | } | ||||||
|  |  | ||||||
| inline s64 msToCycles(float ms) { | constexpr s64 msToCycles(float ms) { | ||||||
|     return static_cast<s64>(BASE_CLOCK_RATE_ARM11 * (0.001f) * ms); |     return static_cast<s64>(BASE_CLOCK_RATE_ARM11 * (0.001f) * ms); | ||||||
| } | } | ||||||
|  |  | ||||||
| inline s64 msToCycles(double ms) { | constexpr s64 msToCycles(double ms) { | ||||||
|     return static_cast<s64>(BASE_CLOCK_RATE_ARM11 * (0.001) * ms); |     return static_cast<s64>(BASE_CLOCK_RATE_ARM11 * (0.001) * ms); | ||||||
| } | } | ||||||
|  |  | ||||||
| inline s64 usToCycles(float us) { | constexpr s64 usToCycles(float us) { | ||||||
|     return static_cast<s64>(BASE_CLOCK_RATE_ARM11 * (0.000001f) * us); |     return static_cast<s64>(BASE_CLOCK_RATE_ARM11 * (0.000001f) * us); | ||||||
| } | } | ||||||
|  |  | ||||||
| inline s64 usToCycles(int us) { | constexpr s64 usToCycles(int us) { | ||||||
|     return (BASE_CLOCK_RATE_ARM11 * static_cast<s64>(us) / 1000000); |     return (BASE_CLOCK_RATE_ARM11 * static_cast<s64>(us) / 1000000); | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -82,11 +82,11 @@ inline s64 usToCycles(u64 us) { | |||||||
|     return (BASE_CLOCK_RATE_ARM11 * static_cast<s64>(us)) / 1000000; |     return (BASE_CLOCK_RATE_ARM11 * static_cast<s64>(us)) / 1000000; | ||||||
| } | } | ||||||
|  |  | ||||||
| inline s64 nsToCycles(float ns) { | constexpr s64 nsToCycles(float ns) { | ||||||
|     return static_cast<s64>(BASE_CLOCK_RATE_ARM11 * (0.000000001f) * ns); |     return static_cast<s64>(BASE_CLOCK_RATE_ARM11 * (0.000000001f) * ns); | ||||||
| } | } | ||||||
|  |  | ||||||
| inline s64 nsToCycles(int ns) { | constexpr s64 nsToCycles(int ns) { | ||||||
|     return BASE_CLOCK_RATE_ARM11 * static_cast<s64>(ns) / 1000000000; |     return BASE_CLOCK_RATE_ARM11 * static_cast<s64>(ns) / 1000000000; | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -114,15 +114,15 @@ inline s64 nsToCycles(u64 ns) { | |||||||
|     return (BASE_CLOCK_RATE_ARM11 * static_cast<s64>(ns)) / 1000000000; |     return (BASE_CLOCK_RATE_ARM11 * static_cast<s64>(ns)) / 1000000000; | ||||||
| } | } | ||||||
|  |  | ||||||
| inline u64 cyclesToNs(s64 cycles) { | constexpr u64 cyclesToNs(s64 cycles) { | ||||||
|     return cycles * 1000000000 / BASE_CLOCK_RATE_ARM11; |     return cycles * 1000000000 / BASE_CLOCK_RATE_ARM11; | ||||||
| } | } | ||||||
|  |  | ||||||
| inline s64 cyclesToUs(s64 cycles) { | constexpr s64 cyclesToUs(s64 cycles) { | ||||||
|     return cycles * 1000000 / BASE_CLOCK_RATE_ARM11; |     return cycles * 1000000 / BASE_CLOCK_RATE_ARM11; | ||||||
| } | } | ||||||
|  |  | ||||||
| inline u64 cyclesToMs(s64 cycles) { | constexpr u64 cyclesToMs(s64 cycles) { | ||||||
|     return cycles * 1000 / BASE_CLOCK_RATE_ARM11; |     return cycles * 1000 / BASE_CLOCK_RATE_ARM11; | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -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. | // 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 GetBufferUpdatePeriod(SampleRate sample_rate) { | ||||||
| constexpr u64 GetBufferUpdateRate(SampleRate sample_rate) { |     return 15 * BASE_CLOCK_RATE_ARM11 / GetSampleRateInHz(sample_rate); | ||||||
|     return GetSampleRateInHz(sample_rate) / 16; |  | ||||||
| } | } | ||||||
|  |  | ||||||
| // Variables holding the current mic buffer writing state | // Variables holding the current mic buffer writing state | ||||||
| @@ -178,14 +177,23 @@ struct MIC_U::Impl { | |||||||
|         } |         } | ||||||
|  |  | ||||||
|         // schedule next run |         // schedule next run | ||||||
|         timing.ScheduleEvent(GetBufferUpdateRate(state.sample_rate) - cycles_late, |         timing.ScheduleEvent(GetBufferUpdatePeriod(state.sample_rate) - cycles_late, | ||||||
|                              buffer_write_event); |                              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<u32>(state.size)}); | ||||||
|  |     } | ||||||
|  |  | ||||||
|     void StartSampling(Kernel::HLERequestContext& ctx) { |     void StartSampling(Kernel::HLERequestContext& ctx) { | ||||||
|         IPC::RequestParser rp{ctx, 0x03, 5, 0}; |         IPC::RequestParser rp{ctx, 0x03, 5, 0}; | ||||||
|  |  | ||||||
|         Encoding encoding = rp.PopEnum<Encoding>(); |         encoding = rp.PopEnum<Encoding>(); | ||||||
|         SampleRate sample_rate = rp.PopEnum<SampleRate>(); |         SampleRate sample_rate = rp.PopEnum<SampleRate>(); | ||||||
|         u32 audio_buffer_offset = rp.PopRaw<u32>(); |         u32 audio_buffer_offset = rp.PopRaw<u32>(); | ||||||
|         u32 audio_buffer_size = rp.Pop<u32>(); |         u32 audio_buffer_size = rp.Pop<u32>(); | ||||||
| @@ -197,9 +205,6 @@ struct MIC_U::Impl { | |||||||
|             mic->StopSampling(); |             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; |         u8 sample_size = encoding == Encoding::PCM8Signed || encoding == Encoding::PCM8 ? 8 : 16; | ||||||
|         state.offset = state.initial_offset = audio_buffer_offset; |         state.offset = state.initial_offset = audio_buffer_offset; | ||||||
|         state.sample_rate = sample_rate; |         state.sample_rate = sample_rate; | ||||||
| @@ -207,10 +212,9 @@ struct MIC_U::Impl { | |||||||
|         state.looped_buffer = audio_buffer_loop; |         state.looped_buffer = audio_buffer_loop; | ||||||
|         state.size = audio_buffer_size; |         state.size = audio_buffer_size; | ||||||
|  |  | ||||||
|         mic->StartSampling({sign, sample_size, audio_buffer_loop, GetSampleRateInHz(sample_rate), |         StartSampling(); | ||||||
|                             audio_buffer_offset, audio_buffer_size}); |  | ||||||
|  |  | ||||||
|         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); |         IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); | ||||||
|         rb.Push(RESULT_SUCCESS); |         rb.Push(RESULT_SUCCESS); | ||||||
| @@ -393,10 +397,11 @@ struct MIC_U::Impl { | |||||||
|     std::unique_ptr<Frontend::Mic::Interface> mic; |     std::unique_ptr<Frontend::Mic::Interface> mic; | ||||||
|     Core::Timing& timing; |     Core::Timing& timing; | ||||||
|     State state{}; |     State state{}; | ||||||
|  |     Encoding encoding{}; | ||||||
|  |  | ||||||
| private: | private: | ||||||
|     template <class Archive> |     template <class Archive> | ||||||
|     void serialize(Archive& ar, const unsigned int) { |     void serialize(Archive& ar, const unsigned int file_version) { | ||||||
|         ar& change_mic_impl_requested; |         ar& change_mic_impl_requested; | ||||||
|         ar& buffer_full_event; |         ar& buffer_full_event; | ||||||
|         // buffer_write_event set in constructor |         // buffer_write_event set in constructor | ||||||
| @@ -406,6 +411,20 @@ private: | |||||||
|         ar& clamp; |         ar& clamp; | ||||||
|         // mic interface set in constructor |         // mic interface set in constructor | ||||||
|         ar& state; |         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; |     friend class boost::serialization::access; | ||||||
| }; | }; | ||||||
|   | |||||||
| @@ -5,7 +5,7 @@ | |||||||
| #pragma once | #pragma once | ||||||
|  |  | ||||||
| #include <memory> | #include <memory> | ||||||
|  | #include <boost/serialization/version.hpp> | ||||||
| #include "core/hle/service/service.h" | #include "core/hle/service/service.h" | ||||||
|  |  | ||||||
| namespace Core { | namespace Core { | ||||||
| @@ -204,3 +204,4 @@ void InstallInterfaces(Core::System& system); | |||||||
|  |  | ||||||
| SERVICE_CONSTRUCT(Service::MIC::MIC_U) | SERVICE_CONSTRUCT(Service::MIC::MIC_U) | ||||||
| BOOST_CLASS_EXPORT_KEY(Service::MIC::MIC_U) | BOOST_CLASS_EXPORT_KEY(Service::MIC::MIC_U) | ||||||
|  | BOOST_CLASS_VERSION(Service::MIC::MIC_U::Impl, 1) | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user