driver_sdl.cpp
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 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 104 int SDLDrv_GetError(void) { return ErrorCode; } 105 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 120 void SDLDrv_PCM_PrintDrivers(void) 121 { 122 LOG_F(INFO, "Available audio drivers:"); 123 124 for (int i = 0; i < SDL_GetNumAudioDrivers(); ++i) 125 LOG_F(INFO, "%s", SDL_GetAudioDriver(i)); 126 } 127 128 int SDLDrv_PCM_CheckDriverName(char const *dev) 129 { 130 for (int i = 0; i < SDL_GetNumAudioDrivers(); ++i) 131 if (!Bstrcasecmp(dev, SDL_GetAudioDriver(i))) 132 return true; 133 134 return false; 135 } 136 137 char const *SDLDrv_PCM_GetDriverName(void) { return SDL_GetCurrentAudioDriver(); } 138 static void SDLDrv_Cleanup(void) { DO_FREE_AND_NULL(SDLAudioDriverNameEnv); } 139 #endif 140 141 int SDLDrv_PCM_Init(int *mixrate, int *numchannels, void * initdata) 142 { 143 UNREFERENCED_PARAMETER(initdata); 144 145 Uint32 inited; 146 int err = 0; 147 148 if (Initialised) 149 SDLDrv_PCM_Shutdown(); 150 #if SDL_MAJOR_VERSION >= 2 151 else if (SDLAudioDriverNameEnv == nullptr) 152 { 153 static int done; 154 155 if (!done) 156 { 157 if (auto s = SDL_getenv("SDL_AUDIODRIVER")) 158 SDLAudioDriverNameEnv = Xstrdup(s); 159 } 160 } 161 162 if (SDLAudioDriverName[0]) 163 SDL_setenv("SDL_AUDIODRIVER", SDLAudioDriverName, true); 164 #endif 165 166 inited = SDL_WasInit(SDL_INIT_AUDIO); 167 168 if (!(inited & SDL_INIT_AUDIO)) 169 { 170 err = SDL_InitSubSystem(SDL_INIT_AUDIO); 171 StartedSDL = SDL_WasInit(SDL_INIT_AUDIO); 172 } 173 174 if (err < 0) 175 { 176 ErrorCode = SDLErr_InitSubSystem; 177 return SDLErr_Error; 178 } 179 180 int chunksize = 512; 181 #ifdef __ANDROID__ 182 chunksize = droidinfo.audio_buffer_size; 183 #endif 184 185 SDL_AudioSpec spec = {}; 186 187 spec.freq = *mixrate; 188 spec.format = AUDIO_S16SYS; 189 spec.channels = *numchannels; 190 spec.samples = chunksize; 191 spec.callback = fillData; 192 spec.userdata = nullptr; 193 194 SDL_AudioSpec actual = {}; 195 196 #if (SDL_MAJOR_VERSION >= 2) 197 audio_dev = err = SDL_OpenAudioDevice(nullptr, 0, &spec, &actual, 0); 198 #else 199 err = !SDL_OpenAudio(&spec, &actual); 200 #endif 201 202 #if SDL_MAJOR_VERSION >= 2 203 // restore original value 204 if (SDLAudioDriverNameEnv) 205 SDL_setenv("SDL_AUDIODRIVER", SDLAudioDriverNameEnv, true); 206 #endif 207 208 if (err == 0) 209 { 210 ErrorCode = SDLErr_OpenAudio; 211 return SDLErr_Error; 212 } 213 214 #if (SDL_MAJOR_VERSION >= 2) 215 char *drivername = Xstrdup(SDL_GetCurrentAudioDriver()); 216 217 for (int i=0;drivername[i] != 0;++i) 218 drivername[i] = toupperlookup[drivername[i]]; 219 220 auto devname = Xstrdup(SDL_GetAudioDeviceName(0, 0)); 221 auto pdevname = Bstrchr(devname, '('); 222 223 if (pdevname) 224 { 225 auto rt = Bstrchr(pdevname++, ')'); 226 if (rt != nullptr) *rt = '\0'; 227 } 228 else 229 pdevname = devname; 230 231 VLOG_F(LOG_ASS, "Using SDL %s driver on %s", drivername, pdevname); 232 233 Xfree(devname); 234 Xfree(drivername); 235 #else 236 char drivernamestr[64] = "(error)"; 237 SDL_AudioDriverName(drivernamestr, sizeof(drivernamestr)); 238 VLOG_F(LOG_ASS, "Using SDL %s driver", drivernamestr); 239 VLOG_F(LOG_ASS, "SDL %s driver", drivernamestr); 240 241 if (actual.freq == 0 || actual.channels == 0) 242 { 243 // hack for when SDL said it opened the audio, but clearly didn't 244 SDL_CloseAudio(); 245 ErrorCode = SDLErr_OpenAudio; 246 return SDLErr_Error; 247 } 248 #endif 249 err = 0; 250 251 *mixrate = actual.freq; 252 if (actual.format == AUDIO_U8 || actual.format == AUDIO_S8) 253 { 254 ErrorCode = SDLErr_OpenAudio; 255 err = 1; 256 } 257 258 *numchannels = actual.channels; 259 if (actual.channels != 1 && actual.channels != 2) 260 { 261 ErrorCode = SDLErr_OpenAudio; 262 err = 1; 263 } 264 265 if (err) 266 { 267 SDL_CloseAudio(); 268 return SDLErr_Error; 269 } 270 271 Initialised = 1; 272 return SDLErr_Ok; 273 } 274 275 void SDLDrv_PCM_Shutdown(void) 276 { 277 if (!Initialised) 278 return; 279 280 if (StartedSDL) 281 SDL_QuitSubSystem(StartedSDL); 282 283 StartedSDL = 0; 284 Initialised = 0; 285 #if SDL_MAJOR_VERSION >= 2 286 SDLDrv_Cleanup(); 287 #endif 288 } 289 290 int SDLDrv_PCM_BeginPlayback(char *BufferStart, int BufferSize, 291 int NumDivisions, void ( *CallBackFunc )( void ) ) 292 { 293 if (!Initialised) 294 { 295 ErrorCode = SDLErr_Uninitialised; 296 return SDLErr_Error; 297 } 298 299 if (Playing) 300 SDLDrv_PCM_StopPlayback(); 301 302 MixBuffer = BufferStart; 303 MixBufferSize = BufferSize; 304 MixBufferCount = NumDivisions; 305 MixBufferCurrent = 0; 306 MixBufferUsed = 0; 307 MixCallBack = CallBackFunc; 308 309 // prime the buffer 310 MixCallBack(); 311 312 #if (SDL_MAJOR_VERSION >= 2) 313 SDL_PauseAudioDevice(audio_dev, 0); 314 #else 315 SDL_PauseAudio(0); 316 #endif 317 Playing = 1; 318 319 return SDLErr_Ok; 320 } 321 322 void SDLDrv_PCM_StopPlayback(void) 323 { 324 if (!Initialised || !Playing) 325 return; 326 327 #if (SDL_MAJOR_VERSION >= 2) 328 SDL_PauseAudioDevice(audio_dev, 1); 329 #else 330 SDL_PauseAudio(1); 331 #endif 332 333 Playing = 0; 334 } 335 336 void SDLDrv_PCM_Lock(void) 337 { 338 #if (SDL_MAJOR_VERSION >= 2) 339 SDL_LockAudioDevice(audio_dev); 340 #else 341 SDL_LockAudio(); 342 #endif 343 } 344 345 void SDLDrv_PCM_Unlock(void) 346 { 347 #if (SDL_MAJOR_VERSION >= 2) 348 SDL_UnlockAudioDevice(audio_dev); 349 #else 350 SDL_UnlockAudio(); 351 #endif 352 }