Skip to content
This repository was archived by the owner on Sep 29, 2025. It is now read-only.

Commit 15c7e98

Browse files
authored
Merge pull request #1585 from davidgfnet/sdl2audio
Add SDL2 audio backend.
2 parents 3cd9736 + ed8bcd7 commit 15c7e98

File tree

2 files changed

+142
-0
lines changed

2 files changed

+142
-0
lines changed

core/oslib/audiobackend_sdl2.cpp

Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
2+
#if defined(USE_SDL_AUDIO)
3+
4+
#include <SDL2/SDL.h>
5+
#include "oslib/audiostream.h"
6+
#include "stdclass.h"
7+
8+
static SDL_AudioDeviceID audiodev;
9+
static bool needs_resampling;
10+
static cResetEvent read_wait;
11+
static cMutex stream_mutex;
12+
static struct {
13+
uint32_t prevs;
14+
uint32_t sample_buffer[2048];
15+
} audiobuf;
16+
static unsigned sample_count = 0;
17+
18+
// To easily access samples.
19+
union Sample { int16_t s[2]; uint32_t l; };
20+
21+
static float InterpolateCatmull4pt3oX(float x0, float x1, float x2, float x3, float t) {
22+
return 0.45 * ((2 * x1) + t * ((-x0 + x2) + t * ((2 * x0 - 5 * x1 + 4 * x2 - x3) + t * (-x0 + 3 * x1 - 3 * x2 + x3))));
23+
}
24+
25+
static void sdl2_audiocb(void* userdata, Uint8* stream, int len) {
26+
stream_mutex.Lock();
27+
// Wait until there's enough samples to feed the kraken
28+
unsigned oslen = len / sizeof(uint32_t);
29+
unsigned islen = needs_resampling ? oslen * 16 / 17 : oslen;
30+
unsigned minlen = needs_resampling ? islen + 2 : islen; // Resampler looks ahead by 2 samples.
31+
32+
if (sample_count < minlen) {
33+
// No data, just output a bit of silence for the underrun
34+
memset(stream, 0, len);
35+
stream_mutex.Unlock();
36+
read_wait.Set();
37+
return;
38+
}
39+
40+
if (!needs_resampling) {
41+
// Just copy bytes for this case.
42+
memcpy(stream, &audiobuf.sample_buffer[0], len);
43+
}
44+
else {
45+
// 44.1KHz to 48KHz (actually 46.86KHz) resampling
46+
uint32_t *outbuf = (uint32_t*)stream;
47+
const float ra = 1.0f / 17;
48+
Sample *sbuf = (Sample*)&audiobuf.sample_buffer[0]; // [-1] stores the previous iteration last sample output
49+
for (int i = 0; i < islen/16; i++) {
50+
*outbuf++ = sbuf[i*16+ 0].l; // First sample stays at the same location.
51+
for (int k = 1; k < 17; k++) {
52+
Sample r;
53+
// Note we access offset -1 on first iteration, as to access prevs
54+
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);
55+
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);
56+
*outbuf++ = r.l;
57+
}
58+
}
59+
audiobuf.prevs = audiobuf.sample_buffer[islen-1];
60+
}
61+
62+
// Move samples in the buffer and consume them
63+
memmove(&audiobuf.sample_buffer[0], &audiobuf.sample_buffer[islen], (sample_count-islen)*sizeof(uint32_t));
64+
sample_count -= islen;
65+
66+
stream_mutex.Unlock();
67+
read_wait.Set();
68+
}
69+
70+
static void sdl2_audio_init() {
71+
if (!SDL_WasInit(SDL_INIT_AUDIO))
72+
SDL_InitSubSystem(SDL_INIT_AUDIO);
73+
74+
// Support 44.1KHz (native) but also upsampling to 48KHz
75+
SDL_AudioSpec wav_spec, out_spec;
76+
memset(&wav_spec, 0, sizeof(wav_spec));
77+
wav_spec.freq = 44100;
78+
wav_spec.format = AUDIO_S16;
79+
wav_spec.channels = 2;
80+
wav_spec.samples = 1024; // Must be power of two
81+
wav_spec.callback = sdl2_audiocb;
82+
83+
// Try 44.1KHz which should be faster since it's native.
84+
audiodev = SDL_OpenAudioDevice(NULL, 0, &wav_spec, &out_spec, 0);
85+
if (!audiodev) {
86+
needs_resampling = true;
87+
wav_spec.freq = 48000;
88+
audiodev = SDL_OpenAudioDevice(NULL, 0, &wav_spec, &out_spec, 0);
89+
verify(audiodev);
90+
}
91+
}
92+
93+
static u32 sdl2_audio_push(void* frame, u32 samples, bool wait) {
94+
// Unpause the device shall it be paused.
95+
if (SDL_GetAudioDeviceStatus(audiodev) != SDL_AUDIO_PLAYING)
96+
SDL_PauseAudioDevice(audiodev, 0);
97+
98+
// If wait, then wait for the buffer to be smaller than a certain size.
99+
stream_mutex.Lock();
100+
if (wait) {
101+
while (sample_count + samples > sizeof(audiobuf.sample_buffer)/sizeof(audiobuf.sample_buffer[0])) {
102+
stream_mutex.Unlock();
103+
read_wait.Wait();
104+
read_wait.Reset();
105+
stream_mutex.Lock();
106+
}
107+
}
108+
109+
// Copy as many samples as possible, drop any remaining (this should not happen usually)
110+
unsigned free_samples = sizeof(audiobuf.sample_buffer) / sizeof(audiobuf.sample_buffer[0]) - sample_count;
111+
unsigned tocopy = samples < free_samples ? samples : free_samples;
112+
memcpy(&audiobuf.sample_buffer[sample_count], frame, tocopy * sizeof(uint32_t));
113+
sample_count += tocopy;
114+
stream_mutex.Unlock();
115+
116+
return 1;
117+
}
118+
119+
static void sdl2_audio_term() {
120+
// Stop audio playback.
121+
SDL_PauseAudioDevice(audiodev, 1);
122+
read_wait.Set();
123+
}
124+
125+
audiobackend_t audiobackend_sdl2audio = {
126+
"sdl2", // Slug
127+
"Simple DirectMedia Layer 2 Audio", // Name
128+
&sdl2_audio_init,
129+
&sdl2_audio_push,
130+
&sdl2_audio_term
131+
};
132+
133+
static bool sdl2audiobe = RegisterAudioBackend(&audiobackend_sdl2audio);
134+
135+
#endif
136+

shell/linux/Makefile

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ FOR_LINUX :=1
66
WEBUI :=1
77
USE_OSS := 1
88
#USE_PULSEAUDIO := 1
9+
#USE_SDLAUDIO := 1
910
#USE_LIBAO := 1
1011
USE_EVDEV := 1
1112
USE_UDEV := 1
@@ -360,6 +361,11 @@ ifdef USE_PULSEAUDIO
360361
LIBS += `pkg-config --libs libpulse-simple`
361362
endif
362363

364+
ifdef USE_SDLAUDIO
365+
CXXFLAGS += `sdl2-config --cflags` -D USE_SDL_AUDIO
366+
LIBS += `sdl2-config --libs`
367+
endif
368+
363369
ifdef USE_LIBAO
364370
CXXFLAGS += `pkg-config --cflags ao` -D USE_LIBAO
365371
LIBS += `pkg-config --libs ao`

0 commit comments

Comments
 (0)