refactor(audio): enhance audio buffering and interpolation methods

- Updated audio buffering strategy to ensure smooth playback by always queuing samples and implementing dynamic rate control.
- Introduced Hermite interpolation for audio resampling, providing better quality than linear interpolation.
- Adjusted audio debug logging for improved monitoring of audio states and buffer management.

Benefits:
- Improved audio playback quality and reduced glitches.
- Enhanced clarity in audio debugging and monitoring processes.
This commit is contained in:
scawful
2025-10-11 23:47:17 -04:00
parent be4d30b208
commit 0808c52788
7 changed files with 142 additions and 73 deletions

View File

@@ -648,34 +648,43 @@ inline int16_t InterpolateLinear(int16_t s0, int16_t s1, double frac) {
return static_cast<int16_t>(s0 + frac * (s1 - s0));
}
// Helper for Hermite interpolation (used by bsnes/Snes9x)
// Provides smoother interpolation than linear with minimal overhead
inline int16_t InterpolateHermite(int16_t p0, int16_t p1, int16_t p2, int16_t p3, double t) {
const double c0 = p1;
const double c1 = (p2 - p0) * 0.5;
const double c2 = p0 - 2.5 * p1 + 2.0 * p2 - 0.5 * p3;
const double c3 = (p3 - p0) * 0.5 + 1.5 * (p1 - p2);
const double result = c0 + c1 * t + c2 * t * t + c3 * t * t * t;
// Clamp to 16-bit range
return result > 32767.0 ? 32767
: (result < -32768.0 ? -32768
: static_cast<int16_t>(result));
}
void Dsp::GetSamples(int16_t* sample_data, int samples_per_frame,
bool pal_timing) {
// Resample from native samples-per-frame (NTSC: ~534, PAL: ~641)
const double native_per_frame = pal_timing ? 641.0 : 534.0;
const double step = native_per_frame / static_cast<double>(samples_per_frame);
// Start reading one native frame behind the frame boundary
double location = static_cast<double>((lastFrameBoundary + 0x400) & 0x3ff);
location -= native_per_frame;
// Ensure location is within valid range
while (location < 0) location += 0x400;
for (int i = 0; i < samples_per_frame; i++) {
const int idx = static_cast<int>(location);
const double frac = location - idx;
const int idx = static_cast<int>(location) & 0x3ff;
const double frac = location - static_cast<int>(location);
switch (interpolation_type) {
case InterpolationType::Linear: {
// const int next_idx = (idx + 1) & 0x3ff;
// const int16_t s0_l = sampleBuffer[(idx * 2) + 0];
// const int16_t s1_l = sampleBuffer[(next_idx * 2) + 0];
// sample_data[(i * 2) + 0] = InterpolateLinear(s0_l, s1_l, frac);
// const int16_t s0_r = sampleBuffer[(idx * 2) + 1];
// const int16_t s1_r = sampleBuffer[(next_idx * 2) + 1];
// sample_data[(i * 2) + 1] = InterpolateLinear(s0_r, s1_r, frac);
const int idx = static_cast<int>(location) & 0x3ff;
const int next_idx = (idx + 1) & 0x3ff;
// Calculate interpolation factor (0.0 to 1.0)
const double frac = location - static_cast<int>(location);
// Linear interpolation for left channel
const int16_t s0_l = sampleBuffer[(idx * 2) + 0];
const int16_t s1_l = sampleBuffer[(next_idx * 2) + 0];
@@ -687,9 +696,25 @@ void Dsp::GetSamples(int16_t* sample_data, int samples_per_frame,
const int16_t s1_r = sampleBuffer[(next_idx * 2) + 1];
sample_data[(i * 2) + 1] = static_cast<int16_t>(
s0_r + frac * (s1_r - s0_r));
// location += step;
break;
}
case InterpolationType::Hermite: {
const int idx0 = (idx - 1 + 0x400) & 0x3ff;
const int idx1 = idx & 0x3ff;
const int idx2 = (idx + 1) & 0x3ff;
const int idx3 = (idx + 2) & 0x3ff;
// Left channel
const int16_t p0_l = sampleBuffer[(idx0 * 2) + 0];
const int16_t p1_l = sampleBuffer[(idx1 * 2) + 0];
const int16_t p2_l = sampleBuffer[(idx2 * 2) + 0];
const int16_t p3_l = sampleBuffer[(idx3 * 2) + 0];
sample_data[(i * 2) + 0] = InterpolateHermite(p0_l, p1_l, p2_l, p3_l, frac);
// Right channel
const int16_t p0_r = sampleBuffer[(idx0 * 2) + 1];
const int16_t p1_r = sampleBuffer[(idx1 * 2) + 1];
const int16_t p2_r = sampleBuffer[(idx2 * 2) + 1];
const int16_t p3_r = sampleBuffer[(idx3 * 2) + 1];
sample_data[(i * 2) + 1] = InterpolateHermite(p0_r, p1_r, p2_r, p3_r, frac);
break;
}
case InterpolationType::Cosine: {
@@ -725,7 +750,6 @@ void Dsp::GetSamples(int16_t* sample_data, int samples_per_frame,
}
}
location += step;
}
}