diff options
Diffstat (limited to 'contrib/SDL-3.2.8/src/audio/dsp')
| -rw-r--r-- | contrib/SDL-3.2.8/src/audio/dsp/SDL_dspaudio.c | 303 | ||||
| -rw-r--r-- | contrib/SDL-3.2.8/src/audio/dsp/SDL_dspaudio.h | 37 |
2 files changed, 340 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 | |||
| 42 | static void DSP_DetectDevices(SDL_AudioDevice **default_playback, SDL_AudioDevice **default_recording) | ||
| 43 | { | ||
| 44 | SDL_EnumUnixAudioDevices(false, NULL); | ||
| 45 | } | ||
| 46 | |||
| 47 | static 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 | |||
| 58 | static 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 | |||
| 202 | static 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 | |||
| 227 | static 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 | |||
| 240 | static Uint8 *DSP_GetDeviceBuf(SDL_AudioDevice *device, int *buffer_size) | ||
| 241 | { | ||
| 242 | return device->hidden->mixbuf; | ||
| 243 | } | ||
| 244 | |||
| 245 | static int DSP_RecordDevice(SDL_AudioDevice *device, void *buffer, int buflen) | ||
| 246 | { | ||
| 247 | return (int)read(device->hidden->audio_fd, buffer, buflen); | ||
| 248 | } | ||
| 249 | |||
| 250 | static 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 | |||
| 267 | static bool InitTimeDevicesExist = false; | ||
| 268 | static 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 | |||
| 275 | static 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 | |||
| 299 | AudioBootStrap DSP_bootstrap = { | ||
| 300 | "dsp", "Open Sound System (/dev/dsp)", DSP_Init, false, false | ||
| 301 | }; | ||
| 302 | |||
| 303 | #endif // SDL_AUDIO_DRIVER_OSS | ||
diff --git a/contrib/SDL-3.2.8/src/audio/dsp/SDL_dspaudio.h b/contrib/SDL-3.2.8/src/audio/dsp/SDL_dspaudio.h new file mode 100644 index 0000000..65dea60 --- /dev/null +++ b/contrib/SDL-3.2.8/src/audio/dsp/SDL_dspaudio.h | |||
| @@ -0,0 +1,37 @@ | |||
| 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 | #ifndef SDL_dspaudio_h_ | ||
| 24 | #define SDL_dspaudio_h_ | ||
| 25 | |||
| 26 | #include "../SDL_sysaudio.h" | ||
| 27 | |||
| 28 | struct SDL_PrivateAudioData | ||
| 29 | { | ||
| 30 | // The file descriptor for the audio device | ||
| 31 | int audio_fd; | ||
| 32 | |||
| 33 | // Raw mixing buffer | ||
| 34 | Uint8 *mixbuf; | ||
| 35 | }; | ||
| 36 | |||
| 37 | #endif // SDL_dspaudio_h_ | ||
