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