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