sdl

FORK: Simple Directmedia Layer
git clone https://git.neptards.moe/neptards/sdl.git
Log | Files | Refs

SDL_sunaudio.c (13052B)


      1 /*
      2   Simple DirectMedia Layer
      3   Copyright (C) 1997-2020 Sam Lantinga <slouken@libsdl.org>
      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 */
     55 static Uint8 snd2au(int sample);
     56 
     57 /* Audio driver bootstrap functions */
     58 static void
     59 SUNAUDIO_DetectDevices(void)
     60 {
     61     SDL_EnumUnixAudioDevices(1, (int (*)(int)) NULL);
     62 }
     63 
     64 #ifdef DEBUG_AUDIO
     65 void
     66 CheckUnderflow(_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 
     81 static void
     82 SUNAUDIO_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 
    105 static void
    106 SUNAUDIO_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 
    173 static Uint8 *
    174 SUNAUDIO_GetDeviceBuf(_THIS)
    175 {
    176     return (this->hidden->mixbuf);
    177 }
    178 
    179 static void
    180 SUNAUDIO_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 
    190 static int
    191 SUNAUDIO_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 
    362 static Uint8
    363 snd2au(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 
    397 static int
    398 SUNAUDIO_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 
    413 AudioBootStrap 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: */