src/audio/dsp/SDL_dspaudio.c
author David Ludwig
Thu, 31 Dec 2015 01:54:11 -0500
changeset 9993 e05d46c27ce3
parent 9619 b94b6d0bff0f
child 9998 f67cf37e9cd4
permissions -rw-r--r--
WinRT: minor code-comment cleanups
     1 /*
     2   Simple DirectMedia Layer
     3   Copyright (C) 1997-2015 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_OSS
    24 
    25 /* Allow access to a raw mixing buffer */
    26 
    27 #include <stdio.h>              /* For perror() */
    28 #include <string.h>             /* For strerror() */
    29 #include <errno.h>
    30 #include <unistd.h>
    31 #include <fcntl.h>
    32 #include <signal.h>
    33 #include <sys/time.h>
    34 #include <sys/ioctl.h>
    35 #include <sys/stat.h>
    36 
    37 #if SDL_AUDIO_DRIVER_OSS_SOUNDCARD_H
    38 /* This is installed on some systems */
    39 #include <soundcard.h>
    40 #else
    41 /* This is recommended by OSS */
    42 #include <sys/soundcard.h>
    43 #endif
    44 
    45 #include "SDL_timer.h"
    46 #include "SDL_audio.h"
    47 #include "../SDL_audiomem.h"
    48 #include "../SDL_audio_c.h"
    49 #include "../SDL_audiodev_c.h"
    50 #include "SDL_dspaudio.h"
    51 
    52 
    53 static void
    54 DSP_DetectDevices(void)
    55 {
    56     SDL_EnumUnixAudioDevices(0, NULL);
    57 }
    58 
    59 
    60 static void
    61 DSP_CloseDevice(_THIS)
    62 {
    63     if (this->hidden != NULL) {
    64         SDL_FreeAudioMem(this->hidden->mixbuf);
    65         this->hidden->mixbuf = NULL;
    66         if (this->hidden->audio_fd >= 0) {
    67             close(this->hidden->audio_fd);
    68             this->hidden->audio_fd = -1;
    69         }
    70         SDL_free(this->hidden);
    71         this->hidden = NULL;
    72     }
    73 }
    74 
    75 
    76 static int
    77 DSP_OpenDevice(_THIS, void *handle, const char *devname, int iscapture)
    78 {
    79     const int flags = ((iscapture) ? OPEN_FLAGS_INPUT : OPEN_FLAGS_OUTPUT);
    80     int format;
    81     int value;
    82     int frag_spec;
    83     SDL_AudioFormat test_format;
    84 
    85     /* We don't care what the devname is...we'll try to open anything. */
    86     /*  ...but default to first name in the list... */
    87     if (devname == NULL) {
    88         devname = SDL_GetAudioDeviceName(0, iscapture);
    89         if (devname == NULL) {
    90             return SDL_SetError("No such audio device");
    91         }
    92     }
    93 
    94     /* Make sure fragment size stays a power of 2, or OSS fails. */
    95     /* I don't know which of these are actually legal values, though... */
    96     if (this->spec.channels > 8)
    97         this->spec.channels = 8;
    98     else if (this->spec.channels > 4)
    99         this->spec.channels = 4;
   100     else if (this->spec.channels > 2)
   101         this->spec.channels = 2;
   102 
   103     /* Initialize all variables that we clean on shutdown */
   104     this->hidden = (struct SDL_PrivateAudioData *)
   105         SDL_malloc((sizeof *this->hidden));
   106     if (this->hidden == NULL) {
   107         return SDL_OutOfMemory();
   108     }
   109     SDL_memset(this->hidden, 0, (sizeof *this->hidden));
   110 
   111     /* Open the audio device */
   112     this->hidden->audio_fd = open(devname, flags, 0);
   113     if (this->hidden->audio_fd < 0) {
   114         DSP_CloseDevice(this);
   115         return SDL_SetError("Couldn't open %s: %s", devname, strerror(errno));
   116     }
   117     this->hidden->mixbuf = NULL;
   118 
   119     /* Make the file descriptor use blocking writes with fcntl() */
   120     {
   121         long ctlflags;
   122         ctlflags = fcntl(this->hidden->audio_fd, F_GETFL);
   123         ctlflags &= ~O_NONBLOCK;
   124         if (fcntl(this->hidden->audio_fd, F_SETFL, ctlflags) < 0) {
   125             DSP_CloseDevice(this);
   126             return SDL_SetError("Couldn't set audio blocking mode");
   127         }
   128     }
   129 
   130     /* Get a list of supported hardware formats */
   131     if (ioctl(this->hidden->audio_fd, SNDCTL_DSP_GETFMTS, &value) < 0) {
   132         perror("SNDCTL_DSP_GETFMTS");
   133         DSP_CloseDevice(this);
   134         return SDL_SetError("Couldn't get audio format list");
   135     }
   136 
   137     /* Try for a closest match on audio format */
   138     format = 0;
   139     for (test_format = SDL_FirstAudioFormat(this->spec.format);
   140          !format && test_format;) {
   141 #ifdef DEBUG_AUDIO
   142         fprintf(stderr, "Trying format 0x%4.4x\n", test_format);
   143 #endif
   144         switch (test_format) {
   145         case AUDIO_U8:
   146             if (value & AFMT_U8) {
   147                 format = AFMT_U8;
   148             }
   149             break;
   150         case AUDIO_S16LSB:
   151             if (value & AFMT_S16_LE) {
   152                 format = AFMT_S16_LE;
   153             }
   154             break;
   155         case AUDIO_S16MSB:
   156             if (value & AFMT_S16_BE) {
   157                 format = AFMT_S16_BE;
   158             }
   159             break;
   160 #if 0
   161 /*
   162  * These formats are not used by any real life systems so they are not
   163  * needed here.
   164  */
   165         case AUDIO_S8:
   166             if (value & AFMT_S8) {
   167                 format = AFMT_S8;
   168             }
   169             break;
   170         case AUDIO_U16LSB:
   171             if (value & AFMT_U16_LE) {
   172                 format = AFMT_U16_LE;
   173             }
   174             break;
   175         case AUDIO_U16MSB:
   176             if (value & AFMT_U16_BE) {
   177                 format = AFMT_U16_BE;
   178             }
   179             break;
   180 #endif
   181         default:
   182             format = 0;
   183             break;
   184         }
   185         if (!format) {
   186             test_format = SDL_NextAudioFormat();
   187         }
   188     }
   189     if (format == 0) {
   190         DSP_CloseDevice(this);
   191         return SDL_SetError("Couldn't find any hardware audio formats");
   192     }
   193     this->spec.format = test_format;
   194 
   195     /* Set the audio format */
   196     value = format;
   197     if ((ioctl(this->hidden->audio_fd, SNDCTL_DSP_SETFMT, &value) < 0) ||
   198         (value != format)) {
   199         perror("SNDCTL_DSP_SETFMT");
   200         DSP_CloseDevice(this);
   201         return SDL_SetError("Couldn't set audio format");
   202     }
   203 
   204     /* Set the number of channels of output */
   205     value = this->spec.channels;
   206     if (ioctl(this->hidden->audio_fd, SNDCTL_DSP_CHANNELS, &value) < 0) {
   207         perror("SNDCTL_DSP_CHANNELS");
   208         DSP_CloseDevice(this);
   209         return SDL_SetError("Cannot set the number of channels");
   210     }
   211     this->spec.channels = value;
   212 
   213     /* Set the DSP frequency */
   214     value = this->spec.freq;
   215     if (ioctl(this->hidden->audio_fd, SNDCTL_DSP_SPEED, &value) < 0) {
   216         perror("SNDCTL_DSP_SPEED");
   217         DSP_CloseDevice(this);
   218         return SDL_SetError("Couldn't set audio frequency");
   219     }
   220     this->spec.freq = value;
   221 
   222     /* Calculate the final parameters for this audio specification */
   223     SDL_CalculateAudioSpec(&this->spec);
   224 
   225     /* Determine the power of two of the fragment size */
   226     for (frag_spec = 0; (0x01U << frag_spec) < this->spec.size; ++frag_spec);
   227     if ((0x01U << frag_spec) != this->spec.size) {
   228         DSP_CloseDevice(this);
   229         return SDL_SetError("Fragment size must be a power of two");
   230     }
   231     frag_spec |= 0x00020000;    /* two fragments, for low latency */
   232 
   233     /* Set the audio buffering parameters */
   234 #ifdef DEBUG_AUDIO
   235     fprintf(stderr, "Requesting %d fragments of size %d\n",
   236             (frag_spec >> 16), 1 << (frag_spec & 0xFFFF));
   237 #endif
   238     if (ioctl(this->hidden->audio_fd, SNDCTL_DSP_SETFRAGMENT, &frag_spec) < 0) {
   239         perror("SNDCTL_DSP_SETFRAGMENT");
   240     }
   241 #ifdef DEBUG_AUDIO
   242     {
   243         audio_buf_info info;
   244         ioctl(this->hidden->audio_fd, SNDCTL_DSP_GETOSPACE, &info);
   245         fprintf(stderr, "fragments = %d\n", info.fragments);
   246         fprintf(stderr, "fragstotal = %d\n", info.fragstotal);
   247         fprintf(stderr, "fragsize = %d\n", info.fragsize);
   248         fprintf(stderr, "bytes = %d\n", info.bytes);
   249     }
   250 #endif
   251 
   252     /* Allocate mixing buffer */
   253     this->hidden->mixlen = this->spec.size;
   254     this->hidden->mixbuf = (Uint8 *) SDL_AllocAudioMem(this->hidden->mixlen);
   255     if (this->hidden->mixbuf == NULL) {
   256         DSP_CloseDevice(this);
   257         return SDL_OutOfMemory();
   258     }
   259     SDL_memset(this->hidden->mixbuf, this->spec.silence, this->spec.size);
   260 
   261     /* We're ready to rock and roll. :-) */
   262     return 0;
   263 }
   264 
   265 
   266 static void
   267 DSP_PlayDevice(_THIS)
   268 {
   269     const Uint8 *mixbuf = this->hidden->mixbuf;
   270     const int mixlen = this->hidden->mixlen;
   271     if (write(this->hidden->audio_fd, mixbuf, mixlen) == -1) {
   272         perror("Audio write");
   273         SDL_OpenedAudioDeviceDisconnected(this);
   274     }
   275 #ifdef DEBUG_AUDIO
   276     fprintf(stderr, "Wrote %d bytes of audio data\n", mixlen);
   277 #endif
   278 }
   279 
   280 static Uint8 *
   281 DSP_GetDeviceBuf(_THIS)
   282 {
   283     return (this->hidden->mixbuf);
   284 }
   285 
   286 static int
   287 DSP_Init(SDL_AudioDriverImpl * impl)
   288 {
   289     /* Set the function pointers */
   290     impl->DetectDevices = DSP_DetectDevices;
   291     impl->OpenDevice = DSP_OpenDevice;
   292     impl->PlayDevice = DSP_PlayDevice;
   293     impl->GetDeviceBuf = DSP_GetDeviceBuf;
   294     impl->CloseDevice = DSP_CloseDevice;
   295 
   296     impl->AllowsArbitraryDeviceNames = 1;
   297 
   298     return 1;   /* this audio target is available. */
   299 }
   300 
   301 
   302 AudioBootStrap DSP_bootstrap = {
   303     "dsp", "OSS /dev/dsp standard audio", DSP_Init, 0
   304 };
   305 
   306 #endif /* SDL_AUDIO_DRIVER_OSS */
   307 
   308 /* vi: set ts=4 sw=4 expandtab: */