/ source / audiolib / src / fx_man.cpp
fx_man.cpp
  1  //-------------------------------------------------------------------------
  2  /*
  3  Copyright (C) 2016 EDuke32 developers and contributors
  4  
  5  This file is part of EDuke32.
  6  
  7  EDuke32 is free software; you can redistribute it and/or
  8  modify it under the terms of the GNU General Public License version 2
  9  as published by the Free Software Foundation.
 10  
 11  This program is distributed in the hope that it will be useful,
 12  but WITHOUT ANY WARRANTY; without even the implied warranty of
 13  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 14  
 15  See the GNU General Public License for more details.
 16  
 17  You should have received a copy of the GNU General Public License
 18  along with this program; if not, write to the Free Software
 19  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 20  */
 21  //-------------------------------------------------------------------------
 22  
 23  #include "fx_man.h"
 24  
 25  #include "_multivc.h"
 26  #include "compat.h"
 27  #include "drivers.h"
 28  #include "driver_adlib.h"
 29  #include "driver_sf2.h"
 30  #include "midi.h"
 31  #include "multivoc.h"
 32  #include "music.h"
 33  #include "osd.h"
 34  
 35  #ifdef _WIN32
 36  # include "driver_winmm.h"
 37  # include <mmsystem.h>
 38  #endif
 39  
 40  #ifdef RENDERTYPESDL
 41  # include "driver_sdl.h"
 42  #endif
 43  
 44  #ifdef __linux__
 45  # include "driver_alsa.h"
 46  #endif
 47  
 48  int FX_ErrorCode = FX_Ok;
 49  int FX_Installed;
 50  int FX_MixRate;
 51  
 52  const char *FX_ErrorString(int const ErrorNumber)
 53  {
 54      switch (ErrorNumber)
 55      {
 56          case FX_Warning:
 57          case FX_Error:          return FX_ErrorString(FX_ErrorCode);
 58          case FX_Ok:             return "Fx ok.";
 59          case FX_MultiVocError:  return MV_ErrorString(MV_Error);
 60          default:                return "Unknown Fx error code.";
 61      }
 62  }
 63  
 64  static int osdcmd_cvar_set_audiolib(osdcmdptr_t parm)
 65  {
 66      int32_t r = osdcmd_cvar_set(parm);
 67  
 68      if (parm->numparms == 0)
 69      {
 70  #ifdef _WIN32
 71          if (!Bstrcasecmp(parm->name, "mus_mme_device"))
 72              WinMMDrv_MIDI_PrintDevices();
 73  #endif
 74  #if defined RENDERTYPESDL && SDL_MAJOR_VERSION >= 2
 75          if (!Bstrcasecmp(parm->name, "snd_sdl_audiodriver"))
 76              SDLDrv_PCM_PrintDrivers();
 77  #endif
 78      }
 79  
 80      if (r != OSDCMD_OK || parm->numparms < 1) return r;
 81  
 82      if (ASS_MIDISoundDriver == ASS_OPL3 && !Bstrcasecmp(parm->name, "mus_emidicard"))
 83          MIDI_Restart();
 84  #ifdef _WIN32
 85      else if (ASS_MIDISoundDriver == ASS_WinMM && !Bstrcasecmp(parm->name, "mus_mme_device"))
 86          MIDI_Restart();
 87  #endif
 88  #ifdef __linux__
 89      else if (ASS_MIDISoundDriver == ASS_ALSA && (!Bstrcasecmp(parm->name, "mus_alsa_clientid") || !Bstrcasecmp(parm->name, "mus_alsa_portid")))
 90          MIDI_Restart();
 91  #endif
 92      else if (ASS_MIDISoundDriver == ASS_SF2 && (!Bstrcasecmp(parm->name, "mus_sf2_bank") || !Bstrcasecmp(parm->name, "mus_sf2_sampleblocksize")))
 93          MIDI_Restart();
 94      else if (!Bstrcasecmp(parm->name, "mus_al_stereo"))
 95          AL_SetStereo(AL_Stereo);
 96  #ifdef HAVE_XMP
 97      else if (!Bstrcasecmp(parm->name, "mus_xmp_interpolation"))
 98          MV_SetXMPInterpolation(MV_XMPInterpolation);
 99  #endif
100  #if defined RENDERTYPESDL && SDL_MAJOR_VERSION >= 2
101      else if (!Bstrcasecmp(parm->name, "snd_sdl_audiodriver"))
102      {
103          if (!FX_Installed || !Bstrcasecmp(parm->parms[0], SDLDrv_PCM_GetDriverName()))
104              return r;
105  
106          if (!SDLDrv_PCM_CheckDriverName(parm->parms[0]))
107          {
108              SDLDrv_PCM_PrintDrivers();
109              return r;
110          }
111  
112          FX_Init(MV_MaxVoices, MV_Channels, MV_MixRate, MV_InitDataPtr);
113      }
114  #endif
115  
116      return r;
117  }
118  
119  void FX_InitCvars(void)
120  {
121      static osdcvardata_t cvars_audiolib [] ={
122          { "mus_emidicard", "force a specific EMIDI instrument set", (void*) &ASS_EMIDICard, CVAR_INT | CVAR_FUNCPTR, -1, 10 },
123  #ifdef __linux__
124          { "mus_alsa_clientid", "specify the ALSA MIDI client ID", (void*) &ALSA_ClientID, CVAR_INT | CVAR_FUNCPTR, 0, 255 },
125          { "mus_alsa_portid", "specify the ALSA MIDI port ID", (void*) &ALSA_PortID, CVAR_INT | CVAR_FUNCPTR, 0, 15 },
126  #endif
127          { "mus_al_additivemode", "enable/disable alternate additive AdLib timbre mode", (void*) &AL_AdditiveMode, CVAR_BOOL, 0, 1 },
128          { "mus_al_postamp", "controls post-synthesization OPL3 volume amplification", (void*) &AL_PostAmp, CVAR_FLOAT, 1, 6 },
129          { "mus_al_stereo", "enable/disable OPL3 stereo mode", (void*) &AL_Stereo, CVAR_BOOL | CVAR_FUNCPTR, 0, 1 },
130          { "mus_sf2_bank", "SoundFont 2 (.sf2) bank filename",  (void*) SF2_BankFile, CVAR_STRING | CVAR_FUNCPTR, 0, sizeof(SF2_BankFile) - 1 },
131          { "mus_sf2_sampleblocksize", "number of samples per effect processing block", (void*) &SF2_EffectSampleBlockSize, CVAR_INT | CVAR_FUNCPTR, 1, 64 },
132  #ifdef _WIN32
133          { "mus_mme_device", "select Windows MME MIDI output device", (void*) &WinMM_DeviceID, CVAR_INT | CVAR_FUNCPTR, -1, WinMMDrv_MIDI_GetNumDevices()-1 },
134  #endif
135  #if defined HAVE_XMP
136          { "mus_xmp_interpolation", "XMP output interpolation: 0: none  1: linear  2: spline", (void*) &MV_XMPInterpolation, CVAR_INT | CVAR_FUNCPTR, 0, 2 },
137  #endif
138  #if defined RENDERTYPESDL && SDL_MAJOR_VERSION >= 2
139          { "snd_sdl_audiodriver", "select SDL audio driver (platform-specific)",
140            (void *)SDLAudioDriverName, CVAR_STRING | CVAR_FUNCPTR, 0, sizeof(SDLAudioDriverName) - 1 },
141  #endif
142          { "snd_lazyalloc", "use lazy sound allocations", (void*) &MV_LazyAlloc, CVAR_BOOL, 0, 1 },
143      };
144  
145      for (auto& i : cvars_audiolib)
146          OSD_RegisterCvar(&i, (i.flags & CVAR_FUNCPTR) ? osdcmd_cvar_set_audiolib : osdcmd_cvar_set);
147  
148  #ifdef _WIN32
149      OSD_RegisterFunction("mus_mme_debuginfo", "Windows MME MIDI buffer debug information", WinMMDrv_MIDI_PrintBufferInfo);
150  #endif
151  }
152  
153  int FX_Init(int numvoices, int numchannels, int mixrate, void* initdata)
154  {
155      if (FX_Installed)
156          FX_Shutdown();
157  
158  #if defined RENDERTYPESDL
159      int SoundCard = ASS_SDL;
160  #elif defined RENDERTYPEWIN
161      int SoundCard = ASS_DirectSound;
162  #endif
163  
164      VLOG_F(LOG_ASS, "Initializing Apogee Sound System");
165  
166      if (SoundDriver_IsPCMSupported(SoundCard) == 0)
167      {
168          // unsupported cards fall back to no sound
169          FX_SetErrorCode(FX_InvalidCard);
170          return FX_Error;
171      }
172  
173      int status = FX_Ok;
174  
175      if (MV_Init(SoundCard, mixrate, numvoices, numchannels, initdata) != MV_Ok)
176      {
177          FX_SetErrorCode(FX_MultiVocError);
178          LOG_F(ERROR, "Failed initializing sound: %s", MV_ErrorString(MV_DriverError));
179          status = FX_Error;
180      }
181  
182      FX_MixRate = MV_MixRate;
183  
184      if (status == FX_Ok)
185      {
186          VLOG_F(LOG_ASS, "Initialized sound at %.1f KHz %s with %d voices", MV_MixRate/1000.f, numchannels == 1 ? "mono" : "stereo", numvoices);
187          FX_Installed = TRUE;
188      }
189  
190      return status;
191  }
192  
193  int FX_Shutdown(void)
194  {
195      if (!FX_Installed)
196          return FX_Ok;
197  
198      int status = MV_Shutdown();
199  
200      if (status != MV_Ok)
201      {
202          FX_SetErrorCode(FX_MultiVocError);
203          status = FX_Error;
204      }
205  
206      FX_Installed = FALSE;
207  
208      return status;
209  }
210  
211  int FX_GetDevice(void) { return ASS_PCMSoundDriver; }
212  
213  
214  #define FMT_MAGIC(i, j, k, l) (i + (j << 8) + (k << 16) + (l << 24))
215  uint32_t constexpr FMT_CDXA_MAGIC = FMT_MAGIC('C','D','X','A');
216  uint32_t constexpr FMT_FLAC_MAGIC = FMT_MAGIC('f','L','a','C');
217  uint32_t constexpr FMT_OGG_MAGIC  = FMT_MAGIC('O','g','g','S');
218  uint32_t constexpr FMT_RIFF_MAGIC = FMT_MAGIC('R','I','F','F');
219  uint32_t constexpr FMT_VOC_MAGIC  = FMT_MAGIC('C','r','e','a');
220  uint32_t constexpr FMT_WAVE_MAGIC = FMT_MAGIC('W','A','V','E');
221  #undef FMT_MAGIC
222  
223  static wavefmt_t FX_ReadFmt(char const * const ptr, uint32_t length)
224  {
225      if (length < 16)
226          return FMT_UNKNOWN;
227  
228      switch (B_LITTLE32(*(uint32_t const *)ptr))
229      {
230          case FMT_OGG_MAGIC:  return FMT_VORBIS;
231          case FMT_VOC_MAGIC:  return FMT_VOC;
232          case FMT_FLAC_MAGIC: return FMT_FLAC;
233          case FMT_RIFF_MAGIC:
234              if (B_LITTLE32(((uint32_t const *)ptr)[2]) == FMT_WAVE_MAGIC) return FMT_WAV;
235              if (B_LITTLE32(((uint32_t const *)ptr)[2]) == FMT_CDXA_MAGIC) return FMT_XA;
236              break;
237          default:
238              if (MV_IdentifyXMP(ptr, length)) return FMT_XMP;
239              break;
240      }
241  
242      return FMT_UNKNOWN;
243  }
244  
245  static int FX_BadFmt(char *, uint32_t, int, int, int, int, int, int, int, fix16_t, intptr_t) { return MV_SetErrorCode(MV_InvalidFile); }
246  static int FX_BadFmt3D(char *, uint32_t, int, int, int, int, int, fix16_t, intptr_t)         { return MV_SetErrorCode(MV_InvalidFile); }
247  
248  int FX_Play(char *ptr, uint32_t ptrlength, int loopstart, int loopend, int pitchoffset,
249              int vol, int left, int right, int priority, fix16_t volume, intptr_t callbackval)
250  {
251      static constexpr decltype(FX_Play) *func[] = { FX_BadFmt, nullptr, MV_PlayVOC, MV_PlayWAV, MV_PlayVorbis, MV_PlayFLAC, MV_PlayXA, MV_PlayXMP };
252  
253      EDUKE32_STATIC_ASSERT(FMT_MAX == ARRAY_SIZE(func));
254  
255      int handle = func[FX_ReadFmt(ptr, ptrlength)](ptr, ptrlength, loopstart, loopend, pitchoffset, vol, left, right, priority, volume, callbackval);
256  
257      if (EDUKE32_PREDICT_FALSE(handle <= MV_Ok))
258      {
259          FX_SetErrorCode(FX_MultiVocError);
260          handle = FX_Warning;
261      }
262  
263      return handle;
264  }
265  
266  int FX_Play3D(char *ptr, uint32_t ptrlength, int loophow, int pitchoffset, int angle, int distance,
267                int priority, fix16_t volume, intptr_t callbackval)
268  {
269      static constexpr decltype(FX_Play3D) *func[] = { FX_BadFmt3D, nullptr, MV_PlayVOC3D, MV_PlayWAV3D, MV_PlayVorbis3D, MV_PlayFLAC3D, MV_PlayXA3D, MV_PlayXMP3D };
270  
271      EDUKE32_STATIC_ASSERT(FMT_MAX == ARRAY_SIZE(func));
272  
273      int handle = func[FX_ReadFmt(ptr, ptrlength)](ptr, ptrlength, loophow, pitchoffset, angle, distance, priority, volume, callbackval);
274  
275      if (EDUKE32_PREDICT_FALSE(handle <= MV_Ok))
276      {
277          FX_SetErrorCode(FX_MultiVocError);
278          handle = FX_Warning;
279      }
280  
281      return handle;
282  }
283  
284  int FX_PlayRaw(char *ptr, uint32_t ptrlength, int rate, int pitchoffset, int vol,
285      int left, int right, int priority, fix16_t volume, intptr_t callbackval)
286  {
287      int handle = MV_PlayRAW(ptr, ptrlength, rate, NULL, NULL, pitchoffset, vol, left, right, priority, volume, callbackval);
288  
289      if (EDUKE32_PREDICT_FALSE(handle <= MV_Ok))
290      {
291          FX_SetErrorCode(FX_MultiVocError);
292          handle = FX_Warning;
293      }
294  
295      return handle;
296  }
297  
298  int FX_PlayLoopedRaw(char *ptr, uint32_t ptrlength, char *loopstart, char *loopend, int rate,
299      int pitchoffset, int vol, int left, int right, int priority, fix16_t volume, intptr_t callbackval)
300  {
301      int handle = MV_PlayRAW(ptr, ptrlength, rate, loopstart, loopend, pitchoffset, vol, left, right, priority, volume, callbackval);
302  
303      if (EDUKE32_PREDICT_FALSE(handle <= MV_Ok))
304      {
305          FX_SetErrorCode(FX_MultiVocError);
306          handle = FX_Warning;
307      }
308  
309      return handle;
310  }
311  
312  int FX_StartDemandFeedPlayback(void (*function)(const char** ptr, uint32_t* length, void* userdata), int bitdepth, int channels, int rate, int pitchoffset,
313      int vol, int left, int right, int priority, fix16_t volume, intptr_t callbackval, void* userdata)
314  {
315      int handle = MV_StartDemandFeedPlayback(function, bitdepth, channels, rate,
316          pitchoffset, vol, left, right, priority, volume, callbackval, userdata);
317  
318      if (EDUKE32_PREDICT_FALSE(handle <= MV_Ok))
319      {
320          FX_SetErrorCode(FX_MultiVocError);
321          handle = FX_Warning;
322      }
323  
324      return handle;
325  }
326  
327  int FX_StartDemandFeedPlayback3D(void (*function)(const char** ptr, uint32_t* length, void* userdata), int bitdepth, int channels, int rate, int pitchoffset,
328      int angle, int distance, int priority, fix16_t volume, intptr_t callbackval, void* userdata)
329  {
330      int handle = MV_StartDemandFeedPlayback3D(function, bitdepth, channels, rate,
331          pitchoffset, angle, distance, priority, volume, callbackval, userdata);
332  
333      if (EDUKE32_PREDICT_FALSE(handle <= MV_Ok))
334      {
335          FX_SetErrorCode(FX_MultiVocError);
336          handle = FX_Warning;
337      }
338  
339      return handle;
340  }