mirror of
https://github.com/libretro/parallel-gx1.git
synced 2026-11-07 04:26:01 -10:00
retro_run runs one video frame and the frontend paces it, so the core no longer paces itself: - The speed limiter is gone: CoreTiming::Throttle and its host-time targets, VI skip, the presenter's presentation-time wait, PrecisionTimer, PerformanceMetrics/PerformanceTracker, and the emulation speed, precision timing, rush/smooth presentation and VI skip settings and core options. The Dolphin IOS device reports 100% and ignores speed limits a guest asks for. - Common/FrameClock.c (C89) counts retro_run calls. Background threads that acted every so many milliseconds wait for a number of frames instead: real-Wiimote pool upkeep, scanning, write tests and reconnect retries, USB scanning, the USB Gecko and GBA link listeners, the BBA read thread, memory card flushing and the WC24 scheduler. Shutdown wakes every waiter. - Waits that polled or timed out now block until signalled: dual-core boot, PauseAndLock in the GPU loop, CPU thread jobs, shader compilation progress, BlockingLoop and ParallelFor setup and teardown. - Bluetooth passthrough submits transfers straight to libusb; the output worker and its timed sleep are deleted, HCI command timeouts count emulated time, and blocking firmware commands wait for the adapter's events. - Real Wiimote reports go out when produced; delayed ones carry a frame number. - Common::Flag sits on retro_atomic. - Deleted: SleepCurrentThread, YieldCPU, Common::SleepUntil, Event::WaitFor, BlockingLoop::WaitYield, timer resolution changes, the play-time tracker, Host_YieldToUI, FrameTiming::ThrottleFrame leftovers and the never-used DO_WAIT_FOR_WIIMOTES mode.
204 lines
7.0 KiB
C++
204 lines
7.0 KiB
C++
// Copyright 2009 Dolphin Emulator Project
|
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
|
|
#pragma once
|
|
|
|
#include <array>
|
|
#include <atomic>
|
|
#include <bit>
|
|
|
|
#include "AudioCommon/SurroundDecoder.h"
|
|
#include "AudioCommon/WaveFile.h"
|
|
#include "Common/CommonTypes.h"
|
|
#include "Common/Config/Config.h"
|
|
#include "Common/Inline.h"
|
|
|
|
class PointerWrap;
|
|
|
|
class Mixer final
|
|
{
|
|
public:
|
|
explicit Mixer(u32 BackendSampleRate);
|
|
~Mixer();
|
|
|
|
void DoState(PointerWrap& p);
|
|
|
|
// Called from audio threads
|
|
std::size_t Mix(s16* samples, std::size_t numSamples);
|
|
std::size_t MixSurround(float* samples, std::size_t num_samples);
|
|
|
|
// Called from main thread
|
|
void PushSamples(const s16* samples, std::size_t num_samples);
|
|
void PushStreamingSamples(const s16* samples, std::size_t num_samples);
|
|
void PushWiimoteSpeakerSamples(std::size_t wiimote_index, const s16* samples,
|
|
std::size_t num_samples, u32 sample_rate_divisor);
|
|
void PushSkylanderPortalSamples(const u8* samples, std::size_t num_samples);
|
|
void PushGBASamples(std::size_t device_number, const s16* samples, std::size_t num_samples);
|
|
|
|
u32 GetSampleRate() const { return m_output_sample_rate; }
|
|
void SetSampleRate(u32 output_sample_rate) { m_output_sample_rate = output_sample_rate; }
|
|
|
|
bool IsOutputSampleRateValid() const { return m_output_sample_rate != 0; }
|
|
|
|
void SetDMAInputSampleRateDivisor(u32 rate_divisor);
|
|
void SetStreamInputSampleRateDivisor(u32 rate_divisor);
|
|
void SetGBAInputSampleRate(std::size_t device_number, u32 sample_rate);
|
|
|
|
void SetStreamingVolume(u32 lvolume, u32 rvolume);
|
|
void SetWiimoteSpeakerVolume(std::size_t wiimote_index, u32 lvolume, u32 rvolume);
|
|
std::size_t MixWiimoteSpeaker(std::size_t wiimote_index, s16* samples, std::size_t num_samples);
|
|
void SetGBAVolume(std::size_t device_number, u32 lvolume, u32 rvolume);
|
|
|
|
void StartLogDTKAudio(const std::string& filename);
|
|
void StopLogDTKAudio();
|
|
|
|
void StartLogDSPAudio(const std::string& filename);
|
|
void StopLogDSPAudio();
|
|
|
|
// 54000000 doesn't work here as it doesn't evenly divide with 32000, but 108000000 does
|
|
static constexpr u64 FIXED_SAMPLE_RATE_DIVIDEND = 54000000 * 2;
|
|
|
|
private:
|
|
const std::size_t SURROUND_CHANNELS = 6;
|
|
|
|
class MixerFifo final
|
|
{
|
|
static constexpr std::size_t MAX_GRANULE_QUEUE_SIZE = 256;
|
|
static constexpr std::size_t GRANULE_QUEUE_MASK = MAX_GRANULE_QUEUE_SIZE - 1;
|
|
|
|
struct StereoPair final
|
|
{
|
|
float l = 0.f;
|
|
float r = 0.f;
|
|
|
|
constexpr StereoPair() = default;
|
|
constexpr StereoPair(const StereoPair&) = default;
|
|
constexpr StereoPair& operator=(const StereoPair&) = default;
|
|
constexpr StereoPair(StereoPair&&) = default;
|
|
constexpr StereoPair& operator=(StereoPair&&) = default;
|
|
|
|
constexpr StereoPair(float mono) : l(mono), r(mono) {}
|
|
constexpr StereoPair(float left, float right) : l(left), r(right) {}
|
|
constexpr StereoPair(s16 left, s16 right) : l(left), r(right) {}
|
|
|
|
StereoPair operator+(const StereoPair& other) const
|
|
{
|
|
return StereoPair(l + other.l, r + other.r);
|
|
}
|
|
|
|
StereoPair operator*(const StereoPair& other) const
|
|
{
|
|
return StereoPair(l * other.l, r * other.r);
|
|
}
|
|
};
|
|
|
|
static constexpr std::size_t GRANULE_SIZE = 256;
|
|
static constexpr std::size_t GRANULE_OVERLAP = GRANULE_SIZE / 2;
|
|
static constexpr std::size_t GRANULE_MASK = GRANULE_SIZE - 1;
|
|
static constexpr std::size_t GRANULE_BITS = std::countr_one(GRANULE_MASK);
|
|
static constexpr std::size_t GRANULE_FRAC_BITS = 32 - GRANULE_BITS;
|
|
|
|
using Granule = std::array<StereoPair, GRANULE_SIZE>;
|
|
|
|
public:
|
|
MixerFifo(Mixer* mixer, u32 sample_rate_divisor,
|
|
u32 sample_rate_dividend = FIXED_SAMPLE_RATE_DIVIDEND)
|
|
: m_mixer(mixer), m_input_sample_rate_dividend(sample_rate_dividend),
|
|
m_input_sample_rate_divisor(sample_rate_divisor)
|
|
{
|
|
}
|
|
void DoState(PointerWrap& p);
|
|
|
|
DOLPHIN_FORCE_INLINE void PushSample(s16 left, s16 right)
|
|
{
|
|
m_next_buffer[m_next_buffer_index] = {left, right};
|
|
m_next_buffer_index = (m_next_buffer_index + 1) & GRANULE_MASK;
|
|
|
|
// The granules overlap by 50%, so we need to enqueue the
|
|
// next buffer every time we fill half of the samples.
|
|
if (m_next_buffer_index == 0 || m_next_buffer_index == m_next_buffer.size() / 2)
|
|
Enqueue();
|
|
}
|
|
|
|
void Mix(s16* samples, std::size_t num_samples);
|
|
|
|
void SetInputSampleRateDividend(u32 rate_dividend);
|
|
u32 GetInputSampleRateDividend() const;
|
|
|
|
void SetInputSampleRateDivisor(u32 rate_divisor);
|
|
u32 GetInputSampleRateDivisor() const;
|
|
|
|
void SetVolume(u32 lvolume, u32 rvolume);
|
|
std::pair<s32, s32> GetVolume() const;
|
|
|
|
private:
|
|
Mixer* m_mixer;
|
|
|
|
// All non-GBA MixerFifo instances use FIXED_SAMPLE_RATE_DIVIDEND.
|
|
u32 m_input_sample_rate_dividend;
|
|
u32 m_input_sample_rate_divisor;
|
|
|
|
Granule m_next_buffer{};
|
|
std::size_t m_next_buffer_index = 0;
|
|
|
|
u32 m_current_index = 0;
|
|
Granule m_front, m_back;
|
|
|
|
std::atomic<std::size_t> m_granule_queue_size{20};
|
|
std::array<Granule, MAX_GRANULE_QUEUE_SIZE> m_queue;
|
|
std::atomic<std::size_t> m_queue_head{0};
|
|
std::atomic<std::size_t> m_queue_tail{0};
|
|
std::atomic<bool> m_queue_fading{false};
|
|
std::atomic<bool> m_queue_looping{false};
|
|
float m_fade_volume = 1.0;
|
|
|
|
void Enqueue();
|
|
bool Dequeue(Granule* granule);
|
|
|
|
// Volume ranges from 0-256
|
|
std::atomic<s32> m_LVolume{256};
|
|
std::atomic<s32> m_RVolume{256};
|
|
|
|
StereoPair m_quantization_error;
|
|
};
|
|
|
|
void RefreshConfig();
|
|
|
|
MixerFifo m_dma_mixer{this, FIXED_SAMPLE_RATE_DIVIDEND / 32000};
|
|
MixerFifo m_streaming_mixer{this, FIXED_SAMPLE_RATE_DIVIDEND / 48000};
|
|
std::array<MixerFifo, 4> m_wiimote_speaker_mixers{
|
|
MixerFifo{this, FIXED_SAMPLE_RATE_DIVIDEND / 3000},
|
|
MixerFifo{this, FIXED_SAMPLE_RATE_DIVIDEND / 3000},
|
|
MixerFifo{this, FIXED_SAMPLE_RATE_DIVIDEND / 3000},
|
|
MixerFifo{this, FIXED_SAMPLE_RATE_DIVIDEND / 3000},
|
|
};
|
|
MixerFifo m_skylander_portal_mixer{this, FIXED_SAMPLE_RATE_DIVIDEND / 8000};
|
|
|
|
// GBAs generally use a 65536 sample rate which is not a factor of our FIXED_SAMPLE_RATE_DIVIDEND.
|
|
static constexpr u32 GBA_SAMPLE_RATE_DIVIDEND = 0x1000000;
|
|
|
|
std::array<MixerFifo, 4> m_gba_mixers{
|
|
MixerFifo{this, GBA_SAMPLE_RATE_DIVIDEND / 65536, GBA_SAMPLE_RATE_DIVIDEND},
|
|
MixerFifo{this, GBA_SAMPLE_RATE_DIVIDEND / 65536, GBA_SAMPLE_RATE_DIVIDEND},
|
|
MixerFifo{this, GBA_SAMPLE_RATE_DIVIDEND / 65536, GBA_SAMPLE_RATE_DIVIDEND},
|
|
MixerFifo{this, GBA_SAMPLE_RATE_DIVIDEND / 65536, GBA_SAMPLE_RATE_DIVIDEND},
|
|
};
|
|
u32 m_output_sample_rate;
|
|
|
|
AudioCommon::SurroundDecoder m_surround_decoder;
|
|
|
|
WaveFileWriter m_wave_writer_dtk;
|
|
WaveFileWriter m_wave_writer_dsp;
|
|
|
|
bool m_log_dtk_audio = false;
|
|
bool m_log_dsp_audio = false;
|
|
|
|
bool m_config_audio_preserve_pitch;
|
|
bool m_config_fill_audio_gaps;
|
|
int m_config_audio_buffer_ms;
|
|
bool m_config_wiimote_routing_enabled = false;
|
|
std::array<bool, 4> m_config_wiimote_output_enabled{};
|
|
|
|
Config::ConfigChangedCallbackID m_config_changed_callback_id;
|
|
};
|