Atlas - SDL_netbsdaudio.c

Home / ext / SDL2 / src / audio / netbsd Lines: 29 | Size: 12283 bytes [Download] [Show on GitHub] [Search similar files] [Raw] [Raw (proxy)]
[FILE BEGIN]
1/* 2 Simple DirectMedia Layer 3 Copyright (C) 1997-2018 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#if SDL_AUDIO_DRIVER_NETBSD 24 25/* 26 * Driver for native NetBSD audio(4). 27 * [email protected]. 28 */ 29 30#include <errno.h> 31#include <unistd.h> 32#include <fcntl.h> 33#include <sys/time.h> 34#include <sys/ioctl.h> 35#include <sys/stat.h> 36#include <sys/types.h> 37#include <sys/audioio.h> 38 39#include "SDL_timer.h" 40#include "SDL_audio.h" 41#include "../../core/unix/SDL_poll.h" 42#include "../SDL_audio_c.h" 43#include "../SDL_audiodev_c.h" 44#include "SDL_netbsdaudio.h" 45 46/* Use timer for synchronization */ 47/* #define USE_TIMER_SYNC */ 48 49/* #define DEBUG_AUDIO */ 50/* #define DEBUG_AUDIO_STREAM */ 51 52 53static void 54NETBSDAUDIO_DetectDevices(void) 55{ 56 SDL_EnumUnixAudioDevices(0, NULL); 57} 58 59 60static void 61NETBSDAUDIO_Status(_THIS) 62{ 63#ifdef DEBUG_AUDIO 64 /* *INDENT-OFF* */ 65 audio_info_t info; 66 const audio_prinfo *prinfo; 67 68 if (ioctl(this->hidden->audio_fd, AUDIO_GETINFO, &info) < 0) { 69 fprintf(stderr, "AUDIO_GETINFO failed.\n"); 70 return; 71 } 72 73 prinfo = this->iscapture ? &info.play : &info.record; 74 75 fprintf(stderr, "\n" 76 "[%s info]\n" 77 "buffer size : %d bytes\n" 78 "sample rate : %i Hz\n" 79 "channels : %i\n" 80 "precision : %i-bit\n" 81 "encoding : 0x%x\n" 82 "seek : %i\n" 83 "sample count : %i\n" 84 "EOF count : %i\n" 85 "paused : %s\n" 86 "error occured : %s\n" 87 "waiting : %s\n" 88 "active : %s\n" 89 "", 90 this->iscapture ? "record" : "play", 91 prinfo->buffer_size, 92 prinfo->sample_rate, 93 prinfo->channels, 94 prinfo->precision, 95 prinfo->encoding, 96 prinfo->seek, 97 prinfo->samples, 98 prinfo->eof, 99 prinfo->pause ? "yes" : "no", 100 prinfo->error ? "yes" : "no", 101 prinfo->waiting ? "yes" : "no", 102 prinfo->active ? "yes" : "no"); 103 104 fprintf(stderr, "\n" 105 "[audio info]\n" 106 "monitor_gain : %i\n" 107 "hw block size : %d bytes\n" 108 "hi watermark : %i\n" 109 "lo watermark : %i\n" 110 "audio mode : %s\n" 111 "", 112 info.monitor_gain, 113 info.blocksize, 114 info.hiwat, info.lowat, 115 (info.mode == AUMODE_PLAY) ? "PLAY" 116 : (info.mode = AUMODE_RECORD) ? "RECORD" 117 : (info.mode == AUMODE_PLAY_ALL ? "PLAY_ALL" : "?")); 118 /* *INDENT-ON* */ 119#endif /* DEBUG_AUDIO */ 120} 121 122 123/* This function waits until it is possible to write a full sound buffer */ 124static void 125NETBSDAUDIO_WaitDevice(_THIS) 126{ 127#ifndef USE_BLOCKING_WRITES /* Not necessary when using blocking writes */ 128 /* See if we need to use timed audio synchronization */ 129 if (this->hidden->frame_ticks) { 130 /* Use timer for general audio synchronization */ 131 Sint32 ticks; 132 133 ticks = ((Sint32) (this->hidden->next_frame - SDL_GetTicks())) - FUDGE_TICKS; 134 if (ticks > 0) { 135 SDL_Delay(ticks); 136 } 137 } else { 138 /* Use SDL_IOReady() for audio synchronization */ 139#ifdef DEBUG_AUDIO 140 fprintf(stderr, "Waiting for audio to get ready\n"); 141#endif 142 if (SDL_IOReady(this->hidden->audio_fd, SDL_TRUE, 10 * 1000) 143 <= 0) { 144 const char *message = 145 "Audio timeout - buggy audio driver? (disabled)"; 146 /* In general we should never print to the screen, 147 but in this case we have no other way of letting 148 the user know what happened. 149 */ 150 fprintf(stderr, "SDL: %s\n", message); 151 SDL_OpenedAudioDeviceDisconnected(this); 152 /* Don't try to close - may hang */ 153 this->hidden->audio_fd = -1; 154#ifdef DEBUG_AUDIO 155 fprintf(stderr, "Done disabling audio\n"); 156#endif 157 } 158#ifdef DEBUG_AUDIO 159 fprintf(stderr, "Ready!\n"); 160#endif 161 } 162#endif /* !USE_BLOCKING_WRITES */ 163} 164 165static void 166NETBSDAUDIO_PlayDevice(_THIS) 167{ 168 int written, p = 0; 169 170 /* Write the audio data, checking for EAGAIN on broken audio drivers */ 171 do { 172 written = write(this->hidden->audio_fd, 173 &this->hidden->mixbuf[p], this->hidden->mixlen - p); 174 175 if (written > 0) 176 p += written; 177 if (written == -1 && errno != 0 && errno != EAGAIN && errno != EINTR) { 178 /* Non recoverable error has occurred. It should be reported!!! */ 179 perror("audio"); 180 break; 181 } 182 183#ifdef DEBUG_AUDIO 184 fprintf(stderr, "Wrote %d bytes of audio data\n", written); 185#endif 186 187 if (p < this->hidden->mixlen 188 || ((written < 0) && ((errno == 0) || (errno == EAGAIN)))) { 189 SDL_Delay(1); /* Let a little CPU time go by */ 190 } 191 } while (p < this->hidden->mixlen); 192 193 /* If timer synchronization is enabled, set the next write frame */ 194 if (this->hidden->frame_ticks) { 195 this->hidden->next_frame += this->hidden->frame_ticks; 196 } 197 198 /* If we couldn't write, assume fatal error for now */ 199 if (written < 0) { 200 SDL_OpenedAudioDeviceDisconnected(this); 201 } 202} 203 204static Uint8 * 205NETBSDAUDIO_GetDeviceBuf(_THIS) 206{ 207 return (this->hidden->mixbuf); 208} 209 210 211static int 212NETBSDAUDIO_CaptureFromDevice(_THIS, void *_buffer, int buflen) 213{ 214 Uint8 *buffer = (Uint8 *) _buffer; 215 int br, p = 0; 216 217 /* Capture the audio data, checking for EAGAIN on broken audio drivers */ 218 do { 219 br = read(this->hidden->audio_fd, buffer + p, buflen - p); 220 if (br > 0) 221 p += br; 222 if (br == -1 && errno != 0 && errno != EAGAIN && errno != EINTR) { 223 /* Non recoverable error has occurred. It should be reported!!! */ 224 perror("audio"); 225 return p ? p : -1; 226 } 227 228#ifdef DEBUG_AUDIO 229 fprintf(stderr, "Captured %d bytes of audio data\n", br); 230#endif 231 232 if (p < buflen 233 || ((br < 0) && ((errno == 0) || (errno == EAGAIN)))) { 234 SDL_Delay(1); /* Let a little CPU time go by */ 235 } 236 } while (p < buflen); 237} 238 239static void 240NETBSDAUDIO_FlushCapture(_THIS) 241{ 242 audio_info_t info; 243 size_t remain; 244 Uint8 buf[512]; 245 246 if (ioctl(this->hidden->audio_fd, AUDIO_GETINFO, &info) < 0) { 247 return; /* oh well. */ 248 } 249 250 remain = (size_t) (info.record.samples * (SDL_AUDIO_BITSIZE(this->spec.format) / 8)); 251 while (remain > 0) { 252 const size_t len = SDL_min(sizeof (buf), remain); 253 const int br = read(this->hidden->audio_fd, buf, len); 254 if (br <= 0) { 255 return; /* oh well. */ 256 } 257 remain -= br; 258 } 259} 260 261static void 262NETBSDAUDIO_CloseDevice(_THIS) 263{ 264 if (this->hidden->audio_fd >= 0) { 265 close(this->hidden->audio_fd); 266 } 267 SDL_free(this->hidden->mixbuf); 268 SDL_free(this->hidden); 269} 270 271static int 272NETBSDAUDIO_OpenDevice(_THIS, void *handle, const char *devname, int iscapture) 273{ 274 const int flags = iscapture ? OPEN_FLAGS_INPUT : OPEN_FLAGS_OUTPUT; 275 SDL_AudioFormat format = 0; 276 audio_info_t info; 277 audio_prinfo *prinfo = iscapture ? &info.play : &info.record; 278 279 /* We don't care what the devname is...we'll try to open anything. */ 280 /* ...but default to first name in the list... */ 281 if (devname == NULL) { 282 devname = SDL_GetAudioDeviceName(0, iscapture); 283 if (devname == NULL) { 284 return SDL_SetError("No such audio device"); 285 } 286 } 287 288 /* Initialize all variables that we clean on shutdown */ 289 this->hidden = (struct SDL_PrivateAudioData *) 290 SDL_malloc((sizeof *this->hidden)); 291 if (this->hidden == NULL) { 292 return SDL_OutOfMemory(); 293 } 294 SDL_zerop(this->hidden); 295 296 /* Open the audio device */ 297 this->hidden->audio_fd = open(devname, flags, 0); 298 if (this->hidden->audio_fd < 0) { 299 return SDL_SetError("Couldn't open %s: %s", devname, strerror(errno)); 300 } 301 302 AUDIO_INITINFO(&info); 303 304 /* Calculate the final parameters for this audio specification */ 305 SDL_CalculateAudioSpec(&this->spec); 306 307 /* Set to play mode */ 308 info.mode = iscapture ? AUMODE_RECORD : AUMODE_PLAY; 309 if (ioctl(this->hidden->audio_fd, AUDIO_SETINFO, &info) < 0) { 310 return SDL_SetError("Couldn't put device into play mode"); 311 } 312 313 AUDIO_INITINFO(&info); 314 for (format = SDL_FirstAudioFormat(this->spec.format); 315 format; format = SDL_NextAudioFormat()) { 316 switch (format) { 317 case AUDIO_U8: 318 prinfo->encoding = AUDIO_ENCODING_ULINEAR; 319 prinfo->precision = 8; 320 break; 321 case AUDIO_S8: 322 prinfo->encoding = AUDIO_ENCODING_SLINEAR; 323 prinfo->precision = 8; 324 break; 325 case AUDIO_S16LSB: 326 prinfo->encoding = AUDIO_ENCODING_SLINEAR_LE; 327 prinfo->precision = 16; 328 break; 329 case AUDIO_S16MSB: 330 prinfo->encoding = AUDIO_ENCODING_SLINEAR_BE; 331 prinfo->precision = 16; 332 break; 333 case AUDIO_U16LSB: 334 prinfo->encoding = AUDIO_ENCODING_ULINEAR_LE; 335 prinfo->precision = 16; 336 break; 337 case AUDIO_U16MSB: 338 prinfo->encoding = AUDIO_ENCODING_ULINEAR_BE; 339 prinfo->precision = 16; 340 break; 341 default: 342 continue; 343 } 344 345 if (ioctl(this->hidden->audio_fd, AUDIO_SETINFO, &info) == 0) { 346 break; 347 } 348 } 349 350 if (!format) { 351 return SDL_SetError("No supported encoding for 0x%x", this->spec.format); 352 } 353 354 this->spec.format = format; 355 356 AUDIO_INITINFO(&info); 357 prinfo->channels = this->spec.channels; 358 if (ioctl(this->hidden->audio_fd, AUDIO_SETINFO, &info) == -1) { 359 this->spec.channels = 1; 360 } 361 AUDIO_INITINFO(&info); 362 prinfo->sample_rate = this->spec.freq; 363 info.blocksize = this->spec.size; 364 info.hiwat = 5; 365 info.lowat = 3; 366 (void) ioctl(this->hidden->audio_fd, AUDIO_SETINFO, &info); 367 (void) ioctl(this->hidden->audio_fd, AUDIO_GETINFO, &info); 368 this->spec.freq = prinfo->sample_rate; 369 370 if (!iscapture) { 371 /* Allocate mixing buffer */ 372 this->hidden->mixlen = this->spec.size; 373 this->hidden->mixbuf = (Uint8 *) SDL_malloc(this->hidden->mixlen); 374 if (this->hidden->mixbuf == NULL) { 375 return SDL_OutOfMemory(); 376 } 377 SDL_memset(this->hidden->mixbuf, this->spec.silence, this->spec.size); 378 } 379 380 NETBSDAUDIO_Status(this); 381 382 /* We're ready to rock and roll. :-) */ 383 return 0; 384} 385 386static int 387NETBSDAUDIO_Init(SDL_AudioDriverImpl * impl) 388{ 389 /* Set the function pointers */ 390 impl->DetectDevices = NETBSDAUDIO_DetectDevices; 391 impl->OpenDevice = NETBSDAUDIO_OpenDevice; 392 impl->PlayDevice = NETBSDAUDIO_PlayDevice; 393 impl->WaitDevice = NETBSDAUDIO_WaitDevice; 394 impl->GetDeviceBuf = NETBSDAUDIO_GetDeviceBuf; 395 impl->CloseDevice = NETBSDAUDIO_CloseDevice; 396 impl->CaptureFromDevice = NETBSDAUDIO_CaptureFromDevice; 397 impl->FlushCapture = NETBSDAUDIO_FlushCapture; 398 399 impl->HasCaptureSupport = SDL_TRUE; 400 impl->AllowsArbitraryDeviceNames = 1; 401 402 return 1; /* this audio target is available. */ 403} 404 405 406AudioBootStrap NETBSDAUDIO_bootstrap = { 407 "netbsd", "NetBSD audio", NETBSDAUDIO_Init, 0 408}; 409 410#endif /* SDL_AUDIO_DRIVER_NETBSD */ 411 412/* vi: set ts=4 sw=4 expandtab: */ 413
[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.