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