mirror of
				https://github.com/PabloMK7/citra.git
				synced 2025-11-03 23:28:48 +00:00 
			
		
		
		
	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:
		
							parent
							
								
									75c9784239
								
							
						
					
					
						commit
						397bd1bb73
					
				
					 3 changed files with 43 additions and 23 deletions
				
			
		| 
						 | 
				
			
			@ -37,24 +37,24 @@
 | 
			
		|||
constexpr u64 BASE_CLOCK_RATE_ARM11 = 268111856;
 | 
			
		||||
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
 | 
			
		||||
    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);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
inline s64 msToCycles(double ms) {
 | 
			
		||||
constexpr s64 msToCycles(double 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);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
inline s64 usToCycles(int us) {
 | 
			
		||||
constexpr s64 usToCycles(int us) {
 | 
			
		||||
    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;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
inline s64 nsToCycles(float ns) {
 | 
			
		||||
constexpr s64 nsToCycles(float 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;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -114,15 +114,15 @@ inline s64 nsToCycles(u64 ns) {
 | 
			
		|||
    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;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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<u32>(state.size)});
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void StartSampling(Kernel::HLERequestContext& ctx) {
 | 
			
		||||
        IPC::RequestParser rp{ctx, 0x03, 5, 0};
 | 
			
		||||
 | 
			
		||||
        Encoding encoding = rp.PopEnum<Encoding>();
 | 
			
		||||
        encoding = rp.PopEnum<Encoding>();
 | 
			
		||||
        SampleRate sample_rate = rp.PopEnum<SampleRate>();
 | 
			
		||||
        u32 audio_buffer_offset = rp.PopRaw<u32>();
 | 
			
		||||
        u32 audio_buffer_size = rp.Pop<u32>();
 | 
			
		||||
| 
						 | 
				
			
			@ -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<Frontend::Mic::Interface> mic;
 | 
			
		||||
    Core::Timing& timing;
 | 
			
		||||
    State state{};
 | 
			
		||||
    Encoding encoding{};
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    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& 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;
 | 
			
		||||
};
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -5,7 +5,7 @@
 | 
			
		|||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <memory>
 | 
			
		||||
 | 
			
		||||
#include <boost/serialization/version.hpp>
 | 
			
		||||
#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)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue