From 1fa4bbf69cf7d76ef0d2e8c9d78bf9aca01f8cfc Mon Sep 17 00:00:00 2001 From: Randy Heit Date: Sun, 15 Aug 2010 19:54:59 +0000 Subject: [PATCH] - Added FluidSynth support as snd_mididevice -5. Only tested with Linux. I will have to try compiling it myself on Windows to see if it's really that slow or if Ubuntu just ships an unoptimized version, because performance is pretty pathetic when compared to the other options. (I understand that it's a complete SoundFont2 renderer, so it is understandably slower than something like TiMidity++, but still. Does it really need to be around 10x slower? I played with the chorus, reverb, and interpolation settings, and none of them seemed to make much difference in performance.) SVN r2545 (trunk) --- FindFluidSynth.cmake | 23 ++++++++++ src/CMakeLists.txt | 21 +++++++-- src/sound/fmodsound.cpp | 31 +++++++++---- src/sound/fmodsound.h | 3 +- src/sound/i_music.cpp | 18 ++++++++ src/sound/i_music.h | 3 ++ src/sound/i_musicinterns.h | 67 ++++++++++++++++++++++++++- src/sound/music_midi_base.cpp | 9 ++-- src/sound/music_midistream.cpp | 82 +++++++++++++++++++++++++++++++++- 9 files changed, 239 insertions(+), 18 deletions(-) create mode 100644 FindFluidSynth.cmake diff --git a/FindFluidSynth.cmake b/FindFluidSynth.cmake new file mode 100644 index 000000000..7d5cb6a8e --- /dev/null +++ b/FindFluidSynth.cmake @@ -0,0 +1,23 @@ +# - Find fluidsynth +# Find the native fluidsynth includes and library +# +# FLUIDSYNTH_INCLUDE_DIR - where to find fluidsynth.h +# FLUIDSYNTH_LIBRARIES - List of libraries when using fluidsynth. +# FLUIDSYNTH_FOUND - True if fluidsynth found. + + +IF (FLUIDSYNTH_INCLUDE_DIR AND FLUIDSYNTH_LIBRARIES) + # Already in cache, be silent + SET(FluidSynth_FIND_QUIETLY TRUE) +ENDIF (FLUIDSYNTH_INCLUDE_DIR AND FLUIDSYNTH_LIBRARIES) + +FIND_PATH(FLUIDSYNTH_INCLUDE_DIR fluidsynth.h) + +FIND_LIBRARY(FLUIDSYNTH_LIBRARIES NAMES fluidsynth ) +MARK_AS_ADVANCED( FLUIDSYNTH_LIBRARIES FLUIDSYNTH_INCLUDE_DIR ) + +# handle the QUIETLY and REQUIRED arguments and set FLUIDSYNTH_FOUND to TRUE if +# all listed variables are TRUE +INCLUDE(FindPackageHandleStandardArgs) +FIND_PACKAGE_HANDLE_STANDARD_ARGS(FluidSynth DEFAULT_MSG FLUIDSYNTH_LIBRARIES FLUIDSYNTH_INCLUDE_DIR) + diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 80d5e3800..d6c0aa382 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -31,6 +31,10 @@ endif( CMAKE_SIZEOF_VOID_P MATCHES "8" ) # fmodapilinux[64] -or simply- fmod # jpeg-6b # ... +# The recommended method is to put it in the zdoom tree, since its +# headers are unversioned. Especially now that we can't work properly +# with anything newer than 4.26.xx, you probably don't want to use +# a system-wide version. # Construct version numbers for searching for the FMOD library on Linux. set( MINOR_VERSIONS "50" "49" "48" "47" "46" "45" "44" "43" "42" "41" @@ -236,6 +240,10 @@ else( FMOD_LIBRARY ) endif( FMOD_LIBRARY ) +# Search for FluidSynth + +include( ../FindFluidSynth.cmake ) + # Search for NASM if( NOT NO_ASM ) @@ -370,7 +378,8 @@ endif( SSE_MATTERS ) if( CMAKE_COMPILER_IS_GNUCXX ) if( PROFILE ) - set( CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -pg" ) + set( CMAKE_C_FLinclude( FindFluidSynth.cmake ) +AGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -pg" ) set( CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -pg" ) set( CMAKE_C_FLAGS_RELWITHDEBINFO "${CMAKE_C_FLAGS_RELWITHDEBINFO} -pg" ) set( CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO} -pg" ) @@ -476,8 +485,10 @@ add_custom_target( revision_check ALL # Libraries ZDoom needs -set( ZDOOM_LIBS ${ZDOOM_LIBS} "${ZLIB_LIBRARIES}" "${JPEG_LIBRARIES}" "${BZIP2_LIBRARIES}" "${FMOD_LIBRARY}" ) -include_directories( "${ZLIB_INCLUDE_DIR}" "${FMOD_INCLUDE_DIR}" "${BZIP2_INCLUDE_DIR}" "${LZMA_INCLUDE_DIR}" "${JPEG_INCLUDE_DIR}" ) + +message( STATUS "Fluid synth libs: ${FLUIDSYNTH_LIBRARIES}" ) +set( ZDOOM_LIBS ${ZDOOM_LIBS} "${ZLIB_LIBRARIES}" "${JPEG_LIBRARIES}" "${BZIP2_LIBRARIES}" "${FMOD_LIBRARY}" "${FLUIDSYNTH_LIBRARIES}" ) +include_directories( "${ZLIB_INCLUDE_DIR}" "${FMOD_INCLUDE_DIR}" "${BZIP2_INCLUDE_DIR}" "${LZMA_INCLUDE_DIR}" "${JPEG_INCLUDE_DIR}" "${FLUIDSYNTH_INCLUDE_DIR}" ) # Start defining source files for ZDoom @@ -572,6 +583,9 @@ else( SSE_MATTERS ) set( X86_SOURCES ) endif( SSE_MATTERS ) +if( FLUIDSYNTH_FOUND ) + add_definitions( -DHAVE_FLUIDSYNTH ) +endif( FLUIDSYNTH_FOUND ) add_executable( zdoom WIN32 autostart.cpp @@ -793,6 +807,7 @@ add_executable( zdoom WIN32 sound/music_mus_midiout.cpp sound/music_mus_opl.cpp sound/music_stream.cpp + sound/music_fluidsynth_mididevice.cpp sound/music_timidity_mididevice.cpp sound/music_win_mididevice.cpp textures/automaptexture.cpp diff --git a/src/sound/fmodsound.cpp b/src/sound/fmodsound.cpp index 30efe22d9..bcd84b9fd 100644 --- a/src/sound/fmodsound.cpp +++ b/src/sound/fmodsound.cpp @@ -101,6 +101,7 @@ EXTERN_CVAR (Int, snd_buffersize) EXTERN_CVAR (Int, snd_samplerate) EXTERN_CVAR (Bool, snd_pitched) EXTERN_CVAR (Int, snd_channels) +EXTERN_CVAR (String, snd_midipatchset) extern int sfx_empty; @@ -115,7 +116,6 @@ CVAR (Bool, snd_waterreverb, true, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) CVAR (String, snd_resampler, "Linear", CVAR_ARCHIVE|CVAR_GLOBALCONFIG) CVAR (String, snd_speakermode, "Auto", CVAR_ARCHIVE|CVAR_GLOBALCONFIG) 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. @@ -644,6 +644,7 @@ bool FMODSoundRenderer::Init() ChannelGroupTargetUnit = NULL; SfxReverbHooked = false; SfxReverbPlaceholder = NULL; + SfxHeadMixer = NULL; OutputPlugin = 0; Printf("I_InitSound: Initializing FMOD\n"); @@ -706,7 +707,8 @@ bool FMODSoundRenderer::Init() if (!ShowedBanner) { - Printf("FMOD Sound System, copyright © Firelight Technologies Pty, Ltd., 1994-2009.\n"); + // '\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; } @@ -1014,6 +1016,12 @@ bool FMODSoundRenderer::Init() result = Sys->createDSPByType(FMOD_DSP_TYPE_MIXER, &SfxReverbPlaceholder); if (result == FMOD_OK) { + result = Sys->createDSPByType(FMOD_DSP_TYPE_MIXER, &SfxHeadMixer); + result = sfx_head->addInput(SfxHeadMixer, &SfxConnection); + result = sfx_head->disconnectFrom(pausable_head); + sfx_head = SfxHeadMixer; + SfxHeadMixer->setActive(true); + SfxHeadMixer->setBypass(false); // Replace the PausableSFX->SFX connection with // PausableSFX->ReverbPlaceholder->SFX. result = SfxReverbPlaceholder->addInput(pausable_head, NULL); @@ -1023,13 +1031,13 @@ bool FMODSoundRenderer::Init() result = sfx_head->addInput(SfxReverbPlaceholder, &connection); if (result == FMOD_OK) { - sfx_head->disconnectFrom(pausable_head); +// 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; + // SfxConnection = connection; } } else @@ -1038,6 +1046,7 @@ bool FMODSoundRenderer::Init() SfxReverbPlaceholder = NULL; } } +#if 1 result = WaterLP->addInput(pausable_head, NULL); WaterLP->setActive(false); WaterLP->setParameter(FMOD_DSP_LOWPASS_CUTOFF, snd_waterlp); @@ -1069,6 +1078,7 @@ bool FMODSoundRenderer::Init() { result = sfx_head->addInput(WaterLP, NULL); } +#endif } } } @@ -1147,6 +1157,11 @@ void FMODSoundRenderer::Shutdown() SfxReverbPlaceholder->release(); SfxReverbPlaceholder = NULL; } + if (SfxHeadMixer != NULL) + { + SfxHeadMixer->release(); + SfxHeadMixer = NULL; + } Sys->close(); if (OutputPlugin != 0) @@ -1330,10 +1345,10 @@ FString FMODSoundRenderer::GatherStats() #endif out.Format ("%d channels,"TEXTCOLOR_YELLOW"%5.2f"TEXTCOLOR_NORMAL"%% CPU " - "(DSP:"TEXTCOLOR_YELLOW"%2.2f"TEXTCOLOR_NORMAL"%% " - "Stream:"TEXTCOLOR_YELLOW"%2.2f"TEXTCOLOR_NORMAL"%% " - "Geometry:"TEXTCOLOR_YELLOW"%2.2f"TEXTCOLOR_NORMAL"%% " - "Update:"TEXTCOLOR_YELLOW"%2.2f"TEXTCOLOR_NORMAL"%%)", + "(DSP:"TEXTCOLOR_YELLOW"%5.2f"TEXTCOLOR_NORMAL"%% " + "Stream:"TEXTCOLOR_YELLOW"%5.2f"TEXTCOLOR_NORMAL"%% " + "Geometry:"TEXTCOLOR_YELLOW"%5.2f"TEXTCOLOR_NORMAL"%% " + "Update:"TEXTCOLOR_YELLOW"%5.2f"TEXTCOLOR_NORMAL"%%)", channels, total, dsp, stream, geometry, update); return out; } diff --git a/src/sound/fmodsound.h b/src/sound/fmodsound.h index 1460b697b..f15629cb4 100644 --- a/src/sound/fmodsound.h +++ b/src/sound/fmodsound.h @@ -103,7 +103,8 @@ private: FMOD::DSP *WaterLP, *WaterReverb; FMOD::DSPConnection *SfxConnection; FMOD::DSP *ChannelGroupTargetUnit; - FMOD::DSP *SfxReverbPlaceholder; + FMOD::DSP *SfxReverbPlaceholder; + FMOD::DSP *SfxHeadMixer; bool SfxReverbHooked; float LastWaterLP; unsigned int OutputPlugin; diff --git a/src/sound/i_music.cpp b/src/sound/i_music.cpp index e25d4b5d6..51640b747 100644 --- a/src/sound/i_music.cpp +++ b/src/sound/i_music.cpp @@ -162,6 +162,18 @@ void MusInfo::TimidityVolumeChanged() { } +void MusInfo::FluidSettingInt(const char *, int) +{ +} + +void MusInfo::FluidSettingNum(const char *, double) +{ +} + +void MusInfo::FluidSettingStr(const char *, const char *) +{ +} + FString MusInfo::GetStats() { return "No stats available for this song"; @@ -428,6 +440,12 @@ MusInfo *I_RegisterSong (const char *filename, BYTE *musiccache, int offset, int { info = new MUSSong2(file, musiccache, len, MIDI_Timidity); } +#ifdef HAVE_FLUIDSYNTH + else if (snd_mididevice == -5 && device == MDEV_DEFAULT) + { + info = new MUSSong2(file, musiccache, len, MIDI_Fluid); + } +#endif if (info != NULL && !info->IsValid()) { delete info; diff --git a/src/sound/i_music.h b/src/sound/i_music.h index 4bde02df1..88e6f61c0 100644 --- a/src/sound/i_music.h +++ b/src/sound/i_music.h @@ -98,6 +98,9 @@ public: virtual FString GetStats(); virtual MusInfo *GetOPLDumper(const char *filename); virtual MusInfo *GetWaveDumper(const char *filename, int rate); + virtual void FluidSettingInt(const char *setting, int value); // FluidSynth settings + virtual void FluidSettingNum(const char *setting, double value); // " + virtual void FluidSettingStr(const char *setting, const char *value); // " enum EState { diff --git a/src/sound/i_musicinterns.h b/src/sound/i_musicinterns.h index e5c4771eb..4deec4f55 100644 --- a/src/sound/i_musicinterns.h +++ b/src/sound/i_musicinterns.h @@ -95,6 +95,9 @@ public: virtual bool NeedThreadedCallback() = 0; virtual void PrecacheInstruments(const WORD *instruments, int count); virtual void TimidityVolumeChanged(); + virtual void FluidSettingInt(const char *setting, int value); + virtual void FluidSettingNum(const char *setting, double value); + virtual void FluidSettingStr(const char *setting, const char *value); virtual FString GetStats(); }; @@ -255,6 +258,64 @@ protected: FILE *File; }; +// FluidSynth implementation of a MIDI device ------------------------------- + +#ifdef HAVE_FLUIDSYNTH +#include + +class FluidSynthMIDIDevice : public MIDIDevice +{ +public: + FluidSynthMIDIDevice(); + ~FluidSynthMIDIDevice(); + + int Open(void (*callback)(unsigned int, void *, DWORD, DWORD), void *userdata); + void Close(); + bool IsOpen() const; + int GetTechnology() const; + int SetTempo(int tempo); + int SetTimeDiv(int timediv); + int StreamOut(MIDIHDR *data); + int StreamOutSync(MIDIHDR *data); + int Resume(); + void Stop(); + int PrepareHeader(MIDIHDR *data); + int UnprepareHeader(MIDIHDR *data); + bool FakeVolume(); + bool Pause(bool paused); + bool NeedThreadedCallback(); + void PrecacheInstruments(const WORD *instruments, int count); + FString GetStats(); + void FluidSettingInt(const char *setting, int value); + void FluidSettingNum(const char *setting, double value); + void FluidSettingStr(const char *setting, const char *value); + +protected: + static bool FillStream(SoundStream *stream, void *buff, int len, void *userdata); + bool ServiceStream(void *buff, int numbytes); + void HandleEvent(int status, int parm1, int parm2); + int LoadPatchSets(const char *patches); + + void (*Callback)(unsigned int, void *, DWORD, DWORD); + void *CallbackData; + + void CalcTickRate(); + int PlayTick(); + + FCriticalSection CritSec; + SoundStream *Stream; + fluid_settings_t *FluidSettings; + fluid_synth_t *FluidSynth; + double Tempo; + double Division; + double SamplesPerTick; + double NextTickIn; + MIDIHDR *Events; + bool Started; + DWORD Position; +}; +#endif + // Base class for streaming MUS and MIDI files ------------------------------ // MIDI device selection. @@ -262,7 +323,8 @@ enum EMIDIDevice { MIDI_Win, MIDI_OPL, - MIDI_Timidity + MIDI_Timidity, + MIDI_Fluid }; class MIDIStreamer : public MusInfo @@ -282,6 +344,9 @@ public: bool IsValid() const; void Update(); FString GetStats(); + void FluidSettingInt(const char *setting, int value); + void FluidSettingNum(const char *setting, double value); + void FluidSettingStr(const char *setting, const char *value); protected: MIDIStreamer(const char *dumpname, EMIDIDevice type); diff --git a/src/sound/music_midi_base.cpp b/src/sound/music_midi_base.cpp index d7983a70f..0184c6a10 100644 --- a/src/sound/music_midi_base.cpp +++ b/src/sound/music_midi_base.cpp @@ -9,6 +9,9 @@ static DWORD nummididevices; static bool nummididevicesset; + +CVAR (String, snd_midipatchset, "", CVAR_ARCHIVE|CVAR_GLOBALCONFIG); + #ifdef _WIN32 UINT mididevice; @@ -19,7 +22,7 @@ CUSTOM_CVAR (Int, snd_mididevice, -1, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) if (!nummididevicesset) return; - if ((self >= (signed)nummididevices) || (self < -4)) + if ((self >= (signed)nummididevices) || (self < -5)) { Printf ("ID out of range. Using default device.\n"); self = 0; @@ -177,8 +180,8 @@ CCMD (snd_listmididevices) CUSTOM_CVAR(Int, snd_mididevice, -1, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) { - if (self < -3) - self = -3; + if (self < -5) + self = -5; else if (self > -1) self = -1; } diff --git a/src/sound/music_midistream.cpp b/src/sound/music_midistream.cpp index 5ed94c34c..d9449a657 100644 --- a/src/sound/music_midistream.cpp +++ b/src/sound/music_midistream.cpp @@ -215,6 +215,12 @@ void MIDIStreamer::Play(bool looping, int subsong) assert(0); // Intentional fall-through for non-Windows systems. +#ifdef HAVE_FLUIDSYNTH + case MIDI_Fluid: + MIDI = new FluidSynthMIDIDevice; + break; +#endif + case MIDI_Timidity: MIDI = new TimidityMIDIDevice; break; @@ -225,10 +231,10 @@ void MIDIStreamer::Play(bool looping, int subsong) } #ifndef _WIN32 - assert(MIDI->NeedThreadedCallback() == false); + assert(MIDI == NULL || MIDI->NeedThreadedCallback() == false); #endif - if (0 != MIDI->Open(Callback, this)) + if (MIDI == NULL || 0 != MIDI->Open(Callback, this)) { Printf(PRINT_BOLD, "Could not open MIDI out device\n"); return; @@ -435,6 +441,48 @@ void MIDIStreamer::TimidityVolumeChanged() } } +//========================================================================== +// +// MIDIStreamer :: FluidSettingInt +// +//========================================================================== + +void MIDIStreamer::FluidSettingInt(const char *setting, int value) +{ + if (MIDI != NULL) + { + MIDI->FluidSettingInt(setting, value); + } +} + +//========================================================================== +// +// MIDIStreamer :: FluidSettingNum +// +//========================================================================== + +void MIDIStreamer::FluidSettingNum(const char *setting, double value) +{ + if (MIDI != NULL) + { + MIDI->FluidSettingNum(setting, value); + } +} + +//========================================================================== +// +// MIDIDeviceStreamer :: FluidSettingStr +// +//========================================================================== + +void MIDIStreamer::FluidSettingStr(const char *setting, const char *value) +{ + if (MIDI != NULL) + { + MIDI->FluidSettingStr(setting, value); + } +} + //========================================================================== // @@ -840,6 +888,36 @@ void MIDIDevice::TimidityVolumeChanged() { } +//========================================================================== +// +// MIDIDevice :: FluidSettingInt +// +//========================================================================== + +void MIDIDevice::FluidSettingInt(const char *setting, int value) +{ +} + +//========================================================================== +// +// MIDIDevice :: FluidSettingNum +// +//========================================================================== + +void MIDIDevice::FluidSettingNum(const char *setting, double value) +{ +} + +//========================================================================== +// +// MIDIDevice :: FluidSettingStr +// +//========================================================================== + +void MIDIDevice::FluidSettingStr(const char *setting, const char *value) +{ +} + //========================================================================== // // MIDIDevice :: GetStats