src/audio/sun/SDL_sunaudio.c
author Philipp Wiesemann <philipp.wiesemann@arcor.de>
Sat, 01 Jun 2013 21:09:36 +0200
changeset 7252 8ecb54eeaeec
parent 7191 75360622e65f
child 7719 31b5f9ff36ca
permissions -rw-r--r--
Corrected indentation of license.
     1 /*
     2   Simple DirectMedia Layer
     3   Copyright (C) 1997-2013 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_config.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 "../SDL_audiomem.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(int iscapture, SDL_AddAudioDevice addfn)
    60 {
    61     SDL_EnumUnixAudioDevices(iscapture, 1, (int (*)(int fd)) NULL, addfn);
    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     fd_set fdset;
   102 
   103     FD_ZERO(&fdset);
   104     FD_SET(this->hidden->audio_fd, &fdset);
   105     select(this->hidden->audio_fd + 1, NULL, &fdset, NULL, NULL);
   106 #endif
   107 }
   108 
   109 static void
   110 SUNAUDIO_PlayDevice(_THIS)
   111 {
   112     /* Write the audio data */
   113     if (this->hidden->ulaw_only) {
   114         /* Assuming that this->spec.freq >= 8000 Hz */
   115         int accum, incr, pos;
   116         Uint8 *aubuf;
   117 
   118         accum = 0;
   119         incr = this->spec.freq / 8;
   120         aubuf = this->hidden->ulaw_buf;
   121         switch (this->hidden->audio_fmt & 0xFF) {
   122         case 8:
   123             {
   124                 Uint8 *sndbuf;
   125 
   126                 sndbuf = this->hidden->mixbuf;
   127                 for (pos = 0; pos < this->hidden->fragsize; ++pos) {
   128                     *aubuf = snd2au((0x80 - *sndbuf) * 64);
   129                     accum += incr;
   130                     while (accum > 0) {
   131                         accum -= 1000;
   132                         sndbuf += 1;
   133                     }
   134                     aubuf += 1;
   135                 }
   136             }
   137             break;
   138         case 16:
   139             {
   140                 Sint16 *sndbuf;
   141 
   142                 sndbuf = (Sint16 *) this->hidden->mixbuf;
   143                 for (pos = 0; pos < this->hidden->fragsize; ++pos) {
   144                     *aubuf = snd2au(*sndbuf / 4);
   145                     accum += incr;
   146                     while (accum > 0) {
   147                         accum -= 1000;
   148                         sndbuf += 1;
   149                     }
   150                     aubuf += 1;
   151                 }
   152             }
   153             break;
   154         }
   155 #ifdef DEBUG_AUDIO
   156         CheckUnderflow(this);
   157 #endif
   158         if (write(this->hidden->audio_fd, this->hidden->ulaw_buf,
   159             this->hidden->fragsize) < 0) {
   160             /* Assume fatal error, for now */
   161             this->enabled = 0;
   162         }
   163         this->hidden->written += this->hidden->fragsize;
   164     } else {
   165 #ifdef DEBUG_AUDIO
   166         CheckUnderflow(this);
   167 #endif
   168         if (write(this->hidden->audio_fd, this->hidden->mixbuf,
   169             this->spec.size) < 0) {
   170             /* Assume fatal error, for now */
   171             this->enabled = 0;
   172         }
   173         this->hidden->written += this->hidden->fragsize;
   174     }
   175 }
   176 
   177 static Uint8 *
   178 SUNAUDIO_GetDeviceBuf(_THIS)
   179 {
   180     return (this->hidden->mixbuf);
   181 }
   182 
   183 static void
   184 SUNAUDIO_CloseDevice(_THIS)
   185 {
   186     if (this->hidden != NULL) {
   187         if (this->hidden->mixbuf != NULL) {
   188             SDL_FreeAudioMem(this->hidden->mixbuf);
   189             this->hidden->mixbuf = NULL;
   190         }
   191         if (this->hidden->ulaw_buf != NULL) {
   192             SDL_free(this->hidden->ulaw_buf);
   193             this->hidden->ulaw_buf = NULL;
   194         }
   195         if (this->hidden->audio_fd >= 0) {
   196             close(this->hidden->audio_fd);
   197             this->hidden->audio_fd = -1;
   198         }
   199         SDL_free(this->hidden);
   200         this->hidden = NULL;
   201     }
   202 }
   203 
   204 static int
   205 SUNAUDIO_OpenDevice(_THIS, const char *devname, int iscapture)
   206 {
   207     const int flags = ((iscapture) ? OPEN_FLAGS_INPUT : OPEN_FLAGS_OUTPUT);
   208     SDL_AudioFormat format = 0;
   209     audio_info_t info;
   210 
   211     /* We don't care what the devname is...we'll try to open anything. */
   212     /*  ...but default to first name in the list... */
   213     if (devname == NULL) {
   214         devname = SDL_GetAudioDeviceName(0, iscapture);
   215         if (devname == NULL) {
   216             return SDL_SetError("No such audio device");
   217         }
   218     }
   219 
   220     /* Initialize all variables that we clean on shutdown */
   221     this->hidden = (struct SDL_PrivateAudioData *)
   222         SDL_malloc((sizeof *this->hidden));
   223     if (this->hidden == NULL) {
   224         return SDL_OutOfMemory();
   225     }
   226     SDL_memset(this->hidden, 0, (sizeof *this->hidden));
   227 
   228     /* Open the audio device */
   229     this->hidden->audio_fd = open(devname, flags, 0);
   230     if (this->hidden->audio_fd < 0) {
   231         return SDL_SetError("Couldn't open %s: %s", devname, strerror(errno));
   232     }
   233 
   234 #ifdef AUDIO_SETINFO
   235     int enc;
   236 #endif
   237     int desired_freq = this->spec.freq;
   238 
   239     /* Determine the audio parameters from the AudioSpec */
   240     switch (SDL_AUDIO_BITSIZE(this->spec.format)) {
   241 
   242     case 8:
   243         {                       /* Unsigned 8 bit audio data */
   244             this->spec.format = AUDIO_U8;
   245 #ifdef AUDIO_SETINFO
   246             enc = AUDIO_ENCODING_LINEAR8;
   247 #endif
   248         }
   249         break;
   250 
   251     case 16:
   252         {                       /* Signed 16 bit audio data */
   253             this->spec.format = AUDIO_S16SYS;
   254 #ifdef AUDIO_SETINFO
   255             enc = AUDIO_ENCODING_LINEAR;
   256 #endif
   257         }
   258         break;
   259 
   260     default:
   261         {
   262             /* !!! FIXME: fallback to conversion on unsupported types! */
   263             return SDL_SetError("Unsupported audio format");
   264         }
   265     }
   266     this->hidden->audio_fmt = this->spec.format;
   267 
   268     this->hidden->ulaw_only = 0;    /* modern Suns do support linear audio */
   269 #ifdef AUDIO_SETINFO
   270     for (;;) {
   271         audio_info_t info;
   272         AUDIO_INITINFO(&info);  /* init all fields to "no change" */
   273 
   274         /* Try to set the requested settings */
   275         info.play.sample_rate = this->spec.freq;
   276         info.play.channels = this->spec.channels;
   277         info.play.precision = (enc == AUDIO_ENCODING_ULAW)
   278             ? 8 : this->spec.format & 0xff;
   279         info.play.encoding = enc;
   280         if (ioctl(this->hidden->audio_fd, AUDIO_SETINFO, &info) == 0) {
   281 
   282             /* Check to be sure we got what we wanted */
   283             if (ioctl(this->hidden->audio_fd, AUDIO_GETINFO, &info) < 0) {
   284                 return SDL_SetError("Error getting audio parameters: %s",
   285                                     strerror(errno));
   286             }
   287             if (info.play.encoding == enc
   288                 && info.play.precision == (this->spec.format & 0xff)
   289                 && info.play.channels == this->spec.channels) {
   290                 /* Yow! All seems to be well! */
   291                 this->spec.freq = info.play.sample_rate;
   292                 break;
   293             }
   294         }
   295 
   296         switch (enc) {
   297         case AUDIO_ENCODING_LINEAR8:
   298             /* unsigned 8bit apparently not supported here */
   299             enc = AUDIO_ENCODING_LINEAR;
   300             this->spec.format = AUDIO_S16SYS;
   301             break;              /* try again */
   302 
   303         case AUDIO_ENCODING_LINEAR:
   304             /* linear 16bit didn't work either, resort to -law */
   305             enc = AUDIO_ENCODING_ULAW;
   306             this->spec.channels = 1;
   307             this->spec.freq = 8000;
   308             this->spec.format = AUDIO_U8;
   309             this->hidden->ulaw_only = 1;
   310             break;
   311 
   312         default:
   313             /* oh well... */
   314             return SDL_SetError("Error setting audio parameters: %s",
   315                                 strerror(errno));
   316         }
   317     }
   318 #endif /* AUDIO_SETINFO */
   319     this->hidden->written = 0;
   320 
   321     /* We can actually convert on-the-fly to U-Law */
   322     if (this->hidden->ulaw_only) {
   323         this->spec.freq = desired_freq;
   324         this->hidden->fragsize = (this->spec.samples * 1000) /
   325             (this->spec.freq / 8);
   326         this->hidden->frequency = 8;
   327         this->hidden->ulaw_buf = (Uint8 *) SDL_malloc(this->hidden->fragsize);
   328         if (this->hidden->ulaw_buf == NULL) {
   329             return SDL_OutOfMemory();
   330         }
   331         this->spec.channels = 1;
   332     } else {
   333         this->hidden->fragsize = this->spec.samples;
   334         this->hidden->frequency = this->spec.freq / 1000;
   335     }
   336 #ifdef DEBUG_AUDIO
   337     fprintf(stderr, "Audio device %s U-Law only\n",
   338             this->hidden->ulaw_only ? "is" : "is not");
   339     fprintf(stderr, "format=0x%x chan=%d freq=%d\n",
   340             this->spec.format, this->spec.channels, this->spec.freq);
   341 #endif
   342 
   343     /* Update the fragment size as size in bytes */
   344     SDL_CalculateAudioSpec(&this->spec);
   345 
   346     /* Allocate mixing buffer */
   347     this->hidden->mixbuf = (Uint8 *) SDL_AllocAudioMem(this->spec.size);
   348     if (this->hidden->mixbuf == NULL) {
   349         return SDL_OutOfMemory();
   350     }
   351     SDL_memset(this->hidden->mixbuf, this->spec.silence, this->spec.size);
   352 
   353     /* We're ready to rock and roll. :-) */
   354     return 0;
   355 }
   356 
   357 /************************************************************************/
   358 /* This function (snd2au()) copyrighted:                                */
   359 /************************************************************************/
   360 /*      Copyright 1989 by Rich Gopstein and Harris Corporation          */
   361 /*                                                                      */
   362 /*      Permission to use, copy, modify, and distribute this software   */
   363 /*      and its documentation for any purpose and without fee is        */
   364 /*      hereby granted, provided that the above copyright notice        */
   365 /*      appears in all copies and that both that copyright notice and   */
   366 /*      this permission notice appear in supporting documentation, and  */
   367 /*      that the name of Rich Gopstein and Harris Corporation not be    */
   368 /*      used in advertising or publicity pertaining to distribution     */
   369 /*      of the software without specific, written prior permission.     */
   370 /*      Rich Gopstein and Harris Corporation make no representations    */
   371 /*      about the suitability of this software for any purpose.  It     */
   372 /*      provided "as is" without express or implied warranty.           */
   373 /************************************************************************/
   374 
   375 static Uint8
   376 snd2au(int sample)
   377 {
   378 
   379     int mask;
   380 
   381     if (sample < 0) {
   382         sample = -sample;
   383         mask = 0x7f;
   384     } else {
   385         mask = 0xff;
   386     }
   387 
   388     if (sample < 32) {
   389         sample = 0xF0 | (15 - sample / 2);
   390     } else if (sample < 96) {
   391         sample = 0xE0 | (15 - (sample - 32) / 4);
   392     } else if (sample < 224) {
   393         sample = 0xD0 | (15 - (sample - 96) / 8);
   394     } else if (sample < 480) {
   395         sample = 0xC0 | (15 - (sample - 224) / 16);
   396     } else if (sample < 992) {
   397         sample = 0xB0 | (15 - (sample - 480) / 32);
   398     } else if (sample < 2016) {
   399         sample = 0xA0 | (15 - (sample - 992) / 64);
   400     } else if (sample < 4064) {
   401         sample = 0x90 | (15 - (sample - 2016) / 128);
   402     } else if (sample < 8160) {
   403         sample = 0x80 | (15 - (sample - 4064) / 256);
   404     } else {
   405         sample = 0x80;
   406     }
   407     return (mask & sample);
   408 }
   409 
   410 static int
   411 SUNAUDIO_Init(SDL_AudioDriverImpl * impl)
   412 {
   413     /* Set the function pointers */
   414     impl->DetectDevices = SUNAUDIO_DetectDevices;
   415     impl->OpenDevice = SUNAUDIO_OpenDevice;
   416     impl->PlayDevice = SUNAUDIO_PlayDevice;
   417     impl->WaitDevice = SUNAUDIO_WaitDevice;
   418     impl->GetDeviceBuf = SUNAUDIO_GetDeviceBuf;
   419     impl->CloseDevice = SUNAUDIO_CloseDevice;
   420 
   421     return 1; /* this audio target is available. */
   422 }
   423 
   424 AudioBootStrap SUNAUDIO_bootstrap = {
   425     "audio", "UNIX /dev/audio interface", SUNAUDIO_Init, 0
   426 };
   427 
   428 #endif /* SDL_AUDIO_DRIVER_SUNAUDIO */
   429 
   430 /* vi: set ts=4 sw=4 expandtab: */