summaryrefslogtreecommitdiff
path: root/contrib/SDL-3.2.8/src/audio/dsp/SDL_dspaudio.c
diff options
context:
space:
mode:
author3gg <3gg@shellblade.net>2025-12-27 12:03:39 -0800
committer3gg <3gg@shellblade.net>2025-12-27 12:03:39 -0800
commit5a079a2d114f96d4847d1ee305d5b7c16eeec50e (patch)
tree8926ab44f168acf787d8e19608857b3af0f82758 /contrib/SDL-3.2.8/src/audio/dsp/SDL_dspaudio.c
Initial commit
Diffstat (limited to 'contrib/SDL-3.2.8/src/audio/dsp/SDL_dspaudio.c')
-rw-r--r--contrib/SDL-3.2.8/src/audio/dsp/SDL_dspaudio.c303
1 files changed, 303 insertions, 0 deletions
diff --git a/contrib/SDL-3.2.8/src/audio/dsp/SDL_dspaudio.c b/contrib/SDL-3.2.8/src/audio/dsp/SDL_dspaudio.c
new file mode 100644
index 0000000..62b8990
--- /dev/null
+++ b/contrib/SDL-3.2.8/src/audio/dsp/SDL_dspaudio.c
@@ -0,0 +1,303 @@
1/*
2 Simple DirectMedia Layer
3 Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
4
5 This software is provided 'as-is', without any express or implied
6 warranty. In no event will the authors be held liable for any damages
7 arising from the use of this software.
8
9 Permission is granted to anyone to use this software for any purpose,
10 including commercial applications, and to alter it and redistribute it
11 freely, subject to the following restrictions:
12
13 1. The origin of this software must not be misrepresented; you must not
14 claim that you wrote the original software. If you use this software
15 in a product, an acknowledgment in the product documentation would be
16 appreciated but is not required.
17 2. Altered source versions must be plainly marked as such, and must not be
18 misrepresented as being the original software.
19 3. This notice may not be removed or altered from any source distribution.
20*/
21#include "SDL_internal.h"
22
23// !!! FIXME: clean out perror and fprintf calls in here.
24
25#ifdef SDL_AUDIO_DRIVER_OSS
26
27#include <stdio.h> // For perror()
28#include <string.h> // For strerror()
29#include <errno.h>
30#include <unistd.h>
31#include <fcntl.h>
32#include <signal.h>
33#include <sys/time.h>
34#include <sys/ioctl.h>
35#include <sys/stat.h>
36
37#include <sys/soundcard.h>
38
39#include "../SDL_audiodev_c.h"
40#include "SDL_dspaudio.h"
41
42static void DSP_DetectDevices(SDL_AudioDevice **default_playback, SDL_AudioDevice **default_recording)
43{
44 SDL_EnumUnixAudioDevices(false, NULL);
45}
46
47static void DSP_CloseDevice(SDL_AudioDevice *device)
48{
49 if (device->hidden) {
50 if (device->hidden->audio_fd >= 0) {
51 close(device->hidden->audio_fd);
52 }
53 SDL_free(device->hidden->mixbuf);
54 SDL_free(device->hidden);
55 }
56}
57
58static bool DSP_OpenDevice(SDL_AudioDevice *device)
59{
60 // Make sure fragment size stays a power of 2, or OSS fails.
61 // (I don't know which of these are actually legal values, though...)
62 if (device->spec.channels > 8) {
63 device->spec.channels = 8;
64 } else if (device->spec.channels > 4) {
65 device->spec.channels = 4;
66 } else if (device->spec.channels > 2) {
67 device->spec.channels = 2;
68 }
69
70 // Initialize all variables that we clean on shutdown
71 device->hidden = (struct SDL_PrivateAudioData *) SDL_calloc(1, sizeof(*device->hidden));
72 if (!device->hidden) {
73 return false;
74 }
75
76 // Open the audio device; we hardcode the device path in `device->name` for lack of better info, so use that.
77 const int flags = ((device->recording) ? OPEN_FLAGS_INPUT : OPEN_FLAGS_OUTPUT);
78 device->hidden->audio_fd = open(device->name, flags | O_CLOEXEC, 0);
79 if (device->hidden->audio_fd < 0) {
80 return SDL_SetError("Couldn't open %s: %s", device->name, strerror(errno));
81 }
82
83 // Make the file descriptor use blocking i/o with fcntl()
84 {
85 const long ctlflags = fcntl(device->hidden->audio_fd, F_GETFL) & ~O_NONBLOCK;
86 if (fcntl(device->hidden->audio_fd, F_SETFL, ctlflags) < 0) {
87 return SDL_SetError("Couldn't set audio blocking mode");
88 }
89 }
90
91 // Get a list of supported hardware formats
92 int value;
93 if (ioctl(device->hidden->audio_fd, SNDCTL_DSP_GETFMTS, &value) < 0) {
94 perror("SNDCTL_DSP_GETFMTS");
95 return SDL_SetError("Couldn't get audio format list");
96 }
97
98 // Try for a closest match on audio format
99 int format = 0;
100 SDL_AudioFormat test_format;
101 const SDL_AudioFormat *closefmts = SDL_ClosestAudioFormats(device->spec.format);
102 while ((test_format = *(closefmts++)) != 0) {
103#ifdef DEBUG_AUDIO
104 fprintf(stderr, "Trying format 0x%4.4x\n", test_format);
105#endif
106 switch (test_format) {
107 case SDL_AUDIO_U8:
108 if (value & AFMT_U8) {
109 format = AFMT_U8;
110 }
111 break;
112 case SDL_AUDIO_S16LE:
113 if (value & AFMT_S16_LE) {
114 format = AFMT_S16_LE;
115 }
116 break;
117 case SDL_AUDIO_S16BE:
118 if (value & AFMT_S16_BE) {
119 format = AFMT_S16_BE;
120 }
121 break;
122
123 default:
124 continue;
125 }
126 break;
127 }
128 if (format == 0) {
129 return SDL_SetError("Couldn't find any hardware audio formats");
130 }
131 device->spec.format = test_format;
132
133 // Set the audio format
134 value = format;
135 if ((ioctl(device->hidden->audio_fd, SNDCTL_DSP_SETFMT, &value) < 0) ||
136 (value != format)) {
137 perror("SNDCTL_DSP_SETFMT");
138 return SDL_SetError("Couldn't set audio format");
139 }
140
141 // Set the number of channels of output
142 value = device->spec.channels;
143 if (ioctl(device->hidden->audio_fd, SNDCTL_DSP_CHANNELS, &value) < 0) {
144 perror("SNDCTL_DSP_CHANNELS");
145 return SDL_SetError("Cannot set the number of channels");
146 }
147 device->spec.channels = value;
148
149 // Set the DSP frequency
150 value = device->spec.freq;
151 if (ioctl(device->hidden->audio_fd, SNDCTL_DSP_SPEED, &value) < 0) {
152 perror("SNDCTL_DSP_SPEED");
153 return SDL_SetError("Couldn't set audio frequency");
154 }
155 device->spec.freq = value;
156
157 // Calculate the final parameters for this audio specification
158 SDL_UpdatedAudioDeviceFormat(device);
159
160 /* Determine the power of two of the fragment size
161 Since apps don't control this in SDL3, and this driver only accepts 8, 16
162 bit formats and 1, 2, 4, 8 channels, this should always be a power of 2 already. */
163 SDL_assert(SDL_powerof2(device->buffer_size) == device->buffer_size);
164
165 int frag_spec = 0;
166 while ((0x01U << frag_spec) < device->buffer_size) {
167 frag_spec++;
168 }
169 frag_spec |= 0x00020000; // two fragments, for low latency
170
171 // Set the audio buffering parameters
172#ifdef DEBUG_AUDIO
173 fprintf(stderr, "Requesting %d fragments of size %d\n",
174 (frag_spec >> 16), 1 << (frag_spec & 0xFFFF));
175#endif
176 if (ioctl(device->hidden->audio_fd, SNDCTL_DSP_SETFRAGMENT, &frag_spec) < 0) {
177 perror("SNDCTL_DSP_SETFRAGMENT");
178 }
179#ifdef DEBUG_AUDIO
180 {
181 audio_buf_info info;
182 ioctl(device->hidden->audio_fd, SNDCTL_DSP_GETOSPACE, &info);
183 fprintf(stderr, "fragments = %d\n", info.fragments);
184 fprintf(stderr, "fragstotal = %d\n", info.fragstotal);
185 fprintf(stderr, "fragsize = %d\n", info.fragsize);
186 fprintf(stderr, "bytes = %d\n", info.bytes);
187 }
188#endif
189
190 // Allocate mixing buffer
191 if (!device->recording) {
192 device->hidden->mixbuf = (Uint8 *)SDL_malloc(device->buffer_size);
193 if (!device->hidden->mixbuf) {
194 return false;
195 }
196 SDL_memset(device->hidden->mixbuf, device->silence_value, device->buffer_size);
197 }
198
199 return true; // We're ready to rock and roll. :-)
200}
201
202static bool DSP_WaitDevice(SDL_AudioDevice *device)
203{
204 const unsigned long ioctlreq = device->recording ? SNDCTL_DSP_GETISPACE : SNDCTL_DSP_GETOSPACE;
205 struct SDL_PrivateAudioData *h = device->hidden;
206
207 while (!SDL_GetAtomicInt(&device->shutdown)) {
208 audio_buf_info info;
209 const int rc = ioctl(h->audio_fd, ioctlreq, &info);
210 if (rc < 0) {
211 if (errno == EAGAIN) {
212 continue;
213 }
214 // Hmm, not much we can do - abort
215 fprintf(stderr, "dsp WaitDevice ioctl failed (unrecoverable): %s\n", strerror(errno));
216 return false;
217 } else if (info.bytes < device->buffer_size) {
218 SDL_Delay(10);
219 } else {
220 break; // ready to go!
221 }
222 }
223
224 return true;
225}
226
227static bool DSP_PlayDevice(SDL_AudioDevice *device, const Uint8 *buffer, int buflen)
228{
229 struct SDL_PrivateAudioData *h = device->hidden;
230 if (write(h->audio_fd, buffer, buflen) == -1) {
231 perror("Audio write");
232 return false;
233 }
234#ifdef DEBUG_AUDIO
235 fprintf(stderr, "Wrote %d bytes of audio data\n", h->mixlen);
236#endif
237 return true;
238}
239
240static Uint8 *DSP_GetDeviceBuf(SDL_AudioDevice *device, int *buffer_size)
241{
242 return device->hidden->mixbuf;
243}
244
245static int DSP_RecordDevice(SDL_AudioDevice *device, void *buffer, int buflen)
246{
247 return (int)read(device->hidden->audio_fd, buffer, buflen);
248}
249
250static void DSP_FlushRecording(SDL_AudioDevice *device)
251{
252 struct SDL_PrivateAudioData *h = device->hidden;
253 audio_buf_info info;
254 if (ioctl(h->audio_fd, SNDCTL_DSP_GETISPACE, &info) == 0) {
255 while (info.bytes > 0) {
256 char buf[512];
257 const size_t len = SDL_min(sizeof(buf), info.bytes);
258 const ssize_t br = read(h->audio_fd, buf, len);
259 if (br <= 0) {
260 break;
261 }
262 info.bytes -= br;
263 }
264 }
265}
266
267static bool InitTimeDevicesExist = false;
268static bool look_for_devices_test(int fd)
269{
270 InitTimeDevicesExist = true; // note that _something_ exists.
271 // Don't add to the device list, we're just seeing if any devices exist.
272 return false;
273}
274
275static bool DSP_Init(SDL_AudioDriverImpl *impl)
276{
277 InitTimeDevicesExist = false;
278 SDL_EnumUnixAudioDevices(false, look_for_devices_test);
279 if (!InitTimeDevicesExist) {
280 SDL_SetError("dsp: No such audio device");
281 return false; // maybe try a different backend.
282 }
283
284 impl->DetectDevices = DSP_DetectDevices;
285 impl->OpenDevice = DSP_OpenDevice;
286 impl->WaitDevice = DSP_WaitDevice;
287 impl->PlayDevice = DSP_PlayDevice;
288 impl->GetDeviceBuf = DSP_GetDeviceBuf;
289 impl->CloseDevice = DSP_CloseDevice;
290 impl->WaitRecordingDevice = DSP_WaitDevice;
291 impl->RecordDevice = DSP_RecordDevice;
292 impl->FlushRecording = DSP_FlushRecording;
293
294 impl->HasRecordingSupport = true;
295
296 return true;
297}
298
299AudioBootStrap DSP_bootstrap = {
300 "dsp", "Open Sound System (/dev/dsp)", DSP_Init, false, false
301};
302
303#endif // SDL_AUDIO_DRIVER_OSS