mirror of
https://github.com/hrydgard/ppsspp.git
synced 2026-10-01 14:58:14 +00:00
Granular queue: Use the current FPS to get an estimate for how long the maximum queue size should be.
This commit is contained in:
1 parent
3b683a8e43
commit
e00ef04792
4 files changed
+26
-9
No files matched your search
@@ -75,11 +75,12 @@ GranularMixer::GranularMixer() {
|
||||
}
|
||||
|
||||
// Executed from sound stream thread
|
||||
void GranularMixer::Mix(s16 *samples, u32 num_samples, int outSampleRate) {
|
||||
void GranularMixer::Mix(s16 *samples, u32 num_samples, int outSampleRate, float fpsEstimate) {
|
||||
_dbg_assert_(samples);
|
||||
if (!samples)
|
||||
return;
|
||||
memset(samples, 0, num_samples * 2 * sizeof(s16));
|
||||
frameTimeEstimate_ = 1.0f / fpsEstimate;
|
||||
|
||||
smoothedReadSize_ = smoothedReadSize_ == 0 ? num_samples : (smoothedReadSize_ * 0.95f + num_samples * 0.05f);
|
||||
|
||||
@@ -89,14 +90,14 @@ void GranularMixer::Mix(s16 *samples, u32 num_samples, int outSampleRate) {
|
||||
|
||||
// We need at least a double because the index jump has 24 bits of fractional precision.
|
||||
const double out_sample_rate = outSampleRate;
|
||||
double in_sample_rate = 44100;
|
||||
double inSampleRate = 44100;
|
||||
|
||||
const double emulation_speed = 1.0f; // TODO: Change when we're in slow-motion mode etc.
|
||||
if (0 < emulation_speed && emulation_speed != 1.0)
|
||||
in_sample_rate *= emulation_speed;
|
||||
inSampleRate *= emulation_speed;
|
||||
|
||||
const double base = static_cast<double>(1 << GRANULE_FRAC_BITS);
|
||||
const u32 index_jump = std::lround(base * in_sample_rate / out_sample_rate);
|
||||
const u32 index_jump = std::lround(base * inSampleRate / out_sample_rate);
|
||||
|
||||
// These fade in / out multiplier are tuned to match a constant
|
||||
// fade speed regardless of the input or the output sample rate.
|
||||
@@ -108,8 +109,9 @@ void GranularMixer::Mix(s16 *samples, u32 num_samples, int outSampleRate) {
|
||||
// in a burst each frame (since we can't force real clock sync). That means 16*3 = 48 or rather 50ms.
|
||||
// However, in case of faster framerates, we should apply some pressure to reduce this. And if real clock sync
|
||||
// is on, we should also be able to get away with a shorter buffer here.
|
||||
const u32 buffer_size_ms = 100;
|
||||
const u32 buffer_size_samples = std::llround(buffer_size_ms * in_sample_rate / 1000.0);
|
||||
// const u32 buffer_size_ms = frameTimeEstimate_ * 44100.0f;
|
||||
const u32 buffer_size_samples = smoothedReadSize_ * 4 + std::llround(frameTimeEstimate_ * inSampleRate);
|
||||
queuedSamplesTarget_ = buffer_size_samples;
|
||||
|
||||
// Limit the possible queue sizes to any number between 4 and 64.
|
||||
const u32 buffer_size_granules =
|
||||
@@ -298,6 +300,8 @@ void GranularMixer::GetStats(GranularStats *stats) {
|
||||
stats->overruns = overruns_;
|
||||
stats->underruns = underruns_;
|
||||
stats->smoothedReadSize = smoothedReadSize_;
|
||||
stats->frameTimeEstimate = frameTimeEstimate_;
|
||||
stats->queuedSamplesTarget = queuedSamplesTarget_;
|
||||
queuedGranulesMin_ = 10000;
|
||||
queuedGranulesMax_ = 0;
|
||||
}
|
||||
@@ -26,6 +26,7 @@ constexpr u32 countr_one_replacement(u32 x) {
|
||||
struct GranularStats {
|
||||
int queuedGranulesMin;
|
||||
int queuedGranulesMax;
|
||||
int queuedSamplesTarget;
|
||||
float smoothedQueuedGranules;
|
||||
int targetQueueSize;
|
||||
int maxQueuedGranules;
|
||||
@@ -34,6 +35,7 @@ struct GranularStats {
|
||||
int overruns;
|
||||
int underruns;
|
||||
float smoothedReadSize;
|
||||
float frameTimeEstimate;
|
||||
};
|
||||
|
||||
class GranularMixer final {
|
||||
@@ -41,10 +43,10 @@ public:
|
||||
explicit GranularMixer();
|
||||
|
||||
// Called from audio threads
|
||||
void Mix(s16* samples, u32 numSamples, int outSampleRate);
|
||||
void Mix(s16 *samples, u32 numSamples, int outSampleRate, float vpsEstimate);
|
||||
|
||||
// Called from emulation thread
|
||||
void PushSamples(const s32* samples, u32 num_samples, float volume);
|
||||
void PushSamples(const s32 *samples, u32 num_samples, float volume);
|
||||
|
||||
void GetStats(GranularStats *stats);
|
||||
|
||||
@@ -105,7 +107,9 @@ private:
|
||||
int overruns_ = 0;
|
||||
int queuedGranulesMin_ = 10000;
|
||||
int queuedGranulesMax_ = 0;
|
||||
int queuedSamplesTarget_ = 0;
|
||||
float smoothedReadSize_ = 0.0f;
|
||||
float frameTimeEstimate_ = 0.0f;
|
||||
|
||||
void Enqueue();
|
||||
void Dequeue(Granule* granule);
|
||||
|
||||
+8
-1
@@ -21,6 +21,7 @@
|
||||
#include "Core/HW/GranularMixer.h"
|
||||
#include "UI/AudioCommon.h"
|
||||
#include "UI/BackgroundAudio.h"
|
||||
#include "Core/HW/Display.h"
|
||||
|
||||
StereoResampler g_resampler;
|
||||
GranularMixer g_granular;
|
||||
@@ -30,7 +31,13 @@ GranularMixer g_granular;
|
||||
void NativeMix(int16_t *outStereo, int numFrames, int sampleRateHz, void *userdata) {
|
||||
// Mix UI sound effects on top.
|
||||
if (g_Config.iAudioSyncMode == (int)AudioSyncMode::GRANULAR) {
|
||||
g_granular.Mix(outStereo, numFrames, sampleRateHz);
|
||||
// We use the FPS estimate, because to maintain smooth audio even though our
|
||||
// frame execution is very front (or back) heavy (as we can't count on "real time clock sync"
|
||||
// to be enabled), we need at least one whole frame buffered, plus a bit of extra.
|
||||
float fpsEstimate, vps, actualFps;
|
||||
__DisplayGetFPS(&vps, &fpsEstimate, &actualFps);
|
||||
|
||||
g_granular.Mix(outStereo, numFrames, sampleRateHz, fpsEstimate);
|
||||
} else {
|
||||
g_resampler.Mix(outStereo, numFrames, false, sampleRateHz);
|
||||
}
|
||||
|
||||
@@ -1353,6 +1353,8 @@ void DrawAudioOut(ImConfig &cfg, ImControl &control) {
|
||||
g_granular.GetStats(&stats);
|
||||
ImGui::Text("Granules");
|
||||
ImGui::Text("Read size: %0.2f", stats.smoothedReadSize);
|
||||
ImGui::Text("Frame time estimate: %0.1f ms", stats.frameTimeEstimate * 1000.0f);
|
||||
ImGui::Text("Queued samples target: %d", stats.queuedSamplesTarget);
|
||||
ImGui::Text("Queued min/max: %d / %d", stats.queuedGranulesMin, stats.queuedGranulesMax);
|
||||
ImGui::Text("Queued (smooth): %0.3f", stats.smoothedQueuedGranules);
|
||||
ImGui::Text("Target: %d", stats.targetQueueSize);
|
||||
|
||||
Reference in new issue
Block a user