sdl audio: use SDL to convert from 44.1 to 48 kHz

Fix crash introduced by 7205068809
Use SDL_ConvertAudio to convert audio when needed.

Fix for MINIDUMP-3K
This commit is contained in:
Flyinghead 2023-01-15 11:21:42 +01:00
parent 8aadf5b097
commit 2b37a5dc08
1 changed files with 37 additions and 37 deletions

View File

@ -13,24 +13,15 @@ class SDLAudioBackend : AudioBackend
bool needs_resampling = false;
cResetEvent read_wait;
std::mutex stream_mutex;
struct {
uint32_t prevs;
uint32_t *sample_buffer;
} audiobuf;
uint32_t *sample_buffer;
unsigned sample_count = 0;
SDL_AudioCVT audioCvt;
SDL_AudioDeviceID recorddev {};
u8 recordbuf[480 * 4];
std::atomic<size_t> rec_read;
std::atomic<size_t> rec_write;
// To easily access samples.
union Sample { int16_t s[2]; uint32_t l; };
static float InterpolateCatmull4pt3oX(float x0, float x1, float x2, float x3, float t) {
return 0.45 * ((2 * x1) + t * ((-x0 + x2) + t * ((2 * x0 - 5 * x1 + 4 * x2 - x3) + t * (-x0 + 3 * x1 - 3 * x2 + x3))));
}
static void audioCallback(void* userdata, Uint8* stream, int len)
{
SDLAudioBackend *backend = (SDLAudioBackend *)userdata;
@ -38,10 +29,10 @@ class SDLAudioBackend : AudioBackend
backend->stream_mutex.lock();
// Wait until there's enough samples to feed the kraken
unsigned oslen = len / sizeof(uint32_t);
unsigned islen = backend->needs_resampling ? oslen * 16 / 17 : oslen;
unsigned minlen = backend->needs_resampling ? islen + 2 : islen; // Resampler looks ahead by 2 samples.
unsigned islen = backend->needs_resampling ? std::ceil(oslen / backend->audioCvt.len_ratio) : oslen;
if (backend->sample_count < minlen) {
if (backend->sample_count < islen)
{
// No data, just output a bit of silence for the underrun
memset(stream, 0, len);
backend->stream_mutex.unlock();
@ -51,28 +42,19 @@ class SDLAudioBackend : AudioBackend
if (!backend->needs_resampling) {
// Just copy bytes for this case.
memcpy(stream, &backend->audiobuf.sample_buffer[0], len);
memcpy(stream, &backend->sample_buffer[0], len);
}
else {
// 44.1KHz to 48KHz (actually 46.86KHz) resampling
uint32_t *outbuf = (uint32_t*)stream;
const float ra = 1.0f / 17;
Sample *sbuf = (Sample*)&backend->audiobuf.sample_buffer[0]; // [-1] stores the previous iteration last sample output
for (u32 i = 0; i < islen/16; i++) {
*outbuf++ = sbuf[i*16+ 0].l; // First sample stays at the same location.
for (int k = 1; k < 17; k++) {
Sample r;
// Note we access offset -1 on first iteration, as to access prevs
r.s[0] = InterpolateCatmull4pt3oX(sbuf[i*16+k-2].s[0], sbuf[i*16+k-1].s[0], sbuf[i*16+k].s[0], sbuf[i*16+k+1].s[0], 1 - ra*k);
r.s[1] = InterpolateCatmull4pt3oX(sbuf[i*16+k-2].s[1], sbuf[i*16+k-1].s[1], sbuf[i*16+k].s[1], sbuf[i*16+k+1].s[1], 1 - ra*k);
*outbuf++ = r.l;
}
}
backend->audiobuf.prevs = backend->audiobuf.sample_buffer[islen-1];
else
{
SDL_AudioCVT& cvt = backend->audioCvt;
cvt.len = islen * sizeof(uint32_t);
memcpy(cvt.buf, &backend->sample_buffer[0], cvt.len);
SDL_ConvertAudio(&cvt);
memcpy(stream, cvt.buf, cvt.len_cvt);
}
// Move samples in the buffer and consume them
memmove(&backend->audiobuf.sample_buffer[0], &backend->audiobuf.sample_buffer[islen], (backend->sample_count-islen)*sizeof(uint32_t));
memmove(&backend->sample_buffer[0], &backend->sample_buffer[islen], (backend->sample_count-islen)*sizeof(uint32_t));
backend->sample_count -= islen;
backend->stream_mutex.unlock();
@ -93,7 +75,7 @@ public:
}
}
audiobuf.sample_buffer = new uint32_t[config::AudioBufferSize]();
sample_buffer = new uint32_t[config::AudioBufferSize]();
// Support 44.1KHz (native) but also upsampling to 48KHz
SDL_AudioSpec wav_spec, out_spec;
@ -109,14 +91,27 @@ public:
audiodev = SDL_OpenAudioDevice(NULL, 0, &wav_spec, &out_spec, 0);
if (audiodev == 0)
{
WARN_LOG(AUDIO, "SDL2: SDL_OpenAudioDevice failed: %s", SDL_GetError());
INFO_LOG(AUDIO, "SDL2: SDL_OpenAudioDevice failed: %s", SDL_GetError());
needs_resampling = true;
wav_spec.freq = 48000;
audiodev = SDL_OpenAudioDevice(NULL, 0, &wav_spec, &out_spec, 0);
if (audiodev == 0)
ERROR_LOG(AUDIO, "SDL2: SDL_OpenAudioDevice failed: %s", SDL_GetError());
else
{
INFO_LOG(AUDIO, "SDL2: Using resampling to 48 KHz");
int ret = SDL_BuildAudioCVT(&audioCvt, AUDIO_S16, 2, 44100, AUDIO_S16, 2, 48000);
if (ret != 1 || audioCvt.needed == 0)
{
ERROR_LOG(AUDIO, "SDL2: can't build audio converter: %s", SDL_GetError());
SDL_CloseAudioDevice(audiodev);
audiodev = 0;
}
else
{
audioCvt.buf = new u8[SAMPLE_COUNT * 2 * sizeof(uint32_t) * audioCvt.len_mult];
}
}
}
return audiodev != 0;
@ -141,7 +136,7 @@ public:
// Copy as many samples as possible, drop any remaining (this should not happen usually)
unsigned free_samples = config::AudioBufferSize - sample_count;
unsigned tocopy = samples < free_samples ? samples : free_samples;
memcpy(&audiobuf.sample_buffer[sample_count], frame, tocopy * sizeof(uint32_t));
memcpy(&sample_buffer[sample_count], frame, tocopy * sizeof(uint32_t));
sample_count += tocopy;
stream_mutex.unlock();
@ -158,8 +153,13 @@ public:
SDL_CloseAudioDevice(audiodev);
audiodev = SDL_AudioDeviceID();
}
delete [] audiobuf.sample_buffer;
audiobuf.sample_buffer = nullptr;
delete [] sample_buffer;
sample_buffer = nullptr;
if (needs_resampling)
{
delete [] audioCvt.buf;
audioCvt.buf = nullptr;
}
}
static void recordCallback(void *userdata, u8 *stream, int len)