Atlas - SDL_sunaudio.c
Home / ext / SDL2 / src / audio / sun Lines: 4 | Size: 13052 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_SUNAUDIO 24 25/* Allow access to a raw mixing buffer */ 26 27#include <fcntl.h> 28#include <errno.h> 29#ifdef __NETBSD__ 30#include <sys/ioctl.h> 31#include <sys/audioio.h> 32#endif 33#ifdef __SVR4 34#include <sys/audioio.h> 35#else 36#include <sys/time.h> 37#include <sys/types.h> 38#endif 39#include <unistd.h> 40 41#include "SDL_timer.h" 42#include "SDL_audio.h" 43#include "../../core/unix/SDL_poll.h" 44#include "../SDL_audio_c.h" 45#include "../SDL_audiodev_c.h" 46#include "SDL_sunaudio.h" 47 48/* Open the audio device for playback, and don't block if busy */ 49 50#if defined(AUDIO_GETINFO) && !defined(AUDIO_GETBUFINFO) 51#define AUDIO_GETBUFINFO AUDIO_GETINFO 52#endif 53 54/* Audio driver functions */ 55static Uint8 snd2au(int sample); 56 57/* Audio driver bootstrap functions */ 58static void 59SUNAUDIO_DetectDevices(void) 60{ 61 SDL_EnumUnixAudioDevices(1, (int (*)(int)) NULL); 62} 63 64#ifdef DEBUG_AUDIO 65void 66CheckUnderflow(_THIS) 67{ 68#ifdef AUDIO_GETBUFINFO 69 audio_info_t info; 70 int left; 71 72 ioctl(this->hidden->audio_fd, AUDIO_GETBUFINFO, &info); 73 left = (this->hidden->written - info.play.samples); 74 if (this->hidden->written && (left == 0)) { 75 fprintf(stderr, "audio underflow!\n"); 76 } 77#endif 78} 79#endif 80 81static void 82SUNAUDIO_WaitDevice(_THIS) 83{ 84#ifdef AUDIO_GETBUFINFO 85#define SLEEP_FUDGE 10 /* 10 ms scheduling fudge factor */ 86 audio_info_t info; 87 Sint32 left; 88 89 ioctl(this->hidden->audio_fd, AUDIO_GETBUFINFO, &info); 90 left = (this->hidden->written - info.play.samples); 91 if (left > this->hidden->fragsize) { 92 Sint32 sleepy; 93 94 sleepy = ((left - this->hidden->fragsize) / this->hidden->frequency); 95 sleepy -= SLEEP_FUDGE; 96 if (sleepy > 0) { 97 SDL_Delay(sleepy); 98 } 99 } 100#else 101 SDL_IOReady(this->hidden->audio_fd, SDL_TRUE, -1); 102#endif 103} 104 105static void 106SUNAUDIO_PlayDevice(_THIS) 107{ 108 /* Write the audio data */ 109 if (this->hidden->ulaw_only) { 110 /* Assuming that this->spec.freq >= 8000 Hz */ 111 int accum, incr, pos; 112 Uint8 *aubuf; 113 114 accum = 0; 115 incr = this->spec.freq / 8; 116 aubuf = this->hidden->ulaw_buf; 117 switch (this->hidden->audio_fmt & 0xFF) { 118 case 8: 119 { 120 Uint8 *sndbuf; 121 122 sndbuf = this->hidden->mixbuf; 123 for (pos = 0; pos < this->hidden->fragsize; ++pos) { 124 *aubuf = snd2au((0x80 - *sndbuf) * 64); 125 accum += incr; 126 while (accum > 0) { 127 accum -= 1000; 128 sndbuf += 1; 129 } 130 aubuf += 1; 131 } 132 } 133 break; 134 case 16: 135 { 136 Sint16 *sndbuf; 137 138 sndbuf = (Sint16 *) this->hidden->mixbuf; 139 for (pos = 0; pos < this->hidden->fragsize; ++pos) { 140 *aubuf = snd2au(*sndbuf / 4); 141 accum += incr; 142 while (accum > 0) { 143 accum -= 1000; 144 sndbuf += 1; 145 } 146 aubuf += 1; 147 } 148 } 149 break; 150 } 151#ifdef DEBUG_AUDIO 152 CheckUnderflow(this); 153#endif 154 if (write(this->hidden->audio_fd, this->hidden->ulaw_buf, 155 this->hidden->fragsize) < 0) { 156 /* Assume fatal error, for now */ 157 SDL_OpenedAudioDeviceDisconnected(this); 158 } 159 this->hidden->written += this->hidden->fragsize; 160 } else { 161#ifdef DEBUG_AUDIO 162 CheckUnderflow(this); 163#endif 164 if (write(this->hidden->audio_fd, this->hidden->mixbuf, 165 this->spec.size) < 0) { 166 /* Assume fatal error, for now */ 167 SDL_OpenedAudioDeviceDisconnected(this); 168 } 169 this->hidden->written += this->hidden->fragsize; 170 } 171} 172 173static Uint8 * 174SUNAUDIO_GetDeviceBuf(_THIS) 175{ 176 return (this->hidden->mixbuf); 177} 178 179static void 180SUNAUDIO_CloseDevice(_THIS) 181{ 182 SDL_free(this->hidden->ulaw_buf); 183 if (this->hidden->audio_fd >= 0) { 184 close(this->hidden->audio_fd); 185 } 186 SDL_free(this->hidden->mixbuf); 187 SDL_free(this->hidden); 188} 189 190static int 191SUNAUDIO_OpenDevice(_THIS, void *handle, const char *devname, int iscapture) 192{ 193#ifdef AUDIO_SETINFO 194 int enc; 195#endif 196 int desired_freq = 0; 197 const int flags = ((iscapture) ? OPEN_FLAGS_INPUT : OPEN_FLAGS_OUTPUT); 198 SDL_AudioFormat format = 0; 199 audio_info_t info; 200 201 /* We don't care what the devname is...we'll try to open anything. */ 202 /* ...but default to first name in the list... */ 203 if (devname == NULL) { 204 devname = SDL_GetAudioDeviceName(0, iscapture); 205 if (devname == NULL) { 206 return SDL_SetError("No such audio device"); 207 } 208 } 209 210 /* Initialize all variables that we clean on shutdown */ 211 this->hidden = (struct SDL_PrivateAudioData *) 212 SDL_malloc((sizeof *this->hidden)); 213 if (this->hidden == NULL) { 214 return SDL_OutOfMemory(); 215 } 216 SDL_zerop(this->hidden); 217 218 /* Open the audio device */ 219 this->hidden->audio_fd = open(devname, flags, 0); 220 if (this->hidden->audio_fd < 0) { 221 return SDL_SetError("Couldn't open %s: %s", devname, strerror(errno)); 222 } 223 224 desired_freq = this->spec.freq; 225 226 /* Determine the audio parameters from the AudioSpec */ 227 switch (SDL_AUDIO_BITSIZE(this->spec.format)) { 228 229 case 8: 230 { /* Unsigned 8 bit audio data */ 231 this->spec.format = AUDIO_U8; 232#ifdef AUDIO_SETINFO 233 enc = AUDIO_ENCODING_LINEAR8; 234#endif 235 } 236 break; 237 238 case 16: 239 { /* Signed 16 bit audio data */ 240 this->spec.format = AUDIO_S16SYS; 241#ifdef AUDIO_SETINFO 242 enc = AUDIO_ENCODING_LINEAR; 243#endif 244 } 245 break; 246 247 default: 248 { 249 /* !!! FIXME: fallback to conversion on unsupported types! */ 250 return SDL_SetError("Unsupported audio format"); 251 } 252 } 253 this->hidden->audio_fmt = this->spec.format; 254 255 this->hidden->ulaw_only = 0; /* modern Suns do support linear audio */ 256#ifdef AUDIO_SETINFO 257 for (;;) { 258 audio_info_t info; 259 AUDIO_INITINFO(&info); /* init all fields to "no change" */ 260 261 /* Try to set the requested settings */ 262 info.play.sample_rate = this->spec.freq; 263 info.play.channels = this->spec.channels; 264 info.play.precision = (enc == AUDIO_ENCODING_ULAW) 265 ? 8 : this->spec.format & 0xff; 266 info.play.encoding = enc; 267 if (ioctl(this->hidden->audio_fd, AUDIO_SETINFO, &info) == 0) { 268 269 /* Check to be sure we got what we wanted */ 270 if (ioctl(this->hidden->audio_fd, AUDIO_GETINFO, &info) < 0) { 271 return SDL_SetError("Error getting audio parameters: %s", 272 strerror(errno)); 273 } 274 if (info.play.encoding == enc 275 && info.play.precision == (this->spec.format & 0xff) 276 && info.play.channels == this->spec.channels) { 277 /* Yow! All seems to be well! */ 278 this->spec.freq = info.play.sample_rate; 279 break; 280 } 281 } 282 283 switch (enc) { 284 case AUDIO_ENCODING_LINEAR8: 285 /* unsigned 8bit apparently not supported here */ 286 enc = AUDIO_ENCODING_LINEAR; 287 this->spec.format = AUDIO_S16SYS; 288 break; /* try again */ 289 290 case AUDIO_ENCODING_LINEAR: 291 /* linear 16bit didn't work either, resort to �-law */ 292 enc = AUDIO_ENCODING_ULAW; 293 this->spec.channels = 1; 294 this->spec.freq = 8000; 295 this->spec.format = AUDIO_U8; 296 this->hidden->ulaw_only = 1; 297 break; 298 299 default: 300 /* oh well... */ 301 return SDL_SetError("Error setting audio parameters: %s", 302 strerror(errno)); 303 } 304 } 305#endif /* AUDIO_SETINFO */ 306 this->hidden->written = 0; 307 308 /* We can actually convert on-the-fly to U-Law */ 309 if (this->hidden->ulaw_only) { 310 this->spec.freq = desired_freq; 311 this->hidden->fragsize = (this->spec.samples * 1000) / 312 (this->spec.freq / 8); 313 this->hidden->frequency = 8; 314 this->hidden->ulaw_buf = (Uint8 *) SDL_malloc(this->hidden->fragsize); 315 if (this->hidden->ulaw_buf == NULL) { 316 return SDL_OutOfMemory(); 317 } 318 this->spec.channels = 1; 319 } else { 320 this->hidden->fragsize = this->spec.samples; 321 this->hidden->frequency = this->spec.freq / 1000; 322 } 323#ifdef DEBUG_AUDIO 324 fprintf(stderr, "Audio device %s U-Law only\n", 325 this->hidden->ulaw_only ? "is" : "is not"); 326 fprintf(stderr, "format=0x%x chan=%d freq=%d\n", 327 this->spec.format, this->spec.channels, this->spec.freq); 328#endif 329 330 /* Update the fragment size as size in bytes */ 331 SDL_CalculateAudioSpec(&this->spec); 332 333 /* Allocate mixing buffer */ 334 this->hidden->mixbuf = (Uint8 *) SDL_malloc(this->spec.size); 335 if (this->hidden->mixbuf == NULL) { 336 return SDL_OutOfMemory(); 337 } 338 SDL_memset(this->hidden->mixbuf, this->spec.silence, this->spec.size); 339 340 /* We're ready to rock and roll. :-) */ 341 return 0; 342} 343 344/************************************************************************/ 345/* This function (snd2au()) copyrighted: */ 346/************************************************************************/ 347/* Copyright 1989 by Rich Gopstein and Harris Corporation */ 348/* */ 349/* Permission to use, copy, modify, and distribute this software */ 350/* and its documentation for any purpose and without fee is */ 351/* hereby granted, provided that the above copyright notice */ 352/* appears in all copies and that both that copyright notice and */ 353/* this permission notice appear in supporting documentation, and */ 354/* that the name of Rich Gopstein and Harris Corporation not be */ 355/* used in advertising or publicity pertaining to distribution */ 356/* of the software without specific, written prior permission. */ 357/* Rich Gopstein and Harris Corporation make no representations */ 358/* about the suitability of this software for any purpose. It */ 359/* provided "as is" without express or implied warranty. */ 360/************************************************************************/ 361 362static Uint8 363snd2au(int sample) 364{ 365 366 int mask; 367 368 if (sample < 0) { 369 sample = -sample; 370 mask = 0x7f; 371 } else { 372 mask = 0xff; 373 } 374 375 if (sample < 32) { 376 sample = 0xF0 | (15 - sample / 2); 377 } else if (sample < 96) { 378 sample = 0xE0 | (15 - (sample - 32) / 4); 379 } else if (sample < 224) { 380 sample = 0xD0 | (15 - (sample - 96) / 8); 381 } else if (sample < 480) { 382 sample = 0xC0 | (15 - (sample - 224) / 16); 383 } else if (sample < 992) { 384 sample = 0xB0 | (15 - (sample - 480) / 32); 385 } else if (sample < 2016) { 386 sample = 0xA0 | (15 - (sample - 992) / 64); 387 } else if (sample < 4064) { 388 sample = 0x90 | (15 - (sample - 2016) / 128); 389 } else if (sample < 8160) { 390 sample = 0x80 | (15 - (sample - 4064) / 256); 391 } else { 392 sample = 0x80; 393 } 394 return (mask & sample); 395} 396 397static int 398SUNAUDIO_Init(SDL_AudioDriverImpl * impl) 399{ 400 /* Set the function pointers */ 401 impl->DetectDevices = SUNAUDIO_DetectDevices; 402 impl->OpenDevice = SUNAUDIO_OpenDevice; 403 impl->PlayDevice = SUNAUDIO_PlayDevice; 404 impl->WaitDevice = SUNAUDIO_WaitDevice; 405 impl->GetDeviceBuf = SUNAUDIO_GetDeviceBuf; 406 impl->CloseDevice = SUNAUDIO_CloseDevice; 407 408 impl->AllowsArbitraryDeviceNames = 1; 409 410 return 1; /* this audio target is available. */ 411} 412 413AudioBootStrap SUNAUDIO_bootstrap = { 414 "audio", "UNIX /dev/audio interface", SUNAUDIO_Init, 0 415}; 416 417#endif /* SDL_AUDIO_DRIVER_SUNAUDIO */ 418 419/* vi: set ts=4 sw=4 expandtab: */ 420[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.