in doom_py/src/vizdoom/src/sound/fmodsound.cpp [641:1176]
bool FMODSoundRenderer::Init()
{
FMOD_RESULT result;
unsigned int version;
FMOD_SPEAKERMODE speakermode;
FMOD_SOUND_FORMAT format;
FMOD_DSP_RESAMPLER resampler;
FMOD_INITFLAGS initflags;
int samplerate;
int driver;
int eval;
SFXPaused = 0;
DSPLocked = false;
MusicGroup = NULL;
SfxGroup = NULL;
PausableSfx = NULL;
SfxConnection = NULL;
WaterLP = NULL;
WaterReverb = NULL;
PrevEnvironment = DefaultEnvironments[0];
DSPClock.AsOne = 0;
ChannelGroupTargetUnit = NULL;
ChannelGroupTargetUnitOutput = NULL;
SfxReverbHooked = false;
SfxReverbPlaceholder = NULL;
OutputPlugin = 0;
Printf("I_InitSound: Initializing FMOD\n");
// This is just for safety. Normally this should never be called if FMod Ex cannot be found.
if (!IsFModExPresent())
{
Sys = NULL;
Printf(TEXTCOLOR_ORANGE"Failed to load fmodex"
#ifdef _WIN64
"64"
#endif
".dll\n");
return false;
}
// Create a System object and initialize.
result = FMOD::System_Create(&Sys);
if (result != FMOD_OK)
{
Sys = NULL;
Printf(TEXTCOLOR_ORANGE"Failed to create FMOD system object: Error %d\n", result);
return false;
}
result = Sys->getVersion(&version);
if (result != FMOD_OK)
{
Printf(TEXTCOLOR_ORANGE"Could not validate FMOD version: Error %d\n", result);
return false;
}
const char *wrongver = NULL;
#if FMOD_VERSION >= 0x43600
if (version < 0x43600)
#else
if (version < 0x42000)
#endif
{
wrongver = "an old";
}
#if FMOD_VERSION < 0x42700
else if ((version & 0xFFFF00) > 0x42600)
#else
else if ((version & 0xFFFF00) > (FMOD_VERSION & 0xFFFF00))
#endif
{
wrongver = "a new";
}
if (wrongver != NULL)
{
Printf (" " TEXTCOLOR_ORANGE "Error! You are using %s version of FMOD (%x.%02x.%02x).\n"
" " TEXTCOLOR_ORANGE "This program was built for version %x.%02x.%02x\n",
wrongver,
version >> 16, (version >> 8) & 255, version & 255,
FMOD_VERSION >> 16, (FMOD_VERSION >> 8) & 255, FMOD_VERSION & 255);
return false;
}
ActiveFMODVersion = version;
if (!ShowedBanner)
{
// '\xa9' is the copyright symbol in the Windows-1252 code page.
Printf("FMOD Sound System, copyright \xa9 Firelight Technologies Pty, Ltd., 1994-2009.\n");
Printf("Loaded FMOD version %x.%02x.%02x\n", version >> 16, (version >> 8) & 255, version & 255);
ShowedBanner = true;
}
#ifdef _WIN32
if (OSPlatform == os_WinNT4)
{
// The following was true as of FMOD 3. I don't know if it still
// applies to FMOD Ex, nor do I have an NT 4 install anymore, but
// there's no reason to get rid of it yet.
//
// If running Windows NT 4, we need to initialize DirectSound before
// using WinMM. If we don't, then FSOUND_Close will corrupt a
// heap. This might just be the Audigy's drivers--I don't know why
// it happens. At least the fix is simple enough. I only need to
// initialize DirectSound once, and then I can initialize/close
// WinMM as many times as I want.
//
// Yes, using WinMM under NT 4 is a good idea. I can get latencies as
// low as 20 ms with WinMM, but with DirectSound I need to have the
// latency as high as 120 ms to avoid crackling--quite the opposite
// from the other Windows versions with real DirectSound support.
static bool inited_dsound = false;
if (!inited_dsound)
{
if (Sys->setOutput(FMOD_OUTPUTTYPE_DSOUND) == FMOD_OK)
{
if (Sys->init(1, FMOD_INIT_NORMAL, 0) == FMOD_OK)
{
inited_dsound = true;
Sleep(50);
Sys->close();
}
Sys->setOutput(FMOD_OUTPUTTYPE_WINMM);
}
}
}
#endif
#if !defined _WIN32 && !defined __APPLE__
// Try to load SDL output plugin
result = Sys->setPluginPath(progdir); // Should we really look for it in the program directory?
result = Sys->loadPlugin("liboutput_sdl.so", &OutputPlugin);
if (result != FMOD_OK)
{
OutputPlugin = 0;
}
#endif
// Set the user specified output mode.
eval = Enum_NumForName(OutputNames, snd_output);
if (eval >= 0)
{
if (eval == 666 && OutputPlugin != 0)
{
result = Sys->setOutputByPlugin(OutputPlugin);
}
else
{
result = Sys->setOutput(FMOD_OUTPUTTYPE(eval));
}
if (result != FMOD_OK)
{
Printf(TEXTCOLOR_BLUE"Setting output type '%s' failed. Using default instead. (Error %d)\n", *snd_output, result);
eval = FMOD_OUTPUTTYPE_AUTODETECT;
Sys->setOutput(FMOD_OUTPUTTYPE_AUTODETECT);
}
}
result = Sys->getNumDrivers(&driver);
#ifdef __unix__
if (result == FMOD_OK)
{
// On Linux, FMOD defaults to OSS. If OSS is not present, it doesn't
// try ALSA; it just fails. We'll try for it, but only if OSS wasn't
// explicitly specified for snd_output.
if (driver == 0 && eval == FMOD_OUTPUTTYPE_AUTODETECT)
{
FMOD_OUTPUTTYPE output;
if (FMOD_OK == Sys->getOutput(&output))
{
if (output == FMOD_OUTPUTTYPE_OSS)
{
Printf(TEXTCOLOR_BLUE"OSS could not be initialized. Trying ALSA.\n");
Sys->setOutput(FMOD_OUTPUTTYPE_ALSA);
result = Sys->getNumDrivers(&driver);
}
}
}
}
#endif
if (result == FMOD_OK)
{
if (driver == 0)
{
Printf(TEXTCOLOR_ORANGE"No working sound devices found. Try a different snd_output?\n");
return false;
}
if (snd_driver >= driver)
{
Printf(TEXTCOLOR_BLUE"Driver %d does not exist. Using 0.\n", *snd_driver);
driver = 0;
}
else
{
driver = snd_driver;
}
result = Sys->setDriver(driver);
}
result = Sys->getDriver(&driver);
#if FMOD_VERSION >= 0x43600
// We were built with an FMOD that only returns the control panel frequency
result = Sys->getDriverCaps(driver, &Driver_Caps, &Driver_MinFrequency, &speakermode);
Driver_MaxFrequency = Driver_MinFrequency;
#else
// We were built with an FMOD that returns a frequency range
result = Sys->getDriverCaps(driver, &Driver_Caps, &Driver_MinFrequency, &Driver_MaxFrequency, &speakermode);
#endif
if (result != FMOD_OK)
{
Printf(TEXTCOLOR_BLUE"Could not ascertain driver capabilities. Some things may be weird. (Error %d)\n", result);
// Fill in some default to pretend it worked. (But as long as we specify a valid driver,
// can this call actually fail?)
Driver_Caps = 0;
Driver_MinFrequency = 4000;
Driver_MaxFrequency = 48000;
speakermode = FMOD_SPEAKERMODE_STEREO;
}
// Set the user selected speaker mode.
eval = Enum_NumForName(SpeakerModeNames, snd_speakermode);
if (eval >= 0)
{
speakermode = FMOD_SPEAKERMODE(eval);
}
result = Sys->setSpeakerMode(speakermode);
if (result != FMOD_OK)
{
Printf(TEXTCOLOR_BLUE"Could not set speaker mode to '%s'. (Error %d)\n", *snd_speakermode, result);
}
// Set software format
eval = Enum_NumForName(SoundFormatNames, snd_output_format);
format = eval >= 0 ? FMOD_SOUND_FORMAT(eval) : FMOD_SOUND_FORMAT_PCM16;
if (format == FMOD_SOUND_FORMAT_PCM8)
{
// PCM-8 sounds like garbage with anything but DirectSound.
FMOD_OUTPUTTYPE output;
if (FMOD_OK != Sys->getOutput(&output) || output != FMOD_OUTPUTTYPE_DSOUND)
{
format = FMOD_SOUND_FORMAT_PCM16;
}
}
eval = Enum_NumForName(ResamplerNames, snd_resampler);
resampler = eval >= 0 ? FMOD_DSP_RESAMPLER(eval) : FMOD_DSP_RESAMPLER_LINEAR;
// These represented the frequency limits for hardware channels, which we never used anyway.
// samplerate = clamp<int>(snd_samplerate, Driver_MinFrequency, Driver_MaxFrequency);
samplerate = snd_samplerate;
if (samplerate == 0 || snd_samplerate == 0)
{ // Creative's ASIO drivers report the only supported frequency as 0!
if (FMOD_OK != Sys->getSoftwareFormat(&samplerate, NULL, NULL, NULL, NULL, NULL))
{
samplerate = 48000;
}
}
if (samplerate != snd_samplerate && snd_samplerate != 0)
{
Printf(TEXTCOLOR_BLUE"Sample rate %d is unsupported. Trying %d.\n", *snd_samplerate, samplerate);
}
result = Sys->setSoftwareFormat(samplerate, format, 0, 0, resampler);
if (result != FMOD_OK)
{
Printf(TEXTCOLOR_BLUE"Could not set mixing format. Defaults will be used. (Error %d)\n", result);
}
// Set software channels according to snd_channels
result = Sys->setSoftwareChannels(snd_channels + NUM_EXTRA_SOFTWARE_CHANNELS);
if (result != FMOD_OK)
{
Printf(TEXTCOLOR_BLUE"Failed to set the preferred number of channels. (Error %d)\n", result);
}
if (Driver_Caps & FMOD_CAPS_HARDWARE_EMULATED)
{ // The user has the 'Acceleration' slider set to off!
// This is really bad for latency!
Printf (TEXTCOLOR_BLUE"Warning: The sound acceleration slider has been set to off.\n");
Printf (TEXTCOLOR_BLUE"Please turn it back on if you want decent sound.\n");
result = Sys->setDSPBufferSize(1024, 10); // At 48khz, the latency between issuing an fmod command and hearing it will now be about 213ms.
}
else if (snd_buffersize != 0 || snd_buffercount != 0)
{
int buffersize = snd_buffersize ? snd_buffersize : 1024;
int buffercount = snd_buffercount ? snd_buffercount : 4;
result = Sys->setDSPBufferSize(buffersize, buffercount);
}
else
{
result = FMOD_OK;
}
if (result != FMOD_OK)
{
Printf(TEXTCOLOR_BLUE"Setting DSP buffer size failed. (Error %d)\n", result);
}
// Try to init
initflags = FMOD_INIT_NORMAL;
if (snd_hrtf)
{
// These flags are the same thing, just with different names.
#ifdef FMOD_INIT_SOFTWARE_HRTF
initflags |= FMOD_INIT_SOFTWARE_HRTF;
#else
initflags |= FMOD_INIT_HRTF_LOWPASS;
#endif
}
if (snd_profile)
{
initflags |= FMOD_INIT_ENABLE_PROFILE;
}
for (;;)
{
result = Sys->init(MAX(*snd_channels, MAX_CHANNELS), initflags, 0);
if (result == FMOD_ERR_OUTPUT_CREATEBUFFER)
{
// Possible causes of a buffer creation failure:
// 1. The speaker mode selected isn't supported by this soundcard. Force it to stereo.
// 2. The output format is unsupported. Force it to 16-bit PCM.
// 3. ???
result = Sys->getSpeakerMode(&speakermode);
if (result == FMOD_OK &&
speakermode != FMOD_SPEAKERMODE_STEREO &&
FMOD_OK == Sys->setSpeakerMode(FMOD_SPEAKERMODE_STEREO))
{
Printf(TEXTCOLOR_RED" Buffer creation failed. Retrying with stereo output.\n");
continue;
}
result = Sys->getSoftwareFormat(&samplerate, &format, NULL, NULL, &resampler, NULL);
if (result == FMOD_OK &&
format != FMOD_SOUND_FORMAT_PCM16 &&
FMOD_OK == Sys->setSoftwareFormat(samplerate, FMOD_SOUND_FORMAT_PCM16, 0, 0, resampler))
{
Printf(TEXTCOLOR_RED" Buffer creation failed. Retrying with PCM-16 output.\n");
continue;
}
}
else if (result == FMOD_ERR_NET_SOCKET_ERROR && (initflags & FMOD_INIT_ENABLE_PROFILE))
{
Printf(TEXTCOLOR_RED" Could not create socket. Retrying without profiling.\n");
initflags &= ~FMOD_INIT_ENABLE_PROFILE;
continue;
}
#ifdef _WIN32
else if (result == FMOD_ERR_OUTPUT_INIT)
{
FMOD_OUTPUTTYPE output;
result = Sys->getOutput(&output);
if (result == FMOD_OK && output != FMOD_OUTPUTTYPE_DSOUND)
{
Printf(TEXTCOLOR_BLUE" Init failed for output type %s. Retrying with DirectSound.\n",
Enum_NameForNum(OutputNames, output));
if (FMOD_OK == Sys->setOutput(FMOD_OUTPUTTYPE_DSOUND))
{
continue;
}
}
}
#endif
break;
}
if (result != FMOD_OK)
{ // Initializing FMOD failed. Cry cry.
Printf(TEXTCOLOR_ORANGE" System::init returned error code %d\n", result);
return false;
}
// Create channel groups
result = Sys->createChannelGroup("Music", &MusicGroup);
if (result != FMOD_OK)
{
Printf(TEXTCOLOR_ORANGE" Could not create music channel group. (Error %d)\n", result);
return false;
}
result = Sys->createChannelGroup("SFX", &SfxGroup);
if (result != FMOD_OK)
{
Printf(TEXTCOLOR_ORANGE" Could not create sfx channel group. (Error %d)\n", result);
return false;
}
result = Sys->createChannelGroup("Pausable SFX", &PausableSfx);
if (result != FMOD_OK)
{
Printf(TEXTCOLOR_ORANGE" Could not create pausable sfx channel group. (Error %d)\n", result);
return false;
}
result = SfxGroup->addGroup(PausableSfx);
if (result != FMOD_OK)
{
Printf(TEXTCOLOR_BLUE" Could not attach pausable sfx to sfx channel group. (Error %d)\n", result);
}
// Create DSP units for underwater effect
result = Sys->createDSPByType(FMOD_DSP_TYPE_LOWPASS, &WaterLP);
if (result != FMOD_OK)
{
Printf(TEXTCOLOR_BLUE" Could not create underwater lowpass unit. (Error %d)\n", result);
}
else
{
result = Sys->createDSPByType(FMOD_DSP_TYPE_SFXREVERB, &WaterReverb);
if (result != FMOD_OK)
{
Printf(TEXTCOLOR_BLUE" Could not create underwater reverb unit. (Error %d)\n", result);
}
}
// Connect underwater DSP unit between PausableSFX and SFX groups, while
// retaining the connection established by SfxGroup->addGroup().
if (WaterLP != NULL)
{
FMOD::DSP *sfx_head, *pausable_head;
result = SfxGroup->getDSPHead(&sfx_head);
if (result == FMOD_OK)
{
result = sfx_head->getInput(0, &pausable_head, &SfxConnection);
if (result == FMOD_OK)
{
// The placeholder mixer is for reference to where to connect the SFX
// reverb unit once it gets created.
result = Sys->createDSPByType(FMOD_DSP_TYPE_MIXER, &SfxReverbPlaceholder);
if (result == FMOD_OK)
{
// Replace the PausableSFX->SFX connection with
// PausableSFX->ReverbPlaceholder->SFX.
result = SfxReverbPlaceholder->addInput(pausable_head, NULL);
if (result == FMOD_OK)
{
FMOD::DSPConnection *connection;
result = sfx_head->addInput(SfxReverbPlaceholder, &connection);
if (result == FMOD_OK)
{
sfx_head->disconnectFrom(pausable_head);
SfxReverbPlaceholder->setActive(true);
SfxReverbPlaceholder->setBypass(true);
// The placeholder now takes the place of the pausable_head
// for the following connections.
pausable_head = SfxReverbPlaceholder;
SfxConnection = connection;
}
}
else
{
SfxReverbPlaceholder->release();
SfxReverbPlaceholder = NULL;
}
}
result = WaterLP->addInput(pausable_head, NULL);
WaterLP->setActive(false);
WaterLP->setParameter(FMOD_DSP_LOWPASS_CUTOFF, snd_waterlp);
WaterLP->setParameter(FMOD_DSP_LOWPASS_RESONANCE, 2);
if (WaterReverb != NULL)
{
result = WaterReverb->addInput(WaterLP, NULL);
if (result == FMOD_OK)
{
result = sfx_head->addInput(WaterReverb, NULL);
if (result == FMOD_OK)
{
// WaterReverb->setParameter(FMOD_DSP_REVERB_ROOMSIZE, 0.001f);
// WaterReverb->setParameter(FMOD_DSP_REVERB_DAMP, 0.2f);
// These parameters are entirely empirical and can probably
// stand some improvement, but it sounds remarkably close
// to the old reverb unit's output.
WaterReverb->setParameter(FMOD_DSP_SFXREVERB_LFREFERENCE, 150);
WaterReverb->setParameter(FMOD_DSP_SFXREVERB_HFREFERENCE, 10000);
WaterReverb->setParameter(FMOD_DSP_SFXREVERB_ROOM, 0);
WaterReverb->setParameter(FMOD_DSP_SFXREVERB_ROOMHF, -5000);
WaterReverb->setParameter(FMOD_DSP_SFXREVERB_DRYLEVEL, 0);
WaterReverb->setParameter(FMOD_DSP_SFXREVERB_DECAYHFRATIO, 1);
WaterReverb->setParameter(FMOD_DSP_SFXREVERB_DECAYTIME, 0.25f);
WaterReverb->setParameter(FMOD_DSP_SFXREVERB_DENSITY, 100);
WaterReverb->setParameter(FMOD_DSP_SFXREVERB_DIFFUSION, 100);
WaterReverb->setActive(false);
}
}
}
else
{
result = sfx_head->addInput(WaterLP, NULL);
}
}
}
}
LastWaterLP = snd_waterlp;
// Find the FMOD Channel Group Target Unit. To completely eliminate sound
// while the program is deactivated, we can deactivate this DSP unit, and
// all audio processing will cease. This is not directly exposed by the
// API but can be easily located by getting the master channel group and
// tracing its single output, since it is known to hook up directly to the
// Channel Group Target Unit. (See FMOD Profiler for proof.)
FMOD::ChannelGroup *master_group;
result = Sys->getMasterChannelGroup(&master_group);
if (result == FMOD_OK)
{
FMOD::DSP *master_head;
result = master_group->getDSPHead(&master_head);
if (result == FMOD_OK)
{
result = master_head->getOutput(0, &ChannelGroupTargetUnit, NULL);
if (result != FMOD_OK)
{
ChannelGroupTargetUnit = NULL;
}
else
{
FMOD::DSP *dontcare;
result = ChannelGroupTargetUnit->getOutput(0, &dontcare, &ChannelGroupTargetUnitOutput);
if (result != FMOD_OK)
{
ChannelGroupTargetUnitOutput = NULL;
}
}
}
}
if (FMOD_OK != Sys->getSoftwareFormat(&OutputRate, NULL, NULL, NULL, NULL, NULL))
{
OutputRate = 48000; // Guess, but this should never happen.
}
Sys->set3DSettings(0.5f, 96.f, 1.f);
Sys->set3DRolloffCallback(RolloffCallback);
// The default is 16k, which periodically starves later FMOD versions
// when streaming FLAC files.
Sys->setStreamBufferSize(64*1024, FMOD_TIMEUNIT_RAWBYTES);
snd_sfxvolume.Callback ();
return true;
}