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
DISKAUDIO_WaitDevice(_THIS)47 DISKAUDIO_WaitDevice(_THIS)
48 {
49     SDL_Delay(this->hidden->io_delay);
50 }
51 
52 static void
DISKAUDIO_PlayDevice(_THIS)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 *
DISKAUDIO_GetDeviceBuf(_THIS)69 DISKAUDIO_GetDeviceBuf(_THIS)
70 {
71     return (this->hidden->mixbuf);
72 }
73 
74 static int
DISKAUDIO_CaptureFromDevice(_THIS,void * buffer,int buflen)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
DISKAUDIO_FlushCapture(_THIS)99 DISKAUDIO_FlushCapture(_THIS)
100 {
101     /* no op...we don't advance the file pointer or anything. */
102 }
103 
104 
105 static void
DISKAUDIO_CloseDevice(_THIS)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 *
get_filename(const int iscapture,const char * devname)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
DISKAUDIO_OpenDevice(_THIS,void * handle,const char * devname,int iscapture)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
DISKAUDIO_DetectDevices(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
DISKAUDIO_Init(SDL_AudioDriverImpl * impl)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: */
207