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