src/audio/bsd/SDL_bsdaudio.c
author Sam Lantinga
Mon, 09 Jan 2017 11:58:01 -0800
changeset 10802 6afc9b833867
parent 10737 3406a0f8b041
permissions -rw-r--r--
We only need the first few keymaps corresponding to the following constants:
K_NORMTAB, K_SHIFTTAB, K_ALTTAB, K_ALTSHIFTTAB

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