1 /*
2 Copyright (C) 2009 Jonathon Fowler <jf@jonof.id.au>
3 Copyright (C) EDuke32 developers and contributors
4
5 This program is free software; you can redistribute it and/or
6 modify it under the terms of the GNU General Public License
7 as published by the Free Software Foundation; either version 2
8 of the License, or (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
13
14 See the GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19
20 */
21
22 /**
23 * libSDL output driver for MultiVoc
24 */
25
26 #include "driver_sdl.h"
27
28 #include "compat.h"
29 #include "multivoc.h"
30 #include "mutex.h"
31 #include "sdl_inc.h"
32 #include "vfs.h"
33
34 #ifdef __ANDROID__
35 #include "duke3d.h"
36 #include "android.h"
37 #endif
38
39 enum
40 {
41 SDLErr_Error = -1,
42 SDLErr_Ok = 0,
43 SDLErr_Uninitialised,
44 SDLErr_InitSubSystem,
45 SDLErr_OpenAudio
46 };
47
48 static int ErrorCode = SDLErr_Ok;
49 static int Initialised;
50 static int Playing;
51 static uint32_t StartedSDL;
52
53 static char *MixBuffer;
54 static int MixBufferSize;
55 static int MixBufferCount;
56 static int MixBufferCurrent;
57 static int MixBufferUsed;
58 static void (*MixCallBack)(void);
59
60 #if (SDL_MAJOR_VERSION >= 2)
61 static SDL_AudioDeviceID audio_dev;
62 #endif
63
64 #if SDL_MAJOR_VERSION >= 2
65 char SDLAudioDriverName[16];
66 char *SDLAudioDriverNameEnv;
67 #endif
68
fillData(void * userdata,Uint8 * ptr,int remaining)69 static void fillData(void * userdata, Uint8 * ptr, int remaining)
70 {
71 UNREFERENCED_PARAMETER(userdata);
72
73 if (!MixBuffer || !MixCallBack)
74 return;
75
76 while (remaining > 0)
77 {
78 if (MixBufferUsed == MixBufferSize)
79 {
80 MixCallBack();
81 MixBufferUsed = 0;
82
83 if (++MixBufferCurrent >= MixBufferCount)
84 MixBufferCurrent -= MixBufferCount;
85 }
86
87 while (remaining > 0 && MixBufferUsed < MixBufferSize)
88 {
89 auto sptr = MixBuffer + (MixBufferCurrent * MixBufferSize) + MixBufferUsed;
90 int len = MixBufferSize - MixBufferUsed;
91
92 if (remaining < len)
93 len = remaining;
94
95 memcpy(ptr, sptr, len);
96
97 ptr += len;
98 MixBufferUsed += len;
99 remaining -= len;
100 }
101 }
102 }
103
SDLDrv_GetError(void)104 int SDLDrv_GetError(void) { return ErrorCode; }
105
SDLDrv_ErrorString(int ErrorNumber)106 const char *SDLDrv_ErrorString(int ErrorNumber)
107 {
108 switch (ErrorNumber)
109 {
110 case SDLErr_Error: return SDLDrv_ErrorString(ErrorCode);
111 case SDLErr_Ok: return "SDL Audio ok.";
112 case SDLErr_Uninitialised: return "SDL Audio uninitialized.";
113 case SDLErr_InitSubSystem: return "SDL Audio: error in Init or InitSubSystem.";
114 case SDLErr_OpenAudio: return "SDL Audio: error in OpenAudio.";
115 default: return "Unknown SDL Audio error code.";
116 }
117 }
118
119 #if SDL_MAJOR_VERSION >= 2
SDLDrv_PCM_PrintDrivers(void)120 void SDLDrv_PCM_PrintDrivers(void)
121 {
122 MV_Printf("Available audio drivers: ");
123
124 for (int i = 0; i < SDL_GetNumAudioDrivers(); ++i)
125 MV_Printf("%s ", SDL_GetAudioDriver(i));
126
127 MV_Printf("\n");
128 }
129
SDLDrv_PCM_CheckDriverName(char const * dev)130 int SDLDrv_PCM_CheckDriverName(char const *dev)
131 {
132 for (int i = 0; i < SDL_GetNumAudioDrivers(); ++i)
133 if (!Bstrcasecmp(dev, SDL_GetAudioDriver(i)))
134 return true;
135
136 return false;
137 }
138
SDLDrv_PCM_GetDriverName(void)139 char const *SDLDrv_PCM_GetDriverName(void) { return SDL_GetCurrentAudioDriver(); }
SDLDrv_Cleanup(void)140 static void SDLDrv_Cleanup(void) { DO_FREE_AND_NULL(SDLAudioDriverNameEnv); }
141 #endif
142
SDLDrv_PCM_Init(int * mixrate,int * numchannels,void * initdata)143 int SDLDrv_PCM_Init(int *mixrate, int *numchannels, void * initdata)
144 {
145 UNREFERENCED_PARAMETER(initdata);
146
147 Uint32 inited;
148 int err = 0;
149
150 if (Initialised)
151 SDLDrv_PCM_Shutdown();
152 #if SDL_MAJOR_VERSION >= 2
153 if (!SDLAudioDriverNameEnv)
154 {
155 if (auto s = SDL_getenv("SDL_AUDIODRIVER"))
156 SDLAudioDriverNameEnv = Xstrdup(s);
157 }
158
159 if (SDLAudioDriverName[0])
160 SDL_setenv("SDL_AUDIODRIVER", SDLAudioDriverName, true);
161 #endif
162
163 inited = SDL_WasInit(SDL_INIT_AUDIO);
164
165 if (!(inited & SDL_INIT_AUDIO))
166 {
167 err = SDL_InitSubSystem(SDL_INIT_AUDIO);
168 StartedSDL = SDL_WasInit(SDL_INIT_AUDIO);
169 }
170
171 if (err < 0)
172 {
173 ErrorCode = SDLErr_InitSubSystem;
174 return SDLErr_Error;
175 }
176
177 int chunksize = 512;
178 #ifdef __ANDROID__
179 chunksize = droidinfo.audio_buffer_size;
180 #endif
181
182 SDL_AudioSpec spec = {};
183
184 spec.freq = *mixrate;
185 spec.format = AUDIO_S16SYS;
186 spec.channels = *numchannels;
187 spec.samples = chunksize;
188 spec.callback = fillData;
189 spec.userdata = nullptr;
190
191 SDL_AudioSpec actual = {};
192
193 #if (SDL_MAJOR_VERSION >= 2)
194 audio_dev = err = SDL_OpenAudioDevice(nullptr, 0, &spec, &actual, SDL_AUDIO_ALLOW_FREQUENCY_CHANGE);
195 #else
196 err = !SDL_OpenAudio(&spec, &actual);
197 #endif
198
199 #if SDL_MAJOR_VERSION >= 2
200 // restore original value
201 if (SDLAudioDriverNameEnv)
202 SDL_setenv("SDL_AUDIODRIVER", SDLAudioDriverNameEnv, true);
203 #endif
204
205 if (err == 0)
206 {
207 ErrorCode = SDLErr_OpenAudio;
208 return SDLErr_Error;
209 }
210
211 #if (SDL_MAJOR_VERSION >= 2)
212 char *drivername = Xstrdup(SDL_GetCurrentAudioDriver());
213
214 for (int i=0;drivername[i] != 0;++i)
215 drivername[i] = toupperlookup[drivername[i]];
216
217 auto devname = Xstrdup(SDL_GetAudioDeviceName(0, 0));
218 auto pdevname = Bstrchr(devname, '(');
219
220 if (pdevname)
221 {
222 auto rt = Bstrchr(pdevname++, ')');
223 if (rt != nullptr) *rt = '\0';
224 }
225 else
226 pdevname = devname;
227
228 MV_Printf("SDL %s driver on %s", drivername, pdevname);
229
230 Xfree(devname);
231 Xfree(drivername);
232 #else
233 char drivernamestr[64] = "(error)";
234 SDL_AudioDriverName(drivernamestr, sizeof(drivernamestr));
235 MV_Printf("SDL %s driver", drivernamestr);
236
237 if (actual.freq == 0 || actual.channels == 0)
238 {
239 // hack for when SDL said it opened the audio, but clearly didn't
240 SDL_CloseAudio();
241 ErrorCode = SDLErr_OpenAudio;
242 return SDLErr_Error;
243 }
244 #endif
245 err = 0;
246
247 *mixrate = actual.freq;
248 if (actual.format == AUDIO_U8 || actual.format == AUDIO_S8)
249 {
250 ErrorCode = SDLErr_OpenAudio;
251 err = 1;
252 }
253
254 *numchannels = actual.channels;
255 if (actual.channels != 1 && actual.channels != 2)
256 {
257 ErrorCode = SDLErr_OpenAudio;
258 err = 1;
259 }
260
261 if (err)
262 {
263 SDL_CloseAudio();
264 return SDLErr_Error;
265 }
266
267 Initialised = 1;
268 return SDLErr_Ok;
269 }
270
SDLDrv_PCM_Shutdown(void)271 void SDLDrv_PCM_Shutdown(void)
272 {
273 if (!Initialised)
274 return;
275
276 if (StartedSDL)
277 SDL_QuitSubSystem(StartedSDL);
278
279 StartedSDL = 0;
280 Initialised = 0;
281 #if SDL_MAJOR_VERSION >= 2
282 SDLDrv_Cleanup();
283 #endif
284 }
285
SDLDrv_PCM_BeginPlayback(char * BufferStart,int BufferSize,int NumDivisions,void (* CallBackFunc)(void))286 int SDLDrv_PCM_BeginPlayback(char *BufferStart, int BufferSize,
287 int NumDivisions, void ( *CallBackFunc )( void ) )
288 {
289 if (!Initialised)
290 {
291 ErrorCode = SDLErr_Uninitialised;
292 return SDLErr_Error;
293 }
294
295 if (Playing)
296 SDLDrv_PCM_StopPlayback();
297
298 MixBuffer = BufferStart;
299 MixBufferSize = BufferSize;
300 MixBufferCount = NumDivisions;
301 MixBufferCurrent = 0;
302 MixBufferUsed = 0;
303 MixCallBack = CallBackFunc;
304
305 // prime the buffer
306 MixCallBack();
307
308 #if (SDL_MAJOR_VERSION >= 2)
309 SDL_PauseAudioDevice(audio_dev, 0);
310 #else
311 SDL_PauseAudio(0);
312 #endif
313 Playing = 1;
314
315 return SDLErr_Ok;
316 }
317
SDLDrv_PCM_StopPlayback(void)318 void SDLDrv_PCM_StopPlayback(void)
319 {
320 if (!Initialised || !Playing)
321 return;
322
323 #if (SDL_MAJOR_VERSION >= 2)
324 SDL_PauseAudioDevice(audio_dev, 1);
325 #else
326 SDL_PauseAudio(1);
327 #endif
328
329 Playing = 0;
330 }
331
SDLDrv_PCM_Lock(void)332 void SDLDrv_PCM_Lock(void)
333 {
334 #if (SDL_MAJOR_VERSION >= 2)
335 SDL_LockAudioDevice(audio_dev);
336 #else
337 SDL_LockAudio();
338 #endif
339 }
340
SDLDrv_PCM_Unlock(void)341 void SDLDrv_PCM_Unlock(void)
342 {
343 #if (SDL_MAJOR_VERSION >= 2)
344 SDL_UnlockAudioDevice(audio_dev);
345 #else
346 SDL_UnlockAudio();
347 #endif
348 }
349