diff --git a/docs/rh-log.txt b/docs/rh-log.txt index 3da199ef3..0e33dbe60 100644 --- a/docs/rh-log.txt +++ b/docs/rh-log.txt @@ -1,4 +1,9 @@ May 14, 2008 +- Revised underwater effect now uses a lowpass filter in combination with an + optional freeverb unit. +- Removed ResetEnvironment hack, since with software reverb, losing the + existing reverb when focus is lost isn't a problem. +- Commented out the TiMidity FIXME messages. - Fixed: FBarShader::GetColumn() passed incorrect information to the software renderer for horizontal bars. diff --git a/src/m_options.cpp b/src/m_options.cpp index 76b5ff8e9..39834bac7 100644 --- a/src/m_options.cpp +++ b/src/m_options.cpp @@ -1142,6 +1142,7 @@ EXTERN_CVAR (Int, snd_buffercount) EXTERN_CVAR (Int, snd_samplerate) EXTERN_CVAR (Bool, snd_hrtf) EXTERN_CVAR (Bool, snd_waterreverb) +EXTERN_CVAR (Float, snd_waterlp) EXTERN_CVAR (Int, snd_mididevice) static void MakeSoundChanges (); @@ -1243,6 +1244,7 @@ static menuitem_t SoundItems[] = { discrete, "MIDI device", {&snd_mididevice}, {0.0}, {0.0}, {0.0}, {NULL} }, { redtext, " ", {NULL}, {0.0}, {0.0}, {0.0}, {NULL} }, { discrete, "Underwater reverb", {&snd_waterreverb}, {2.0}, {0.0}, {0.0}, {OnOff} }, + { slider, "Underwater cutoff", {&snd_waterlp}, {0.0}, {2000.0},{50.0}, {NULL} }, { discrete, "Randomize pitches", {&snd_pitched}, {2.0}, {0.0}, {0.0}, {OnOff} }, { redtext, " ", {NULL}, {0.0}, {0.0}, {0.0}, {NULL} }, { more, "Restart sound", {NULL}, {0.0}, {0.0}, {0.0}, {(value_t *)MakeSoundChanges} }, diff --git a/src/sound/fmodsound.cpp b/src/sound/fmodsound.cpp index c57e12932..e43990a6c 100644 --- a/src/sound/fmodsound.cpp +++ b/src/sound/fmodsound.cpp @@ -119,6 +119,20 @@ CVAR (String, snd_output_format, "PCM-16", CVAR_ARCHIVE|CVAR_GLOBALCONFIG) CVAR (String, snd_midipatchset, "", CVAR_ARCHIVE|CVAR_GLOBALCONFIG) CVAR (Bool, snd_profile, false, 0) +// Underwater low-pass filter cutoff frequency. Set to 0 to disable the filter. +CUSTOM_CVAR (Float, snd_waterlp, 250, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) +{ + // Clamp to the DSP unit's limits. + if (*self < 10 && *self != 0) + { + self = 10; + } + else if (*self > 22000) + { + self = 22000; + } +} + // PRIVATE DATA DEFINITIONS ------------------------------------------------ static const ReverbContainer *PrevEnvironment; @@ -569,6 +583,9 @@ bool FMODSoundRenderer::Init() MusicGroup = NULL; SfxGroup = NULL; PausableSfx = NULL; + SfxConnection = NULL; + WaterLP = NULL; + WaterReverb = NULL; PrevEnvironment = DefaultEnvironments[0]; DSPClockLo = 0; DSPClockHi = 0; @@ -830,9 +847,55 @@ bool FMODSoundRenderer::Init() result = SfxGroup->addGroup(PausableSfx); if (result != FMOD_OK) { - Printf(TEXTCOLOR_BLUE" Could not create attach pausable sfx to sfx channel group. (Error %d)\n", result); + 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_REVERB, &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); + result = sfx_head->getInput(0, &pausable_head, &SfxConnection); + + 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) + { + FMOD::DSPConnection *dry; + result = WaterReverb->addInput(pausable_head, &dry); + result = dry->setMix(0.1f); + result = WaterReverb->addInput(WaterLP, NULL); + result = sfx_head->addInput(WaterReverb, NULL); + WaterReverb->setParameter(FMOD_DSP_REVERB_ROOMSIZE, 0.001f); + WaterReverb->setParameter(FMOD_DSP_REVERB_DAMP, 0.2f); + WaterReverb->setActive(false); + } + else + { + result = sfx_head->addInput(WaterLP, NULL); + } + } + LastWaterLP = snd_waterlp; + result = SPC_CreateCodec(Sys); if (result != FMOD_OK) { @@ -878,6 +941,16 @@ void FMODSoundRenderer::Shutdown() SfxGroup->release(); SfxGroup = NULL; } + if (WaterLP != NULL) + { + WaterLP->release(); + WaterLP = NULL; + } + if (WaterReverb != NULL) + { + WaterReverb->release(); + WaterReverb = NULL; + } // Free all loaded samples for (i = 0; i < S_sfx.Size(); i++) @@ -1490,17 +1563,6 @@ void FMODSoundRenderer::UpdateSoundParams3D(FSoundChan *chan, float pos[3], floa fchan->set3DAttributes((FMOD_VECTOR *)pos, (FMOD_VECTOR *)vel); } -//========================================================================== -// -// FMODSoundRenderer :: ResetEnvironment -// -//========================================================================== - -void FMODSoundRenderer::ResetEnvironment() -{ - PrevEnvironment = NULL; -} - //========================================================================== // // FMODSoundRenderer :: UpdateListener @@ -1549,26 +1611,72 @@ void FMODSoundRenderer::UpdateListener() } else { - underwater = (listener->waterlevel == 3 && snd_waterreverb); + underwater = (listener->waterlevel == 3 && snd_waterlp); assert (zones != NULL); env = zones[listener->Sector->ZoneNumber].Environment; if (env == NULL) { env = DefaultEnvironments[0]; } - if (env == DefaultEnvironments[0] && underwater) +/* if (env == DefaultEnvironments[0] && underwater) { env = DefaultEnvironments[22]; } +*/ } if (env != PrevEnvironment || env->Modified) { DPrintf ("Reverb Environment %s\n", env->Name); const_cast(env)->Modified = false; Sys->setReverbProperties((FMOD_REVERB_PROPERTIES *)(&env->Properties)); - PausableSfx->setPitch(underwater ? 0.64171f : 1); PrevEnvironment = env; } + + if (underwater) + { + //PausableSfx->setPitch(0.64171f); // This appears to be what Duke 3D uses + PausableSfx->setPitch(0.7937005f); // Approx. 4 semitones lower; what Nash suggesetd + if (WaterLP != NULL) + { + if (LastWaterLP != snd_waterlp) + { + LastWaterLP = snd_waterlp; + WaterLP->setParameter(FMOD_DSP_LOWPASS_CUTOFF, snd_waterlp); + } + WaterLP->setActive(true); + if (WaterReverb != NULL && snd_waterreverb) + { + WaterReverb->setActive(true); + WaterReverb->setBypass(false); + SfxConnection->setMix(0); + } + else + { + // Let some of the original mix through so that high frequencies are + // not completely lost. The reverb unit has its own connection and + // preserves dry sounds itself if used. + SfxConnection->setMix(0.1f); + if (WaterReverb != NULL) + { + WaterReverb->setActive(true); + WaterReverb->setBypass(true); + } + } + } + } + else + { + PausableSfx->setPitch(1); + if (WaterLP != NULL) + { + SfxConnection->setMix(1); + WaterLP->setActive(false); + if (WaterReverb != NULL) + { + WaterReverb->setActive(false); + } + } + } } //========================================================================== diff --git a/src/sound/fmodsound.h b/src/sound/fmodsound.h index 86ee01220..04f43092c 100644 --- a/src/sound/fmodsound.h +++ b/src/sound/fmodsound.h @@ -47,7 +47,6 @@ public: void PrintStatus (); void PrintDriversList (); FString GatherStats (); - void ResetEnvironment (); void DrawWaveDebug(int mode); @@ -85,6 +84,9 @@ private: FMOD::System *Sys; FMOD::ChannelGroup *SfxGroup, *PausableSfx; FMOD::ChannelGroup *MusicGroup; + FMOD::DSP *WaterLP, *WaterReverb; + FMOD::DSPConnection *SfxConnection; + float LastWaterLP; // Just for snd_status display int Driver_MinFrequency; diff --git a/src/sound/i_sound.cpp b/src/sound/i_sound.cpp index 0a9ff4793..1c4d02823 100644 --- a/src/sound/i_sound.cpp +++ b/src/sound/i_sound.cpp @@ -208,10 +208,6 @@ void SoundRenderer::DrawWaveDebug(int mode) { } -void SoundRenderer::ResetEnvironment () -{ -} - SoundStream::~SoundStream () { } diff --git a/src/sound/i_sound.h b/src/sound/i_sound.h index 74965ac09..57c70436f 100644 --- a/src/sound/i_sound.h +++ b/src/sound/i_sound.h @@ -106,7 +106,6 @@ public: virtual void PrintStatus () = 0; virtual void PrintDriversList () = 0; virtual FString GatherStats (); - virtual void ResetEnvironment (); virtual void DrawWaveDebug(int mode); }; diff --git a/src/timidity/timidity.cpp b/src/timidity/timidity.cpp index 9f95f9000..e6f8dbbe4 100644 --- a/src/timidity/timidity.cpp +++ b/src/timidity/timidity.cpp @@ -138,7 +138,7 @@ static int read_config_file(const char *name, bool ismain) * before TiMidity kills the note. This may be useful to implement * later, but I don't see any urgent need for it. */ - Printf("FIXME: Implement \"timeout\" in TiMidity config.\n"); + //Printf("FIXME: Implement \"timeout\" in TiMidity config.\n"); } else if (!strcmp(w[0], "copydrumset") /* "copydrumset" drumset */ || !strcmp(w[0], "copybank")) /* "copybank" bank */ @@ -148,7 +148,7 @@ static int read_config_file(const char *name, bool ismain) * the current drumset or bank. May be useful later, but not a * high priority. */ - Printf("FIXME: Implement \"%s\" in TiMidity config.\n", w[0]); + //Printf("FIXME: Implement \"%s\" in TiMidity config.\n", w[0]); } else if (!strcmp(w[0], "undef")) /* "undef" progno */ { @@ -156,7 +156,7 @@ static int read_config_file(const char *name, bool ismain) * Undefines the tone "progno" of the current tone bank (or * drum set?). Not a high priority. */ - Printf("FIXME: Implement \"undef\" in TiMidity config.\n"); + //Printf("FIXME: Implement \"undef\" in TiMidity config.\n"); } else if (!strcmp(w[0], "altassign")) /* "altassign" prog1 prog2 ... */ { @@ -164,7 +164,7 @@ static int read_config_file(const char *name, bool ismain) * Sets the alternate assign for drum set. Whatever that's * supposed to mean. */ - Printf("FIXME: Implement \"altassign\" in TiMidity config.\n"); + //Printf("FIXME: Implement \"altassign\" in TiMidity config.\n"); } else if (!strcmp(w[0], "soundfont")) { @@ -261,7 +261,7 @@ static int read_config_file(const char *name, bool ismain) * apparently it sets some sort of base offset for tone numbers. * Why anyone would want to do this is beyond me. */ - Printf("FIXME: Implement \"progbase\" in TiMidity config.\n"); + //Printf("FIXME: Implement \"progbase\" in TiMidity config.\n"); } else if (!strcmp(w[0], "map")) /* "map" name set1 elem1 set2 elem2 */ { @@ -271,7 +271,7 @@ static int read_config_file(const char *name, bool ismain) * documentation whatsoever for it, but it looks like it's used * for remapping one instrument to another somehow. */ - Printf("FIXME: Implement \"map\" in TiMidity config.\n"); + //Printf("FIXME: Implement \"map\" in TiMidity config.\n"); } /* Standard TiMidity config */ diff --git a/src/win32/i_input.cpp b/src/win32/i_input.cpp index f089c2689..5903e36cf 100644 --- a/src/win32/i_input.cpp +++ b/src/win32/i_input.cpp @@ -737,10 +737,6 @@ LRESULT CALLBACK WndProc (HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) if (wParam) { SetPriorityClass (GetCurrentProcess (), INGAME_PRIORITY_CLASS); - if (GSnd != NULL) - { - GSnd->ResetEnvironment(); - } } else if (!noidle && !netgame) {