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