citra-emu
/
citra-canary
Archived
1
0
Fork 0

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:
Hamish Milne 2020-04-18 19:52:56 +01:00 committed by GitHub
parent 75c9784239
commit 397bd1bb73
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 43 additions and 23 deletions

View File

@ -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;
} }

View File

@ -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;
}; };

View File

@ -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)