1/*
2 Simple DirectMedia Layer
3 Copyright (C) 1997-2021 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 */
46static void
47DISKAUDIO_WaitDevice(_THIS)
48{
49 SDL_Delay(_this->hidden->io_delay);
50}
51
52static void
53DISKAUDIO_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
68static Uint8 *
69DISKAUDIO_GetDeviceBuf(_THIS)
70{
71 return (_this->hidden->mixbuf);
72}
73
74static int
75DISKAUDIO_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
98static void
99DISKAUDIO_FlushCapture(_THIS)
100{
101 /* no op...we don't advance the file pointer or anything. */
102}
103
104
105static void
106DISKAUDIO_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
116static const char *
117get_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
128static int
129DISKAUDIO_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
173static void
174DISKAUDIO_DetectDevices(void)
175{
176 SDL_AddAudioDevice(SDL_FALSE, DEFAULT_OUTPUT_DEVNAME, NULL, (void *) 0x1);
177 SDL_AddAudioDevice(SDL_TRUE, DEFAULT_INPUT_DEVNAME, NULL, (void *) 0x2);
178}
179
180static int
181DISKAUDIO_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
200AudioBootStrap 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