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