src/audio/disk/SDL_diskaudio.c
author Sam Lantinga <slouken@libsdl.org>
Wed, 03 Jan 2018 10:03:25 -0800
changeset 11811 5d94cb6b24d3
parent 10755 43953bdaa3ee
child 12503 806492103856
permissions -rw-r--r--
Updated copyright for 2018
     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_DISK
    24 
    25 /* Output raw audio data to a file. */
    26 
    27 #if HAVE_STDIO_H
    28 #include <stdio.h>
    29 #endif
    30 
    31 #include "SDL_rwops.h"
    32 #include "SDL_timer.h"
    33 #include "SDL_audio.h"
    34 #include "../SDL_audio_c.h"
    35 #include "SDL_diskaudio.h"
    36 #include "SDL_log.h"
    37 
    38 /* !!! FIXME: these should be SDL hints, not environment variables. */
    39 /* environment variables and defaults. */
    40 #define DISKENVR_OUTFILE         "SDL_DISKAUDIOFILE"
    41 #define DISKDEFAULT_OUTFILE      "sdlaudio.raw"
    42 #define DISKENVR_INFILE         "SDL_DISKAUDIOFILEIN"
    43 #define DISKDEFAULT_INFILE      "sdlaudio-in.raw"
    44 #define DISKENVR_IODELAY      "SDL_DISKAUDIODELAY"
    45 
    46 /* This function waits until it is possible to write a full sound buffer */
    47 static void
    48 DISKAUDIO_WaitDevice(_THIS)
    49 {
    50     SDL_Delay(this->hidden->io_delay);
    51 }
    52 
    53 static void
    54 DISKAUDIO_PlayDevice(_THIS)
    55 {
    56     const size_t written = SDL_RWwrite(this->hidden->io,
    57                                        this->hidden->mixbuf,
    58                                        1, this->spec.size);
    59 
    60     /* If we couldn't write, assume fatal error for now */
    61     if (written != this->spec.size) {
    62         SDL_OpenedAudioDeviceDisconnected(this);
    63     }
    64 #ifdef DEBUG_AUDIO
    65     fprintf(stderr, "Wrote %d bytes of audio data\n", written);
    66 #endif
    67 }
    68 
    69 static Uint8 *
    70 DISKAUDIO_GetDeviceBuf(_THIS)
    71 {
    72     return (this->hidden->mixbuf);
    73 }
    74 
    75 static int
    76 DISKAUDIO_CaptureFromDevice(_THIS, void *buffer, int buflen)
    77 {
    78     struct SDL_PrivateAudioData *h = this->hidden;
    79     const int origbuflen = buflen;
    80 
    81     SDL_Delay(h->io_delay);
    82 
    83     if (h->io) {
    84         const size_t br = SDL_RWread(h->io, buffer, 1, buflen);
    85         buflen -= (int) br;
    86         buffer = ((Uint8 *) buffer) + br;
    87         if (buflen > 0) {  /* EOF (or error, but whatever). */
    88             SDL_RWclose(h->io);
    89             h->io = NULL;
    90         }
    91     }
    92 
    93     /* if we ran out of file, just write silence. */
    94     SDL_memset(buffer, this->spec.silence, buflen);
    95 
    96     return origbuflen;
    97 }
    98 
    99 static void
   100 DISKAUDIO_FlushCapture(_THIS)
   101 {
   102     /* no op...we don't advance the file pointer or anything. */
   103 }
   104 
   105 
   106 static void
   107 DISKAUDIO_CloseDevice(_THIS)
   108 {
   109     if (this->hidden->io != NULL) {
   110         SDL_RWclose(this->hidden->io);
   111     }
   112     SDL_free(this->hidden->mixbuf);
   113     SDL_free(this->hidden);
   114 }
   115 
   116 
   117 static const char *
   118 get_filename(const int iscapture, const char *devname)
   119 {
   120     if (devname == NULL) {
   121         devname = SDL_getenv(iscapture ? DISKENVR_INFILE : DISKENVR_OUTFILE);
   122         if (devname == NULL) {
   123             devname = iscapture ? DISKDEFAULT_INFILE : DISKDEFAULT_OUTFILE;
   124         }
   125     }
   126     return devname;
   127 }
   128 
   129 static int
   130 DISKAUDIO_OpenDevice(_THIS, void *handle, const char *devname, int iscapture)
   131 {
   132     /* handle != NULL means "user specified the placeholder name on the fake detected device list" */
   133     const char *fname = get_filename(iscapture, handle ? NULL : devname);
   134     const char *envr = SDL_getenv(DISKENVR_IODELAY);
   135 
   136     this->hidden = (struct SDL_PrivateAudioData *)
   137         SDL_malloc(sizeof(*this->hidden));
   138     if (this->hidden == NULL) {
   139         return SDL_OutOfMemory();
   140     }
   141     SDL_zerop(this->hidden);
   142 
   143     if (envr != NULL) {
   144         this->hidden->io_delay = SDL_atoi(envr);
   145     } else {
   146         this->hidden->io_delay = ((this->spec.samples * 1000) / this->spec.freq);
   147     }
   148 
   149     /* Open the audio device */
   150     this->hidden->io = SDL_RWFromFile(fname, iscapture ? "rb" : "wb");
   151     if (this->hidden->io == NULL) {
   152         return -1;
   153     }
   154 
   155     /* Allocate mixing buffer */
   156     if (!iscapture) {
   157         this->hidden->mixbuf = (Uint8 *) SDL_malloc(this->spec.size);
   158         if (this->hidden->mixbuf == NULL) {
   159             return SDL_OutOfMemory();
   160         }
   161         SDL_memset(this->hidden->mixbuf, this->spec.silence, this->spec.size);
   162     }
   163 
   164     SDL_LogCritical(SDL_LOG_CATEGORY_AUDIO,
   165                 "You are using the SDL disk i/o audio driver!\n");
   166     SDL_LogCritical(SDL_LOG_CATEGORY_AUDIO,
   167                 " %s file [%s].\n", iscapture ? "Reading from" : "Writing to",
   168                 fname);
   169 
   170     /* We're ready to rock and roll. :-) */
   171     return 0;
   172 }
   173 
   174 static void
   175 DISKAUDIO_DetectDevices(void)
   176 {
   177     SDL_AddAudioDevice(SDL_FALSE, DEFAULT_OUTPUT_DEVNAME, (void *) 0x1);
   178     SDL_AddAudioDevice(SDL_TRUE, DEFAULT_INPUT_DEVNAME, (void *) 0x2);
   179 }
   180 
   181 static int
   182 DISKAUDIO_Init(SDL_AudioDriverImpl * impl)
   183 {
   184     /* Set the function pointers */
   185     impl->OpenDevice = DISKAUDIO_OpenDevice;
   186     impl->WaitDevice = DISKAUDIO_WaitDevice;
   187     impl->PlayDevice = DISKAUDIO_PlayDevice;
   188     impl->GetDeviceBuf = DISKAUDIO_GetDeviceBuf;
   189     impl->CaptureFromDevice = DISKAUDIO_CaptureFromDevice;
   190     impl->FlushCapture = DISKAUDIO_FlushCapture;
   191 
   192     impl->CloseDevice = DISKAUDIO_CloseDevice;
   193     impl->DetectDevices = DISKAUDIO_DetectDevices;
   194 
   195     impl->AllowsArbitraryDeviceNames = 1;
   196     impl->HasCaptureSupport = SDL_TRUE;
   197 
   198     return 1;   /* this audio target is available. */
   199 }
   200 
   201 AudioBootStrap DISKAUDIO_bootstrap = {
   202     "disk", "direct-to-disk audio", DISKAUDIO_Init, 1
   203 };
   204 
   205 #endif /* SDL_AUDIO_DRIVER_DISK */
   206 
   207 /* vi: set ts=4 sw=4 expandtab: */