Atlas - SDL_netbsdaudio.c

Home / ext / SDL / src / audio / netbsd Lines: 30 | Size: 10526 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#ifdef SDL_AUDIO_DRIVER_NETBSD 24 25// Driver for native NetBSD audio(4). 26 27#include <errno.h> 28#include <unistd.h> 29#include <fcntl.h> 30#include <sys/time.h> 31#include <sys/ioctl.h> 32#include <sys/stat.h> 33#include <sys/types.h> 34#include <sys/audioio.h> 35 36#include "../../core/unix/SDL_poll.h" 37#include "../SDL_audiodev_c.h" 38#include "SDL_netbsdaudio.h" 39 40//#define DEBUG_AUDIO 41 42static void NETBSDAUDIO_DetectDevices(SDL_AudioDevice **default_playback, SDL_AudioDevice **default_recording) 43{ 44 SDL_EnumUnixAudioDevices(false, NULL); 45} 46 47static void NETBSDAUDIO_Status(SDL_AudioDevice *device) 48{ 49#ifdef DEBUG_AUDIO 50 /* *INDENT-OFF* */ // clang-format off 51 audio_info_t info; 52 const struct audio_prinfo *prinfo; 53 54 if (ioctl(device->hidden->audio_fd, AUDIO_GETINFO, &info) < 0) { 55 fprintf(stderr, "AUDIO_GETINFO failed.\n"); 56 return; 57 } 58 59 prinfo = device->recording ? &info.record : &info.play; 60 61 fprintf(stderr, "\n" 62 "[%s info]\n" 63 "buffer size : %d bytes\n" 64 "sample rate : %i Hz\n" 65 "channels : %i\n" 66 "precision : %i-bit\n" 67 "encoding : 0x%x\n" 68 "seek : %i\n" 69 "sample count : %i\n" 70 "EOF count : %i\n" 71 "paused : %s\n" 72 "error occurred : %s\n" 73 "waiting : %s\n" 74 "active : %s\n" 75 "", 76 device->recording ? "record" : "play", 77 prinfo->buffer_size, 78 prinfo->sample_rate, 79 prinfo->channels, 80 prinfo->precision, 81 prinfo->encoding, 82 prinfo->seek, 83 prinfo->samples, 84 prinfo->eof, 85 prinfo->pause ? "yes" : "no", 86 prinfo->error ? "yes" : "no", 87 prinfo->waiting ? "yes" : "no", 88 prinfo->active ? "yes" : "no"); 89 90 fprintf(stderr, "\n" 91 "[audio info]\n" 92 "monitor_gain : %i\n" 93 "hw block size : %d bytes\n" 94 "hi watermark : %i\n" 95 "lo watermark : %i\n" 96 "audio mode : %s\n" 97 "", 98 info.monitor_gain, 99 info.blocksize, 100 info.hiwat, info.lowat, 101 (info.mode == AUMODE_PLAY) ? "PLAY" 102 : (info.mode == AUMODE_RECORD) ? "RECORD" 103 : (info.mode == AUMODE_PLAY_ALL ? "PLAY_ALL" : "?")); 104 105 fprintf(stderr, "\n" 106 "[audio spec]\n" 107 "format : 0x%x\n" 108 "size : %u\n" 109 "", 110 device->spec.format, 111 device->buffer_size); 112 /* *INDENT-ON* */ // clang-format on 113 114#endif // DEBUG_AUDIO 115} 116 117static bool NETBSDAUDIO_WaitDevice(SDL_AudioDevice *device) 118{ 119 const bool recording = device->recording; 120 while (!SDL_GetAtomicInt(&device->shutdown)) { 121 audio_info_t info; 122 const int rc = ioctl(device->hidden->audio_fd, AUDIO_GETINFO, &info); 123 if (rc < 0) { 124 if (errno == EAGAIN) { 125 continue; 126 } 127 // Hmm, not much we can do - abort 128 fprintf(stderr, "netbsdaudio WaitDevice ioctl failed (unrecoverable): %s\n", strerror(errno)); 129 return false; 130 } 131 const size_t remain = (size_t)((recording ? info.record.seek : info.play.seek) * SDL_AUDIO_BYTESIZE(device->spec.format)); 132 if (!recording && (remain >= device->buffer_size)) { 133 SDL_Delay(10); 134 } else if (recording && (remain < device->buffer_size)) { 135 SDL_Delay(10); 136 } else { 137 break; // ready to go! 138 } 139 } 140 141 return true; 142} 143 144static bool NETBSDAUDIO_PlayDevice(SDL_AudioDevice *device, const Uint8 *buffer, int buflen) 145{ 146 struct SDL_PrivateAudioData *h = device->hidden; 147 const int written = write(h->audio_fd, buffer, buflen); 148 if (written != buflen) { // Treat even partial writes as fatal errors. 149 return false; 150 } 151 152#ifdef DEBUG_AUDIO 153 fprintf(stderr, "Wrote %d bytes of audio data\n", written); 154#endif 155 return true; 156} 157 158static Uint8 *NETBSDAUDIO_GetDeviceBuf(SDL_AudioDevice *device, int *buffer_size) 159{ 160 return device->hidden->mixbuf; 161} 162 163static int NETBSDAUDIO_RecordDevice(SDL_AudioDevice *device, void *vbuffer, int buflen) 164{ 165 Uint8 *buffer = (Uint8 *)vbuffer; 166 const int br = read(device->hidden->audio_fd, buffer, buflen); 167 if (br == -1) { 168 // Non recoverable error has occurred. It should be reported!!! 169 perror("audio"); 170 return -1; 171 } 172 173#ifdef DEBUG_AUDIO 174 fprintf(stderr, "Recorded %d bytes of audio data\n", br); 175#endif 176 return br; 177} 178 179static void NETBSDAUDIO_FlushRecording(SDL_AudioDevice *device) 180{ 181 struct SDL_PrivateAudioData *h = device->hidden; 182 audio_info_t info; 183 if (ioctl(device->hidden->audio_fd, AUDIO_GETINFO, &info) == 0) { 184 size_t remain = (size_t)(info.record.seek * SDL_AUDIO_BYTESIZE(device->spec.format)); 185 while (remain > 0) { 186 char buf[512]; 187 const size_t len = SDL_min(sizeof(buf), remain); 188 const ssize_t br = read(h->audio_fd, buf, len); 189 if (br <= 0) { 190 break; 191 } 192 remain -= br; 193 } 194 } 195} 196 197static void NETBSDAUDIO_CloseDevice(SDL_AudioDevice *device) 198{ 199 if (device->hidden) { 200 if (device->hidden->audio_fd >= 0) { 201 close(device->hidden->audio_fd); 202 } 203 SDL_free(device->hidden->mixbuf); 204 SDL_free(device->hidden); 205 device->hidden = NULL; 206 } 207} 208 209static bool NETBSDAUDIO_OpenDevice(SDL_AudioDevice *device) 210{ 211 const bool recording = device->recording; 212 int encoding = AUDIO_ENCODING_NONE; 213 audio_info_t info, hwinfo; 214 struct audio_prinfo *prinfo = recording ? &info.record : &info.play; 215 216 // Initialize all variables that we clean on shutdown 217 device->hidden = (struct SDL_PrivateAudioData *) SDL_calloc(1, sizeof(*device->hidden)); 218 if (!device->hidden) { 219 return false; 220 } 221 222 // Open the audio device; we hardcode the device path in `device->name` for lack of better info, so use that. 223 const int flags = ((device->recording) ? O_RDONLY : O_WRONLY); 224 device->hidden->audio_fd = open(device->name, flags | O_CLOEXEC); 225 if (device->hidden->audio_fd < 0) { 226 return SDL_SetError("Couldn't open %s: %s", device->name, strerror(errno)); 227 } 228 229 AUDIO_INITINFO(&info); 230 231#ifdef AUDIO_GETFORMAT // Introduced in NetBSD 9.0 232 if (ioctl(device->hidden->audio_fd, AUDIO_GETFORMAT, &hwinfo) != -1) { 233 // Use the device's native sample rate so the kernel doesn't have to resample. 234 device->spec.freq = recording ? hwinfo.record.sample_rate : hwinfo.play.sample_rate; 235 } 236#endif 237 238 prinfo->sample_rate = device->spec.freq; 239 prinfo->channels = device->spec.channels; 240 241 SDL_AudioFormat test_format; 242 const SDL_AudioFormat *closefmts = SDL_ClosestAudioFormats(device->spec.format); 243 while ((test_format = *(closefmts++)) != 0) { 244 switch (test_format) { 245 case SDL_AUDIO_U8: 246 encoding = AUDIO_ENCODING_ULINEAR; 247 break; 248 case SDL_AUDIO_S8: 249 encoding = AUDIO_ENCODING_SLINEAR; 250 break; 251 case SDL_AUDIO_S16LE: 252 encoding = AUDIO_ENCODING_SLINEAR_LE; 253 break; 254 case SDL_AUDIO_S16BE: 255 encoding = AUDIO_ENCODING_SLINEAR_BE; 256 break; 257 case SDL_AUDIO_S32LE: 258 encoding = AUDIO_ENCODING_SLINEAR_LE; 259 break; 260 case SDL_AUDIO_S32BE: 261 encoding = AUDIO_ENCODING_SLINEAR_BE; 262 break; 263 default: 264 continue; 265 } 266 break; 267 } 268 269 if (!test_format) { 270 return SDL_SetError("%s: Unsupported audio format", "netbsd"); 271 } 272 prinfo->encoding = encoding; 273 prinfo->precision = SDL_AUDIO_BITSIZE(test_format); 274 275 info.hiwat = 5; 276 info.lowat = 3; 277 if (ioctl(device->hidden->audio_fd, AUDIO_SETINFO, &info) < 0) { 278 return SDL_SetError("AUDIO_SETINFO failed for %s: %s", device->name, strerror(errno)); 279 } 280 281 if (ioctl(device->hidden->audio_fd, AUDIO_GETINFO, &info) < 0) { 282 return SDL_SetError("AUDIO_GETINFO failed for %s: %s", device->name, strerror(errno)); 283 } 284 285 // Final spec used for the device. 286 device->spec.format = test_format; 287 device->spec.freq = prinfo->sample_rate; 288 device->spec.channels = prinfo->channels; 289 290 SDL_UpdatedAudioDeviceFormat(device); 291 292 if (!recording) { 293 // Allocate mixing buffer 294 device->hidden->mixlen = device->buffer_size; 295 device->hidden->mixbuf = (Uint8 *)SDL_malloc(device->hidden->mixlen); 296 if (!device->hidden->mixbuf) { 297 return false; 298 } 299 SDL_memset(device->hidden->mixbuf, device->silence_value, device->buffer_size); 300 } 301 302 NETBSDAUDIO_Status(device); 303 304 return true; // We're ready to rock and roll. :-) 305} 306 307static bool NETBSDAUDIO_Init(SDL_AudioDriverImpl *impl) 308{ 309 impl->DetectDevices = NETBSDAUDIO_DetectDevices; 310 impl->OpenDevice = NETBSDAUDIO_OpenDevice; 311 impl->WaitDevice = NETBSDAUDIO_WaitDevice; 312 impl->PlayDevice = NETBSDAUDIO_PlayDevice; 313 impl->GetDeviceBuf = NETBSDAUDIO_GetDeviceBuf; 314 impl->CloseDevice = NETBSDAUDIO_CloseDevice; 315 impl->WaitRecordingDevice = NETBSDAUDIO_WaitDevice; 316 impl->RecordDevice = NETBSDAUDIO_RecordDevice; 317 impl->FlushRecording = NETBSDAUDIO_FlushRecording; 318 319 impl->HasRecordingSupport = true; 320 321 return true; 322} 323 324AudioBootStrap NETBSDAUDIO_bootstrap = { 325 "netbsd", "NetBSD audio", NETBSDAUDIO_Init, false, false 326}; 327 328#endif // SDL_AUDIO_DRIVER_NETBSD 329
[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.