Atlas - SDL_dspaudio.c
Home / ext / SDL / src / audio / dsp Lines: 9 | Size: 9844 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#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 304[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.