Atlas - SDL_sndioaudio.c
Home / ext / SDL / src / audio / sndio Lines: 2 | Size: 11020 bytes [Download] [Show on GitHub] [Search similar files] [Raw] [Raw (proxy)][FILE BEGIN]1/* 2 Simple DirectMedia Layer 3 Copyright (C) 1997-2025 Sam Lantinga <[email protected]> 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 22#include "SDL_internal.h" 23 24#ifdef SDL_AUDIO_DRIVER_SNDIO 25 26// OpenBSD sndio target 27 28#ifdef HAVE_STDIO_H 29#include <stdio.h> 30#endif 31 32#ifdef HAVE_SIGNAL_H 33#include <signal.h> 34#endif 35 36#include <poll.h> 37#include <unistd.h> 38 39#include "../SDL_sysaudio.h" 40#include "SDL_sndioaudio.h" 41 42#ifdef SDL_AUDIO_DRIVER_SNDIO_DYNAMIC 43#endif 44 45#ifndef INFTIM 46#define INFTIM -1 47#endif 48 49#ifndef SIO_DEVANY 50#define SIO_DEVANY "default" 51#endif 52 53static struct sio_hdl *(*SNDIO_sio_open)(const char *, unsigned int, int); 54static void (*SNDIO_sio_close)(struct sio_hdl *); 55static int (*SNDIO_sio_setpar)(struct sio_hdl *, struct sio_par *); 56static int (*SNDIO_sio_getpar)(struct sio_hdl *, struct sio_par *); 57static int (*SNDIO_sio_start)(struct sio_hdl *); 58static int (*SNDIO_sio_stop)(struct sio_hdl *); 59static size_t (*SNDIO_sio_read)(struct sio_hdl *, void *, size_t); 60static size_t (*SNDIO_sio_write)(struct sio_hdl *, const void *, size_t); 61static int (*SNDIO_sio_nfds)(struct sio_hdl *); 62static int (*SNDIO_sio_pollfd)(struct sio_hdl *, struct pollfd *, int); 63static int (*SNDIO_sio_revents)(struct sio_hdl *, struct pollfd *); 64static int (*SNDIO_sio_eof)(struct sio_hdl *); 65static void (*SNDIO_sio_initpar)(struct sio_par *); 66 67#ifdef SDL_AUDIO_DRIVER_SNDIO_DYNAMIC 68static const char *sndio_library = SDL_AUDIO_DRIVER_SNDIO_DYNAMIC; 69static SDL_SharedObject *sndio_handle = NULL; 70 71static bool load_sndio_sym(const char *fn, void **addr) 72{ 73 *addr = SDL_LoadFunction(sndio_handle, fn); 74 if (!*addr) { 75 return false; // Don't call SDL_SetError(): SDL_LoadFunction already did. 76 } 77 78 return true; 79} 80 81// cast funcs to char* first, to please GCC's strict aliasing rules. 82#define SDL_SNDIO_SYM(x) \ 83 if (!load_sndio_sym(#x, (void **)(char *)&SNDIO_##x)) \ 84 return false 85#else 86#define SDL_SNDIO_SYM(x) SNDIO_##x = x 87#endif 88 89static bool load_sndio_syms(void) 90{ 91 SDL_SNDIO_SYM(sio_open); 92 SDL_SNDIO_SYM(sio_close); 93 SDL_SNDIO_SYM(sio_setpar); 94 SDL_SNDIO_SYM(sio_getpar); 95 SDL_SNDIO_SYM(sio_start); 96 SDL_SNDIO_SYM(sio_stop); 97 SDL_SNDIO_SYM(sio_read); 98 SDL_SNDIO_SYM(sio_write); 99 SDL_SNDIO_SYM(sio_nfds); 100 SDL_SNDIO_SYM(sio_pollfd); 101 SDL_SNDIO_SYM(sio_revents); 102 SDL_SNDIO_SYM(sio_eof); 103 SDL_SNDIO_SYM(sio_initpar); 104 return true; 105} 106 107#undef SDL_SNDIO_SYM 108 109#ifdef SDL_AUDIO_DRIVER_SNDIO_DYNAMIC 110 111SDL_ELF_NOTE_DLOPEN( 112 "audio-libsndio", 113 "Support for audio through libsndio", 114 SDL_ELF_NOTE_DLOPEN_PRIORITY_SUGGESTED, 115 SDL_AUDIO_DRIVER_SNDIO_DYNAMIC 116) 117 118static void UnloadSNDIOLibrary(void) 119{ 120 if (sndio_handle) { 121 SDL_UnloadObject(sndio_handle); 122 sndio_handle = NULL; 123 } 124} 125 126static bool LoadSNDIOLibrary(void) 127{ 128 bool result = true; 129 if (!sndio_handle) { 130 sndio_handle = SDL_LoadObject(sndio_library); 131 if (!sndio_handle) { 132 result = false; // Don't call SDL_SetError(): SDL_LoadObject already did. 133 } else { 134 result = load_sndio_syms(); 135 if (!result) { 136 UnloadSNDIOLibrary(); 137 } 138 } 139 } 140 return result; 141} 142 143#else 144 145static void UnloadSNDIOLibrary(void) 146{ 147} 148 149static bool LoadSNDIOLibrary(void) 150{ 151 load_sndio_syms(); 152 return true; 153} 154 155#endif // SDL_AUDIO_DRIVER_SNDIO_DYNAMIC 156 157static bool SNDIO_WaitDevice(SDL_AudioDevice *device) 158{ 159 const bool recording = device->recording; 160 161 while (!SDL_GetAtomicInt(&device->shutdown)) { 162 if (SNDIO_sio_eof(device->hidden->dev)) { 163 return false; 164 } 165 166 const int nfds = SNDIO_sio_pollfd(device->hidden->dev, device->hidden->pfd, recording ? POLLIN : POLLOUT); 167 if (nfds <= 0 || poll(device->hidden->pfd, nfds, 10) < 0) { 168 return false; 169 } 170 171 const int revents = SNDIO_sio_revents(device->hidden->dev, device->hidden->pfd); 172 if (recording && (revents & POLLIN)) { 173 break; 174 } else if (!recording && (revents & POLLOUT)) { 175 break; 176 } else if (revents & POLLHUP) { 177 return false; 178 } 179 } 180 181 return true; 182} 183 184static bool SNDIO_PlayDevice(SDL_AudioDevice *device, const Uint8 *buffer, int buflen) 185{ 186 // !!! FIXME: this should be non-blocking so we can check device->shutdown. 187 // this is set to blocking, because we _have_ to send the entire buffer down, but hopefully WaitDevice took most of the delay time. 188 if (SNDIO_sio_write(device->hidden->dev, buffer, buflen) != buflen) { 189 return false; // If we couldn't write, assume fatal error for now 190 } 191#ifdef DEBUG_AUDIO 192 fprintf(stderr, "Wrote %d bytes of audio data\n", written); 193#endif 194 return true; 195} 196 197static int SNDIO_RecordDevice(SDL_AudioDevice *device, void *buffer, int buflen) 198{ 199 // We set recording devices non-blocking; this can safely return 0 in SDL3, but we'll check for EOF to cause a device disconnect. 200 const size_t br = SNDIO_sio_read(device->hidden->dev, buffer, buflen); 201 if ((br == 0) && SNDIO_sio_eof(device->hidden->dev)) { 202 return -1; 203 } 204 return (int) br; 205} 206 207static void SNDIO_FlushRecording(SDL_AudioDevice *device) 208{ 209 char buf[512]; 210 while (!SDL_GetAtomicInt(&device->shutdown) && (SNDIO_sio_read(device->hidden->dev, buf, sizeof(buf)) > 0)) { 211 // do nothing 212 } 213} 214 215static Uint8 *SNDIO_GetDeviceBuf(SDL_AudioDevice *device, int *buffer_size) 216{ 217 return device->hidden->mixbuf; 218} 219 220static void SNDIO_CloseDevice(SDL_AudioDevice *device) 221{ 222 if (device->hidden) { 223 if (device->hidden->dev) { 224 SNDIO_sio_stop(device->hidden->dev); 225 SNDIO_sio_close(device->hidden->dev); 226 } 227 SDL_free(device->hidden->pfd); 228 SDL_free(device->hidden->mixbuf); 229 SDL_free(device->hidden); 230 device->hidden = NULL; 231 } 232} 233 234static bool SNDIO_OpenDevice(SDL_AudioDevice *device) 235{ 236 device->hidden = (struct SDL_PrivateAudioData *) SDL_calloc(1, sizeof(*device->hidden)); 237 if (!device->hidden) { 238 return false; 239 } 240 241 // Recording devices must be non-blocking for SNDIO_FlushRecording 242 device->hidden->dev = SNDIO_sio_open(SIO_DEVANY, 243 device->recording ? SIO_REC : SIO_PLAY, device->recording); 244 if (!device->hidden->dev) { 245 return SDL_SetError("sio_open() failed"); 246 } 247 248 device->hidden->pfd = SDL_malloc(sizeof(struct pollfd) * SNDIO_sio_nfds(device->hidden->dev)); 249 if (!device->hidden->pfd) { 250 return false; 251 } 252 253 struct sio_par par; 254 SNDIO_sio_initpar(&par); 255 256 par.rate = device->spec.freq; 257 par.pchan = device->spec.channels; 258 par.round = device->sample_frames; 259 par.appbufsz = par.round * 2; 260 261 // Try for a closest match on audio format 262 SDL_AudioFormat test_format; 263 const SDL_AudioFormat *closefmts = SDL_ClosestAudioFormats(device->spec.format); 264 while ((test_format = *(closefmts++)) != 0) { 265 if (!SDL_AUDIO_ISFLOAT(test_format)) { 266 par.le = SDL_AUDIO_ISLITTLEENDIAN(test_format) ? 1 : 0; 267 par.sig = SDL_AUDIO_ISSIGNED(test_format) ? 1 : 0; 268 par.bits = SDL_AUDIO_BITSIZE(test_format); 269 270 if (SNDIO_sio_setpar(device->hidden->dev, &par) == 0) { 271 continue; 272 } 273 if (SNDIO_sio_getpar(device->hidden->dev, &par) == 0) { 274 return SDL_SetError("sio_getpar() failed"); 275 } 276 if (par.bps != SIO_BPS(par.bits)) { 277 continue; 278 } 279 if ((par.bits == 8 * par.bps) || (par.msb)) { 280 break; 281 } 282 } 283 } 284 285 if (!test_format) { 286 return SDL_SetError("sndio: Unsupported audio format"); 287 } 288 289 if ((par.bps == 4) && (par.sig) && (par.le)) { 290 device->spec.format = SDL_AUDIO_S32LE; 291 } else if ((par.bps == 4) && (par.sig) && (!par.le)) { 292 device->spec.format = SDL_AUDIO_S32BE; 293 } else if ((par.bps == 2) && (par.sig) && (par.le)) { 294 device->spec.format = SDL_AUDIO_S16LE; 295 } else if ((par.bps == 2) && (par.sig) && (!par.le)) { 296 device->spec.format = SDL_AUDIO_S16BE; 297 } else if ((par.bps == 1) && (par.sig)) { 298 device->spec.format = SDL_AUDIO_S8; 299 } else if ((par.bps == 1) && (!par.sig)) { 300 device->spec.format = SDL_AUDIO_U8; 301 } else { 302 return SDL_SetError("sndio: Got unsupported hardware audio format."); 303 } 304 305 device->spec.freq = par.rate; 306 device->spec.channels = par.pchan; 307 device->sample_frames = par.round; 308 309 // Calculate the final parameters for this audio specification 310 SDL_UpdatedAudioDeviceFormat(device); 311 312 // Allocate mixing buffer 313 device->hidden->mixbuf = (Uint8 *)SDL_malloc(device->buffer_size); 314 if (!device->hidden->mixbuf) { 315 return false; 316 } 317 SDL_memset(device->hidden->mixbuf, device->silence_value, device->buffer_size); 318 319 if (!SNDIO_sio_start(device->hidden->dev)) { 320 return SDL_SetError("sio_start() failed"); 321 } 322 323 return true; // We're ready to rock and roll. :-) 324} 325 326static void SNDIO_Deinitialize(void) 327{ 328 UnloadSNDIOLibrary(); 329} 330 331static void SNDIO_DetectDevices(SDL_AudioDevice **default_playback, SDL_AudioDevice **default_recording) 332{ 333 *default_playback = SDL_AddAudioDevice(false, DEFAULT_PLAYBACK_DEVNAME, NULL, (void *)0x1); 334 *default_recording = SDL_AddAudioDevice(true, DEFAULT_RECORDING_DEVNAME, NULL, (void *)0x2); 335} 336 337static bool SNDIO_Init(SDL_AudioDriverImpl *impl) 338{ 339 if (!LoadSNDIOLibrary()) { 340 return false; 341 } 342 343 impl->OpenDevice = SNDIO_OpenDevice; 344 impl->WaitDevice = SNDIO_WaitDevice; 345 impl->PlayDevice = SNDIO_PlayDevice; 346 impl->GetDeviceBuf = SNDIO_GetDeviceBuf; 347 impl->CloseDevice = SNDIO_CloseDevice; 348 impl->WaitRecordingDevice = SNDIO_WaitDevice; 349 impl->RecordDevice = SNDIO_RecordDevice; 350 impl->FlushRecording = SNDIO_FlushRecording; 351 impl->Deinitialize = SNDIO_Deinitialize; 352 impl->DetectDevices = SNDIO_DetectDevices; 353 354 impl->HasRecordingSupport = true; 355 356 return true; 357} 358 359AudioBootStrap SNDIO_bootstrap = { 360 "sndio", "OpenBSD sndio", SNDIO_Init, false, false 361}; 362 363#endif // SDL_AUDIO_DRIVER_SNDIO 364[FILE END](C) 2025 0x4248 (C) 2025 4248 Media and 4248 Systems, All part of 0x4248 See LICENCE files for more information. Not all files are by 0x4248 always check Licencing.