/ source / audiolib / src / driver_sdl.cpp
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  }