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