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 }