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