diff --git a/CMakeLists.txt b/CMakeLists.txt index 0c3ec99d03..db58ccf2f0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -106,6 +106,9 @@ endif( ZD_CMAKE_COMPILER_IS_GNUCXX_COMPATIBLE ) set(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}") +option( NO_FMOD "Disable FMODEx sound support" OFF ) +option( NO_OPENAL "Disable OpenAL sound support" OFF ) + find_package( BZip2 ) find_package( JPEG ) find_package( ZLIB ) diff --git a/FindMPG123.cmake b/FindMPG123.cmake new file mode 100644 index 0000000000..a9b6dd8b22 --- /dev/null +++ b/FindMPG123.cmake @@ -0,0 +1,28 @@ +# - Find mpg123 +# Find the native mpg123 includes and library +# +# MPG123_INCLUDE_DIR - where to find mpg123.h +# MPG123_LIBRARIES - List of libraries when using mpg123. +# MPG123_FOUND - True if mpg123 found. + +IF(MPG123_INCLUDE_DIR AND MPG123_LIBRARIES) + # Already in cache, be silent + SET(MPG123_FIND_QUIETLY TRUE) +ENDIF(MPG123_INCLUDE_DIR AND MPG123_LIBRARIES) + +FIND_PATH(MPG123_INCLUDE_DIR mpg123.h + PATHS "${MPG123_DIR}" + PATH_SUFFIXES include + ) + +FIND_LIBRARY(MPG123_LIBRARIES NAMES mpg123 mpg123-0 + PATHS "${MPG123_DIR}" + PATH_SUFFIXES lib + ) + +# MARK_AS_ADVANCED(MPG123_LIBRARIES MPG123_INCLUDE_DIR) + +# handle the QUIETLY and REQUIRED arguments and set MPG123_FOUND to TRUE if +# all listed variables are TRUE +INCLUDE(FindPackageHandleStandardArgs) +FIND_PACKAGE_HANDLE_STANDARD_ARGS(MPG123 DEFAULT_MSG MPG123_LIBRARIES MPG123_INCLUDE_DIR) diff --git a/FindSndFile.cmake b/FindSndFile.cmake new file mode 100644 index 0000000000..ab66fc5c2d --- /dev/null +++ b/FindSndFile.cmake @@ -0,0 +1,29 @@ +# - Try to find SndFile +# Once done this will define +# +# SNDFILE_FOUND - system has SndFile +# SNDFILE_INCLUDE_DIRS - the SndFile include directory +# SNDFILE_LIBRARIES - Link these to use SndFile +# +# Copyright © 2006 Wengo +# Copyright © 2009 Guillaume Martres +# +# Redistribution and use is allowed according to the terms of the New +# BSD license. +# For details see the accompanying COPYING-CMAKE-SCRIPTS file. +# + +find_path(SNDFILE_INCLUDE_DIR NAMES sndfile.h) + +find_library(SNDFILE_LIBRARY NAMES sndfile sndfile-1) + +set(SNDFILE_INCLUDE_DIRS ${SNDFILE_INCLUDE_DIR}) +set(SNDFILE_LIBRARIES ${SNDFILE_LIBRARY}) + +INCLUDE(FindPackageHandleStandardArgs) +# handle the QUIETLY and REQUIRED arguments and set SNDFILE_FOUND to TRUE if +# all listed variables are TRUE +FIND_PACKAGE_HANDLE_STANDARD_ARGS(SndFile DEFAULT_MSG SNDFILE_LIBRARY SNDFILE_INCLUDE_DIR) + +# show the SNDFILE_INCLUDE_DIRS and SNDFILE_LIBRARIES variables only in the advanced view +mark_as_advanced(SNDFILE_INCLUDE_DIRS SNDFILE_LIBRARIES) diff --git a/output_sdl/CMakeLists.txt b/output_sdl/CMakeLists.txt index cc3614656c..a601fb9901 100644 --- a/output_sdl/CMakeLists.txt +++ b/output_sdl/CMakeLists.txt @@ -1,9 +1,11 @@ cmake_minimum_required( VERSION 2.4 ) -add_library( output_sdl MODULE output_sdl.c ) -include_directories( ${FMOD_INCLUDE_DIR} ${SDL2_INCLUDE_DIR} ) -target_link_libraries( output_sdl ${SDL2_LIBRARY} ) +if( NOT NO_FMOD AND FMOD_INCLUDE_DIR ) + add_library( output_sdl MODULE output_sdl.c ) + include_directories( ${FMOD_INCLUDE_DIR} ${SDL2_INCLUDE_DIR} ) + target_link_libraries( output_sdl ${SDL2_LIBRARY} ) -FILE( WRITE ${CMAKE_CURRENT_BINARY_DIR}/link-make "if [ ! -e ${ZDOOM_OUTPUT_DIR}/liboutput_sdl.so ]; then ln -sf output_sdl/liboutput_sdl.so ${ZDOOM_OUTPUT_DIR}/liboutput_sdl.so; fi" ) -add_custom_command( TARGET output_sdl POST_BUILD - COMMAND chmod +x ${CMAKE_CURRENT_BINARY_DIR}/link-make - COMMAND /bin/sh -c ${CMAKE_CURRENT_BINARY_DIR}/link-make ) + FILE( WRITE ${CMAKE_CURRENT_BINARY_DIR}/link-make "if [ ! -e ${ZDOOM_OUTPUT_DIR}/liboutput_sdl.so ]; then ln -sf output_sdl/liboutput_sdl.so ${ZDOOM_OUTPUT_DIR}/liboutput_sdl.so; fi" ) + add_custom_command( TARGET output_sdl POST_BUILD + COMMAND chmod +x ${CMAKE_CURRENT_BINARY_DIR}/link-make + COMMAND /bin/sh -c ${CMAKE_CURRENT_BINARY_DIR}/link-make ) +endif( NOT NO_FMOD AND FMOD_INCLUDE_DIR ) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index c363803211..5feab8402d 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -213,76 +213,88 @@ else( WIN32 ) endif( FPU_CONTROL_DIR ) endif( WIN32 ) -if( X64 ) - set( NO_ASM ON ) -endif( X64 ) -# Check if we have OpenGL +if( NOT NO_OPENAL ) + find_package( OpenAL ) + mark_as_advanced(CLEAR OPENAL_LIBRARY OPENAL_INCLUDE_DIR) + if( OPENAL_FOUND ) + include_directories( ${OPENAL_INCLUDE_DIR} ) + set( ZDOOM_LIBS ${OPENAL_LIBRARY} ${ZDOOM_LIBS} ) + else( OPENAL_FOUND ) + set( NO_OPENAL ON ) + endif( OPENAL_FOUND ) +endif( NOT NO_OPENAL ) -if( NOT OPENGL_FOUND ) - message( FATAL_ERROR "OpenGL is required for building." ) -endif( NOT OPENGL_FOUND ) -if( NOT OPENGL_GLU_FOUND ) - message( FATAL_ERROR "OpenGL GLU is required for building." ) -endif( NOT OPENGL_GLU_FOUND ) +if( NOT NO_FMOD ) + # Search for FMOD include files + if( NOT WIN32 ) + find_path( FMOD_INCLUDE_DIR fmod.hpp + PATHS ${FMOD_LOCAL_INC_DIRS} ) + endif( NOT WIN32 ) -set( ZDOOM_LIBS ${ZDOOM_LIBS} ${OPENGL_LIBRARIES} ) -include_directories( ${OPENGL_INCLUDE_DIR} ) + if( NOT FMOD_INCLUDE_DIR ) + find_path( FMOD_INCLUDE_DIR fmod.hpp + PATHS ${FMOD_SEARCH_PATHS} + ${FMOD_INC_PATH_SUFFIXES} ) + endif( NOT FMOD_INCLUDE_DIR ) + if( FMOD_INCLUDE_DIR ) + message( STATUS "FMOD include files found at ${FMOD_INCLUDE_DIR}" ) + include_directories( "${FMOD_INCLUDE_DIR}" ) + else( FMOD_INCLUDE_DIR ) + message( STATUS "Could not find FMOD include files" ) + set( NO_FMOD ON ) + endif( FMOD_INCLUDE_DIR ) +endif( NOT NO_FMOD ) -# Decide on the name of the FMOD library we want to use. +if( NOT NO_FMOD ) + # Decide on the name of the FMOD library we want to use. + if( NOT FMOD_LIB_NAME AND MSVC ) + set( FMOD_LIB_NAME fmodex${X64}_vc ) + endif( NOT FMOD_LIB_NAME AND MSVC ) -if( NOT FMOD_LIB_NAME AND MSVC ) - set( FMOD_LIB_NAME fmodex${X64}_vc ) -endif( NOT FMOD_LIB_NAME AND MSVC ) + if( NOT FMOD_LIB_NAME AND BORLAND ) + set( FMOD_LIB_NAME fmodex${X64}_bc ) + endif( NOT FMOD_LIB_NAME AND BORLAND ) -if( NOT FMOD_LIB_NAME AND BORLAND ) - set( FMOD_LIB_NAME fmodex${X64}_bc ) -endif( NOT FMOD_LIB_NAME AND BORLAND ) + if( NOT FMOD_LIB_NAME ) + set( FMOD_LIB_NAME fmodex${X64} ) + endif( NOT FMOD_LIB_NAME ) -if( NOT FMOD_LIB_NAME ) - set( FMOD_LIB_NAME fmodex${X64} ) -endif( NOT FMOD_LIB_NAME ) + # Search for FMOD library + if( WIN32 OR APPLE ) + find_library( FMOD_LIBRARY ${FMOD_LIB_NAME} + PATHS ${FMOD_SEARCH_PATHS} + ${FMOD_LIB_PATH_SUFFIXES} ) + else( WIN32 OR APPLE ) + find_library( FMOD_LIBRARY + NAMES ${FMOD_VERSIONS} + PATHS ${FMOD_LOCAL_LIB_DIRS} ) + endif( WIN32 OR APPLE ) + if( FMOD_LIBRARY ) + message( STATUS "FMOD library found at ${FMOD_LIBRARY}" ) + set( ZDOOM_LIBS ${ZDOOM_LIBS} "${FMOD_LIBRARY}" ) + else( FMOD_LIBRARY ) + message( STATUS "Could not find FMOD library" ) + set( NO_FMOD ON ) + endif( FMOD_LIBRARY ) +endif( NOT NO_FMOD ) -# Search for FMOD include files +if( NO_FMOD ) + add_definitions( -DNO_FMOD=1 ) +endif( NO_FMOD ) +if( NO_OPENAL ) + add_definitions( -DNO_OPENAL=1 ) +endif( NO_OPENAL ) -if( NOT WIN32 ) - find_path( FMOD_INCLUDE_DIR fmod.hpp - PATHS ${FMOD_LOCAL_INC_DIRS} ) -endif( NOT WIN32 ) +# Search for libSndFile -if( NOT FMOD_INCLUDE_DIR ) - find_path( FMOD_INCLUDE_DIR fmod.hpp - PATHS ${FMOD_SEARCH_PATHS} - ${FMOD_INC_PATH_SUFFIXES} ) -endif( NOT FMOD_INCLUDE_DIR ) +find_package( SndFile ) -if( FMOD_INCLUDE_DIR ) - message( STATUS "FMOD include files found at ${FMOD_INCLUDE_DIR}" ) -else( FMOD_INCLUDE_DIR ) - message( SEND_ERROR "Could not find FMOD include files" ) -endif( FMOD_INCLUDE_DIR ) - - -# Search for FMOD library - -if( WIN32 OR APPLE ) - find_library( FMOD_LIBRARY ${FMOD_LIB_NAME} - PATHS ${FMOD_SEARCH_PATHS} - ${FMOD_LIB_PATH_SUFFIXES} ) -else( WIN32 OR APPLE ) - find_library( FMOD_LIBRARY - NAMES ${FMOD_VERSIONS} - PATHS ${FMOD_LOCAL_LIB_DIRS} ) -endif( WIN32 OR APPLE ) - -if( FMOD_LIBRARY ) - message( STATUS "FMOD library found at ${FMOD_LIBRARY}" ) -else( FMOD_LIBRARY ) - message( SEND_ERROR "Could not find FMOD library" ) -endif( FMOD_LIBRARY ) +# Search for libmpg123 +find_package( MPG123 ) # Search for FluidSynth @@ -530,9 +542,17 @@ add_custom_target( revision_check ALL # Libraries ZDoom needs message( STATUS "Fluid synth libs: ${FLUIDSYNTH_LIBRARIES}" ) -set( ZDOOM_LIBS ${ZDOOM_LIBS} "${ZLIB_LIBRARIES}" "${JPEG_LIBRARIES}" "${BZIP2_LIBRARIES}" "${GME_LIBRARIES}" "${FMOD_LIBRARY}" ) -include_directories( "${ZLIB_INCLUDE_DIR}" "${FMOD_INCLUDE_DIR}" "${BZIP2_INCLUDE_DIR}" "${LZMA_INCLUDE_DIR}" "${JPEG_INCLUDE_DIR}" "${GME_INCLUDE_DIR}" ) +set( ZDOOM_LIBS ${ZDOOM_LIBS} "${ZLIB_LIBRARIES}" "${JPEG_LIBRARIES}" "${BZIP2_LIBRARIES}" "${GME_LIBRARIES}" ) +include_directories( "${ZLIB_INCLUDE_DIR}" "${BZIP2_INCLUDE_DIR}" "${LZMA_INCLUDE_DIR}" "${JPEG_INCLUDE_DIR}" "${GME_INCLUDE_DIR}" ) +if( SNDFILE_FOUND ) + set( ZDOOM_LIBS ${ZDOOM_LIBS} "${SNDFILE_LIBRARIES}" ) + include_directories( "${SNDFILE_INCLUDE_DIRS}" ) +endif( SNDFILE_FOUND ) +if( MPG123_FOUND ) + set( ZDOOM_LIBS ${ZDOOM_LIBS} "${MPG123_LIBRARIES}" ) + include_directories( "${MPG123_INCLUDE_DIR}" ) +endif( MPG123_FOUND ) if( NOT DYN_FLUIDSYNTH) if( FLUIDSYNTH_FOUND ) set( ZDOOM_LIBS ${ZDOOM_LIBS} "${FLUIDSYNTH_LIBRARIES}" ) @@ -673,6 +693,12 @@ else( SSE_MATTERS ) set( X86_SOURCES ) endif( SSE_MATTERS ) +if( SNDFILE_FOUND ) + add_definitions( -DHAVE_SNDFILE ) +endif( SNDFILE_FOUND ) +if( MPG123_FOUND ) + add_definitions( -DHAVE_MPG123 ) +endif( MPG123_FOUND ) if( DYN_FLUIDSYNTH ) add_definitions( -DHAVE_FLUIDSYNTH -DDYN_FLUIDSYNTH ) elseif( FLUIDSYNTH_FOUND ) @@ -1120,6 +1146,7 @@ add_executable( zdoom WIN32 MACOSX_BUNDLE sound/fmodsound.cpp sound/i_music.cpp sound/i_sound.cpp + sound/mpg123_decoder.cpp sound/music_cd.cpp sound/music_dumb.cpp sound/music_gme.cpp @@ -1136,6 +1163,8 @@ add_executable( zdoom WIN32 MACOSX_BUNDLE sound/music_softsynth_mididevice.cpp sound/music_timidity_mididevice.cpp sound/music_win_mididevice.cpp + sound/oalsound.cpp + sound/sndfile_decoder.cpp sound/music_pseudo_mididevice.cpp textures/animations.cpp textures/anim_switches.cpp @@ -1248,9 +1277,9 @@ endif( NOT ZDOOM_OUTPUT_OLDSTYLE OR NO_GENERATOR_EXPRESSIONS ) if( MSVC ) option( ZDOOM_GENERATE_MAPFILE "Generate .map file for debugging." OFF ) if( ZDOOM_GENERATE_MAPFILE ) - set_target_properties(zdoom PROPERTIES LINK_FLAGS "/MANIFEST:NO /DELAYLOAD:\"fmodex${X64}.dll\" /MAP") + set_target_properties(zdoom PROPERTIES LINK_FLAGS "/MANIFEST:NO /DELAYLOAD:\"fmodex${X64}.dll\" /DELAYLOAD:\"openal32.dll\" /DELAYLOAD:\"libmpg123-0.dll\" /DELAYLOAD:\"libsndfile-1.dll\" /MAP") else( ZDOOM_GENERATE_MAPFILE ) - set_target_properties(zdoom PROPERTIES LINK_FLAGS "/MANIFEST:NO /DELAYLOAD:\"fmodex${X64}.dll\"") + set_target_properties(zdoom PROPERTIES LINK_FLAGS "/MANIFEST:NO /DELAYLOAD:\"fmodex${X64}.dll\" /DELAYLOAD:\"openal32.dll\" /DELAYLOAD:\"libmpg123-0.dll\" /DELAYLOAD:\"libsndfile-1.dll\"") endif( ZDOOM_GENERATE_MAPFILE ) add_custom_command(TARGET zdoom POST_BUILD diff --git a/src/d_main.cpp b/src/d_main.cpp index 9b46d42c23..9ddc17cd58 100644 --- a/src/d_main.cpp +++ b/src/d_main.cpp @@ -905,6 +905,7 @@ void D_Display () } while (diff < 1); wipestart = nowtime; done = screen->WipeDo (1); + S_UpdateMusic(); // OpenAL needs this to keep the music running, thanks to a complete lack of a sane streaming implementation using callbacks. :( C_DrawConsole (hw2d); // console and M_Drawer (); // menu are drawn even on top of wipes screen->Update (); // page flip or blit buffer @@ -1003,6 +1004,7 @@ void D_DoomLoop () // Update display, next frame, with current state. I_StartTic (); D_Display (); + S_UpdateMusic(); // OpenAL needs this to keep the music running, thanks to a complete lack of a sane streaming implementation using callbacks. :( } catch (CRecoverableError &error) { diff --git a/src/files.cpp b/src/files.cpp index 52aa891c94..82ea04c729 100644 --- a/src/files.cpp +++ b/src/files.cpp @@ -544,3 +544,59 @@ char *MemoryReader::Gets(char *strbuf, int len) { return GetsFromBuffer(bufptr, strbuf, len); } + +//========================================================================== +// +// MemoryArrayReader +// +// reads data from an array of memory +// +//========================================================================== + +MemoryArrayReader::MemoryArrayReader (const char *buffer, long length) +{ + buf.Resize(length); + memcpy(&buf[0], buffer, length); + Length=length; + FilePos=0; +} + +MemoryArrayReader::~MemoryArrayReader () +{ +} + +long MemoryArrayReader::Tell () const +{ + return FilePos; +} + +long MemoryArrayReader::Seek (long offset, int origin) +{ + switch (origin) + { + case SEEK_CUR: + offset+=FilePos; + break; + + case SEEK_END: + offset+=Length; + break; + + } + FilePos=clamp(offset,0,Length-1); + return 0; +} + +long MemoryArrayReader::Read (void *buffer, long len) +{ + if (len>Length-FilePos) len=Length-FilePos; + if (len<0) len=0; + memcpy(buffer,&buf[FilePos],len); + FilePos+=len; + return len; +} + +char *MemoryArrayReader::Gets(char *strbuf, int len) +{ + return GetsFromBuffer((char*)&buf[0], strbuf, len); +} diff --git a/src/files.h b/src/files.h index f7d061c8e5..a38722738f 100644 --- a/src/files.h +++ b/src/files.h @@ -336,6 +336,24 @@ protected: const char * bufptr; }; +class MemoryArrayReader : public FileReader +{ +public: + MemoryArrayReader (const char *buffer, long length); + ~MemoryArrayReader (); + + virtual long Tell () const; + virtual long Seek (long offset, int origin); + virtual long Read (void *buffer, long len); + virtual char *Gets(char *strbuf, int len); + virtual const char *GetBuffer() const { return (char*)&buf[0]; } + TArray &GetArray() { return buf; } + + void UpdateLength() { Length = buf.Size(); } + +protected: + TArray buf; +}; #endif diff --git a/src/menu/menudef.cpp b/src/menu/menudef.cpp index 616bdf2ca3..819ba96ba7 100644 --- a/src/menu/menudef.cpp +++ b/src/menu/menudef.cpp @@ -49,6 +49,7 @@ #include "i_music.h" #include "m_joy.h" #include "gi.h" +#include "i_sound.h" #include "optionmenuitems.h" @@ -60,6 +61,8 @@ static FOptionMenuDescriptor DefaultOptionMenuSettings; // contains common setti FOptionMenuSettings OptionSettings; FOptionMap OptionValues; +void I_BuildALDeviceList(FOptionValues *opt); + static void DeinitMenus() { { @@ -168,6 +171,14 @@ static bool CheckSkipOptionBlock(FScanner &sc) filter = true; #endif } + else if (sc.Compare("OpenAL")) + { + filter |= IsOpenALPresent(); + } + else if (sc.Compare("FModEx")) + { + filter |= IsFModExPresent(); + } } while (sc.CheckString(",")); sc.MustGetStringName(")"); @@ -589,7 +600,11 @@ static void ParseOptionSettings(FScanner &sc) while (!sc.CheckString("}")) { sc.MustGetString(); - if (sc.Compare("ifgame")) + if (sc.Compare("else")) + { + SkipSubBlock(sc); + } + else if (sc.Compare("ifgame")) { if (!CheckSkipGameBlock(sc)) { @@ -626,7 +641,11 @@ static void ParseOptionMenuBody(FScanner &sc, FOptionMenuDescriptor *desc) while (!sc.CheckString("}")) { sc.MustGetString(); - if (sc.Compare("ifgame")) + if (sc.Compare("else")) + { + SkipSubBlock(sc); + } + else if (sc.Compare("ifgame")) { if (!CheckSkipGameBlock(sc)) { @@ -1262,6 +1281,11 @@ void M_CreateMenus() { I_BuildMIDIMenuList(*opt); } + opt = OptionValues.CheckKey(NAME_Aldevices); + if (opt != NULL) + { + I_BuildALDeviceList(*opt); + } } //============================================================================= diff --git a/src/namedef.h b/src/namedef.h index 78551badf1..0d49bba767 100644 --- a/src/namedef.h +++ b/src/namedef.h @@ -555,6 +555,7 @@ xx(Controlmessage) xx(Crosshairs) xx(Colorpickermenu) xx(Mididevices) +xx(Aldevices) xx(CustomizeControls) xx(MessageOptions) xx(AutomapOptions) diff --git a/src/oplsynth/opl_mus_player.cpp b/src/oplsynth/opl_mus_player.cpp index a528c10e2b..f9b46a08de 100644 --- a/src/oplsynth/opl_mus_player.cpp +++ b/src/oplsynth/opl_mus_player.cpp @@ -52,29 +52,22 @@ void OPLmusicBlock::Restart() LastOffset = 0; } -OPLmusicFile::OPLmusicFile (FILE *file, BYTE *musiccache, int len) - : ScoreLen (len) +OPLmusicFile::OPLmusicFile (FileReader *reader) + : ScoreLen (reader->GetLength()) { if (io == NULL) { return; } - scoredata = new BYTE[len]; + scoredata = new BYTE[ScoreLen]; - if (file) - { - if (fread (scoredata, 1, len, file) != (size_t)len) - { -fail: delete[] scoredata; - scoredata = NULL; - return; - } - } - else - { - memcpy(scoredata, &musiccache[0], len); - } + if (reader->Read(scoredata, ScoreLen) != ScoreLen) + { +fail: delete[] scoredata; + scoredata = NULL; + return; + } if (0 == (NumChips = io->OPLinit(NumChips))) { @@ -100,7 +93,7 @@ fail: delete[] scoredata; { RawPlayer = DosBox1; SamplesPerTick = OPL_SAMPLE_RATE / 1000; - ScoreLen = MIN(len - 24, LittleLong(((DWORD *)scoredata)[4])) + 24; + ScoreLen = MIN(ScoreLen - 24, LittleLong(((DWORD *)scoredata)[4])) + 24; } else if (((DWORD *)scoredata)[2] == MAKE_ID(2,0,0,0)) { @@ -120,7 +113,7 @@ fail: delete[] scoredata; RawPlayer = DosBox2; SamplesPerTick = OPL_SAMPLE_RATE / 1000; int headersize = 0x1A + scoredata[0x19]; - ScoreLen = MIN(len - headersize, LittleLong(((DWORD *)scoredata)[3]) * 2) + headersize; + ScoreLen = MIN(ScoreLen - headersize, LittleLong(((DWORD *)scoredata)[3]) * 2) + headersize; } else { diff --git a/src/oplsynth/opl_mus_player.h b/src/oplsynth/opl_mus_player.h index b0eb4b6c8a..8e675c5d87 100644 --- a/src/oplsynth/opl_mus_player.h +++ b/src/oplsynth/opl_mus_player.h @@ -29,7 +29,7 @@ protected: class OPLmusicFile : public OPLmusicBlock { public: - OPLmusicFile(FILE *file, BYTE *musiccache, int len); + OPLmusicFile(FileReader *reader); OPLmusicFile(const OPLmusicFile *source, const char *filename); virtual ~OPLmusicFile(); diff --git a/src/p_mobj.cpp b/src/p_mobj.cpp index 5921f97e70..e2aa677649 100644 --- a/src/p_mobj.cpp +++ b/src/p_mobj.cpp @@ -4749,7 +4749,7 @@ AActor *P_SpawnMapThing (FMapThing *mthing, int position) // [RH] sound sequence overriders if (mentry->Type == NULL && mentry->Special == SMT_SSeqOverride) { - int type = mentry->Args[0]; + int type = mthing->args[0]; if (type == 255) type = -1; if (type > 63) { diff --git a/src/s_advsound.cpp b/src/s_advsound.cpp index 60a6730f64..c72005813e 100644 --- a/src/s_advsound.cpp +++ b/src/s_advsound.cpp @@ -1366,7 +1366,7 @@ static void S_AddSNDINFO (int lump) FName nm = sc.String; sc.MustGetString(); if (sc.Compare("timidity")) MidiDevices[nm] = MDEV_TIMIDITY; - else if (sc.Compare("fmod")) MidiDevices[nm] = MDEV_FMOD; + else if (sc.Compare("fmod") || sc.Compare("sndsys")) MidiDevices[nm] = MDEV_SNDSYS; else if (sc.Compare("standard")) MidiDevices[nm] = MDEV_MMAPI; else if (sc.Compare("opl")) MidiDevices[nm] = MDEV_OPL; else if (sc.Compare("default")) MidiDevices[nm] = MDEV_DEFAULT; diff --git a/src/s_sound.cpp b/src/s_sound.cpp index 7b543c4e4a..bacd5aa946 100644 --- a/src/s_sound.cpp +++ b/src/s_sound.cpp @@ -1316,52 +1316,35 @@ sfxinfo_t *S_LoadSound(sfxinfo_t *sfx) int size = Wads.LumpLength(sfx->lumpnum); if (size > 0) { - BYTE *sfxdata; - BYTE *sfxstart; FWadLump wlump = Wads.OpenLumpNum(sfx->lumpnum); - sfxstart = sfxdata = new BYTE[size]; + BYTE *sfxdata = new BYTE[size]; wlump.Read(sfxdata, size); - SDWORD len = LittleLong(((SDWORD *)sfxdata)[1]); + SDWORD dmxlen = LittleLong(((SDWORD *)sfxdata)[1]); // If the sound is voc, use the custom loader. - if (strncmp ((const char *)sfxstart, "Creative Voice File", 19) == 0) + if (strncmp ((const char *)sfxdata, "Creative Voice File", 19) == 0) { - sfx->data = GSnd->LoadSoundVoc(sfxstart, size); + sfx->data = GSnd->LoadSoundVoc(sfxdata, size); } // If the sound is raw, just load it as such. - // Otherwise, try the sound as DMX format. - // If that fails, let FMOD try and figure it out. - else if (sfx->bLoadRAW || - (((BYTE *)sfxdata)[0] == 3 && ((BYTE *)sfxdata)[1] == 0 && len <= size - 8)) + else if (sfx->bLoadRAW) { - int frequency; - - if (sfx->bLoadRAW) - { - len = Wads.LumpLength (sfx->lumpnum); - frequency = sfx->RawRate; - } - else - { - frequency = LittleShort(((WORD *)sfxdata)[1]); - if (frequency == 0) - { - frequency = 11025; - } - sfxstart = sfxdata + 8; - } - sfx->data = GSnd->LoadSoundRaw(sfxstart, len, frequency, 1, 8, sfx->LoopStart); + sfx->data = GSnd->LoadSoundRaw(sfxdata, size, sfx->RawRate, 1, 8, sfx->LoopStart); } + // Otherwise, try the sound as DMX format. + else if (((BYTE *)sfxdata)[0] == 3 && ((BYTE *)sfxdata)[1] == 0 && dmxlen <= size - 8) + { + int frequency = LittleShort(((WORD *)sfxdata)[1]); + if (frequency == 0) frequency = 11025; + sfx->data = GSnd->LoadSoundRaw(sfxdata+8, dmxlen, frequency, 1, 8, sfx->LoopStart); + } + // If that fails, let the sound system try and figure it out. else { - len = Wads.LumpLength (sfx->lumpnum); - sfx->data = GSnd->LoadSound(sfxstart, len); - } - - if (sfxdata != NULL) - { - delete[] sfxdata; + sfx->data = GSnd->LoadSound(sfxdata, size); } + + delete[] sfxdata; } if (!sfx->data.isValid()) @@ -1930,29 +1913,32 @@ void S_UpdateSounds (AActor *listenactor) S_ActivatePlayList(false); } - // should never happen - S_SetListener(listener, listenactor); - - for (FSoundChan *chan = Channels; chan != NULL; chan = chan->NextChan) + if (listenactor != NULL) { - if ((chan->ChanFlags & (CHAN_EVICTED | CHAN_IS3D)) == CHAN_IS3D) + // should never happen + S_SetListener(listener, listenactor); + + for (FSoundChan *chan = Channels; chan != NULL; chan = chan->NextChan) { - CalcPosVel(chan, &pos, &vel); - GSnd->UpdateSoundParams3D(&listener, chan, !!(chan->ChanFlags & CHAN_AREA), pos, vel); + if ((chan->ChanFlags & (CHAN_EVICTED | CHAN_IS3D)) == CHAN_IS3D) + { + CalcPosVel(chan, &pos, &vel); + GSnd->UpdateSoundParams3D(&listener, chan, !!(chan->ChanFlags & CHAN_AREA), pos, vel); + } + chan->ChanFlags &= ~CHAN_JUSTSTARTED; } - chan->ChanFlags &= ~CHAN_JUSTSTARTED; - } - SN_UpdateActiveSequences(); + SN_UpdateActiveSequences(); - GSnd->UpdateListener(&listener); - GSnd->UpdateSounds(); + GSnd->UpdateListener(&listener); + GSnd->UpdateSounds(); - if (level.time >= RestartEvictionsAt) - { - RestartEvictionsAt = 0; - S_RestoreEvictedChannels(); + if (level.time >= RestartEvictionsAt) + { + RestartEvictionsAt = 0; + S_RestoreEvictedChannels(); + } } } @@ -2345,8 +2331,6 @@ bool S_StartMusic (const char *m_id) // specified, it will only be played if the specified CD is in a drive. //========================================================================== -TArray musiccache; - bool S_ChangeMusic (const char *musicname, int order, bool looping, bool force) { if (!force && PlayList) @@ -2426,7 +2410,7 @@ bool S_ChangeMusic (const char *musicname, int order, bool looping, bool force) else { int lumpnum = -1; - int offset = 0, length = 0; + int length = 0; int device = MDEV_DEFAULT; MusInfo *handle = NULL; FName musicasname = musicname; @@ -2447,6 +2431,7 @@ bool S_ChangeMusic (const char *musicname, int order, bool looping, bool force) musicname += 7; } + FileReader *reader = NULL; if (!FileExists (musicname)) { if ((lumpnum = Wads.CheckNumForFullName (musicname, true, ns_music)) == -1) @@ -2469,34 +2454,22 @@ bool S_ChangeMusic (const char *musicname, int order, bool looping, bool force) } if (handle == NULL) { - if (!Wads.IsUncompressedFile(lumpnum)) + if (Wads.LumpLength (lumpnum) == 0) { - // We must cache the music data and use it from memory. - - // shut down old music before reallocating and overwriting the cache! - S_StopMusic (true); - - offset = -1; // this tells the low level code that the music - // is being used from memory - length = Wads.LumpLength (lumpnum); - if (length == 0) - { - return false; - } - musiccache.Resize(length); - Wads.ReadLump(lumpnum, &musiccache[0]); + return false; } - else + reader = Wads.ReopenLumpNumNewFile(lumpnum); + if (reader == NULL) { - offset = Wads.GetLumpOffset (lumpnum); - length = Wads.LumpLength (lumpnum); - if (length == 0) - { - return false; - } + return false; } } } + else + { + // Load an external file. + reader = new FileReader(musicname); + } // shutdown old music S_StopMusic (true); @@ -2508,6 +2481,7 @@ bool S_ChangeMusic (const char *musicname, int order, bool looping, bool force) mus_playing.name = musicname; mus_playing.baseorder = order; LastSong = musicname; + delete reader; return true; } @@ -2515,16 +2489,11 @@ bool S_ChangeMusic (const char *musicname, int order, bool looping, bool force) if (handle != NULL) { mus_playing.handle = handle; - } - else if (offset != -1) - { - mus_playing.handle = I_RegisterSong (lumpnum != -1 ? - Wads.GetWadFullName (Wads.GetLumpFile (lumpnum)) : - musicname, NULL, offset, length, device); + delete reader; } else { - mus_playing.handle = I_RegisterSong (NULL, &musiccache[0], -1, length, device); + mus_playing.handle = I_RegisterSong (reader, device); } } @@ -2622,6 +2591,17 @@ void S_StopMusic (bool force) } } +//========================================================================== +// +// +// +//========================================================================== + +void S_UpdateMusic() +{ + GSnd->UpdateMusic(); +} + //========================================================================== // // CCMD playsound diff --git a/src/s_sound.h b/src/s_sound.h index da8bf3cbcf..49fb81fb35 100644 --- a/src/s_sound.h +++ b/src/s_sound.h @@ -329,6 +329,7 @@ int S_GetMusic (char **name); // Stops the music for sure. void S_StopMusic (bool force); +void S_UpdateMusic(); // Stop and resume music, during game PAUSE. void S_PauseSound (bool notmusic, bool notsfx); @@ -389,7 +390,7 @@ enum EMidiDevice MDEV_DEFAULT = -1, MDEV_MMAPI = 0, MDEV_OPL = 1, - MDEV_FMOD = 2, + MDEV_SNDSYS = 2, MDEV_TIMIDITY = 3, MDEV_FLUIDSYNTH = 4, MDEV_GUS = 5, diff --git a/src/sound/efx.h b/src/sound/efx.h new file mode 100644 index 0000000000..1f89ae9b9d --- /dev/null +++ b/src/sound/efx.h @@ -0,0 +1,758 @@ +#ifndef AL_EFX_H +#define AL_EFX_H + + +#ifdef __cplusplus +extern "C" { +#endif + +#define ALC_EXT_EFX_NAME "ALC_EXT_EFX" + +#define ALC_EFX_MAJOR_VERSION 0x20001 +#define ALC_EFX_MINOR_VERSION 0x20002 +#define ALC_MAX_AUXILIARY_SENDS 0x20003 + + +/* Listener properties. */ +#define AL_METERS_PER_UNIT 0x20004 + +/* Source properties. */ +#define AL_DIRECT_FILTER 0x20005 +#define AL_AUXILIARY_SEND_FILTER 0x20006 +#define AL_AIR_ABSORPTION_FACTOR 0x20007 +#define AL_ROOM_ROLLOFF_FACTOR 0x20008 +#define AL_CONE_OUTER_GAINHF 0x20009 +#define AL_DIRECT_FILTER_GAINHF_AUTO 0x2000A +#define AL_AUXILIARY_SEND_FILTER_GAIN_AUTO 0x2000B +#define AL_AUXILIARY_SEND_FILTER_GAINHF_AUTO 0x2000C + + +/* Effect properties. */ + +/* Reverb effect parameters */ +#define AL_REVERB_DENSITY 0x0001 +#define AL_REVERB_DIFFUSION 0x0002 +#define AL_REVERB_GAIN 0x0003 +#define AL_REVERB_GAINHF 0x0004 +#define AL_REVERB_DECAY_TIME 0x0005 +#define AL_REVERB_DECAY_HFRATIO 0x0006 +#define AL_REVERB_REFLECTIONS_GAIN 0x0007 +#define AL_REVERB_REFLECTIONS_DELAY 0x0008 +#define AL_REVERB_LATE_REVERB_GAIN 0x0009 +#define AL_REVERB_LATE_REVERB_DELAY 0x000A +#define AL_REVERB_AIR_ABSORPTION_GAINHF 0x000B +#define AL_REVERB_ROOM_ROLLOFF_FACTOR 0x000C +#define AL_REVERB_DECAY_HFLIMIT 0x000D + +/* EAX Reverb effect parameters */ +#define AL_EAXREVERB_DENSITY 0x0001 +#define AL_EAXREVERB_DIFFUSION 0x0002 +#define AL_EAXREVERB_GAIN 0x0003 +#define AL_EAXREVERB_GAINHF 0x0004 +#define AL_EAXREVERB_GAINLF 0x0005 +#define AL_EAXREVERB_DECAY_TIME 0x0006 +#define AL_EAXREVERB_DECAY_HFRATIO 0x0007 +#define AL_EAXREVERB_DECAY_LFRATIO 0x0008 +#define AL_EAXREVERB_REFLECTIONS_GAIN 0x0009 +#define AL_EAXREVERB_REFLECTIONS_DELAY 0x000A +#define AL_EAXREVERB_REFLECTIONS_PAN 0x000B +#define AL_EAXREVERB_LATE_REVERB_GAIN 0x000C +#define AL_EAXREVERB_LATE_REVERB_DELAY 0x000D +#define AL_EAXREVERB_LATE_REVERB_PAN 0x000E +#define AL_EAXREVERB_ECHO_TIME 0x000F +#define AL_EAXREVERB_ECHO_DEPTH 0x0010 +#define AL_EAXREVERB_MODULATION_TIME 0x0011 +#define AL_EAXREVERB_MODULATION_DEPTH 0x0012 +#define AL_EAXREVERB_AIR_ABSORPTION_GAINHF 0x0013 +#define AL_EAXREVERB_HFREFERENCE 0x0014 +#define AL_EAXREVERB_LFREFERENCE 0x0015 +#define AL_EAXREVERB_ROOM_ROLLOFF_FACTOR 0x0016 +#define AL_EAXREVERB_DECAY_HFLIMIT 0x0017 + +/* Chorus effect parameters */ +#define AL_CHORUS_WAVEFORM 0x0001 +#define AL_CHORUS_PHASE 0x0002 +#define AL_CHORUS_RATE 0x0003 +#define AL_CHORUS_DEPTH 0x0004 +#define AL_CHORUS_FEEDBACK 0x0005 +#define AL_CHORUS_DELAY 0x0006 + +/* Distortion effect parameters */ +#define AL_DISTORTION_EDGE 0x0001 +#define AL_DISTORTION_GAIN 0x0002 +#define AL_DISTORTION_LOWPASS_CUTOFF 0x0003 +#define AL_DISTORTION_EQCENTER 0x0004 +#define AL_DISTORTION_EQBANDWIDTH 0x0005 + +/* Echo effect parameters */ +#define AL_ECHO_DELAY 0x0001 +#define AL_ECHO_LRDELAY 0x0002 +#define AL_ECHO_DAMPING 0x0003 +#define AL_ECHO_FEEDBACK 0x0004 +#define AL_ECHO_SPREAD 0x0005 + +/* Flanger effect parameters */ +#define AL_FLANGER_WAVEFORM 0x0001 +#define AL_FLANGER_PHASE 0x0002 +#define AL_FLANGER_RATE 0x0003 +#define AL_FLANGER_DEPTH 0x0004 +#define AL_FLANGER_FEEDBACK 0x0005 +#define AL_FLANGER_DELAY 0x0006 + +/* Frequency shifter effect parameters */ +#define AL_FREQUENCY_SHIFTER_FREQUENCY 0x0001 +#define AL_FREQUENCY_SHIFTER_LEFT_DIRECTION 0x0002 +#define AL_FREQUENCY_SHIFTER_RIGHT_DIRECTION 0x0003 + +/* Vocal morpher effect parameters */ +#define AL_VOCAL_MORPHER_PHONEMEA 0x0001 +#define AL_VOCAL_MORPHER_PHONEMEA_COARSE_TUNING 0x0002 +#define AL_VOCAL_MORPHER_PHONEMEB 0x0003 +#define AL_VOCAL_MORPHER_PHONEMEB_COARSE_TUNING 0x0004 +#define AL_VOCAL_MORPHER_WAVEFORM 0x0005 +#define AL_VOCAL_MORPHER_RATE 0x0006 + +/* Pitchshifter effect parameters */ +#define AL_PITCH_SHIFTER_COARSE_TUNE 0x0001 +#define AL_PITCH_SHIFTER_FINE_TUNE 0x0002 + +/* Ringmodulator effect parameters */ +#define AL_RING_MODULATOR_FREQUENCY 0x0001 +#define AL_RING_MODULATOR_HIGHPASS_CUTOFF 0x0002 +#define AL_RING_MODULATOR_WAVEFORM 0x0003 + +/* Autowah effect parameters */ +#define AL_AUTOWAH_ATTACK_TIME 0x0001 +#define AL_AUTOWAH_RELEASE_TIME 0x0002 +#define AL_AUTOWAH_RESONANCE 0x0003 +#define AL_AUTOWAH_PEAK_GAIN 0x0004 + +/* Compressor effect parameters */ +#define AL_COMPRESSOR_ONOFF 0x0001 + +/* Equalizer effect parameters */ +#define AL_EQUALIZER_LOW_GAIN 0x0001 +#define AL_EQUALIZER_LOW_CUTOFF 0x0002 +#define AL_EQUALIZER_MID1_GAIN 0x0003 +#define AL_EQUALIZER_MID1_CENTER 0x0004 +#define AL_EQUALIZER_MID1_WIDTH 0x0005 +#define AL_EQUALIZER_MID2_GAIN 0x0006 +#define AL_EQUALIZER_MID2_CENTER 0x0007 +#define AL_EQUALIZER_MID2_WIDTH 0x0008 +#define AL_EQUALIZER_HIGH_GAIN 0x0009 +#define AL_EQUALIZER_HIGH_CUTOFF 0x000A + +/* Effect type */ +#define AL_EFFECT_FIRST_PARAMETER 0x0000 +#define AL_EFFECT_LAST_PARAMETER 0x8000 +#define AL_EFFECT_TYPE 0x8001 + +/* Effect types, used with the AL_EFFECT_TYPE property */ +#define AL_EFFECT_NULL 0x0000 +#define AL_EFFECT_REVERB 0x0001 +#define AL_EFFECT_CHORUS 0x0002 +#define AL_EFFECT_DISTORTION 0x0003 +#define AL_EFFECT_ECHO 0x0004 +#define AL_EFFECT_FLANGER 0x0005 +#define AL_EFFECT_FREQUENCY_SHIFTER 0x0006 +#define AL_EFFECT_VOCAL_MORPHER 0x0007 +#define AL_EFFECT_PITCH_SHIFTER 0x0008 +#define AL_EFFECT_RING_MODULATOR 0x0009 +#define AL_EFFECT_AUTOWAH 0x000A +#define AL_EFFECT_COMPRESSOR 0x000B +#define AL_EFFECT_EQUALIZER 0x000C +#define AL_EFFECT_EAXREVERB 0x8000 + +/* Auxiliary Effect Slot properties. */ +#define AL_EFFECTSLOT_EFFECT 0x0001 +#define AL_EFFECTSLOT_GAIN 0x0002 +#define AL_EFFECTSLOT_AUXILIARY_SEND_AUTO 0x0003 + +/* NULL Auxiliary Slot ID to disable a source send. */ +#define AL_EFFECTSLOT_NULL 0x0000 + + +/* Filter properties. */ + +/* Lowpass filter parameters */ +#define AL_LOWPASS_GAIN 0x0001 +#define AL_LOWPASS_GAINHF 0x0002 + +/* Highpass filter parameters */ +#define AL_HIGHPASS_GAIN 0x0001 +#define AL_HIGHPASS_GAINLF 0x0002 + +/* Bandpass filter parameters */ +#define AL_BANDPASS_GAIN 0x0001 +#define AL_BANDPASS_GAINLF 0x0002 +#define AL_BANDPASS_GAINHF 0x0003 + +/* Filter type */ +#define AL_FILTER_FIRST_PARAMETER 0x0000 +#define AL_FILTER_LAST_PARAMETER 0x8000 +#define AL_FILTER_TYPE 0x8001 + +/* Filter types, used with the AL_FILTER_TYPE property */ +#define AL_FILTER_NULL 0x0000 +#define AL_FILTER_LOWPASS 0x0001 +#define AL_FILTER_HIGHPASS 0x0002 +#define AL_FILTER_BANDPASS 0x0003 + + +/* Effect object function types. */ +typedef void (AL_APIENTRY *LPALGENEFFECTS)(ALsizei, ALuint*); +typedef void (AL_APIENTRY *LPALDELETEEFFECTS)(ALsizei, ALuint*); +typedef ALboolean (AL_APIENTRY *LPALISEFFECT)(ALuint); +typedef void (AL_APIENTRY *LPALEFFECTI)(ALuint, ALenum, ALint); +typedef void (AL_APIENTRY *LPALEFFECTIV)(ALuint, ALenum, ALint*); +typedef void (AL_APIENTRY *LPALEFFECTF)(ALuint, ALenum, ALfloat); +typedef void (AL_APIENTRY *LPALEFFECTFV)(ALuint, ALenum, ALfloat*); +typedef void (AL_APIENTRY *LPALGETEFFECTI)(ALuint, ALenum, ALint*); +typedef void (AL_APIENTRY *LPALGETEFFECTIV)(ALuint, ALenum, ALint*); +typedef void (AL_APIENTRY *LPALGETEFFECTF)(ALuint, ALenum, ALfloat*); +typedef void (AL_APIENTRY *LPALGETEFFECTFV)(ALuint, ALenum, ALfloat*); + +/* Filter object function types. */ +typedef void (AL_APIENTRY *LPALGENFILTERS)(ALsizei, ALuint*); +typedef void (AL_APIENTRY *LPALDELETEFILTERS)(ALsizei, ALuint*); +typedef ALboolean (AL_APIENTRY *LPALISFILTER)(ALuint); +typedef void (AL_APIENTRY *LPALFILTERI)(ALuint, ALenum, ALint); +typedef void (AL_APIENTRY *LPALFILTERIV)(ALuint, ALenum, ALint*); +typedef void (AL_APIENTRY *LPALFILTERF)(ALuint, ALenum, ALfloat); +typedef void (AL_APIENTRY *LPALFILTERFV)(ALuint, ALenum, ALfloat*); +typedef void (AL_APIENTRY *LPALGETFILTERI)(ALuint, ALenum, ALint*); +typedef void (AL_APIENTRY *LPALGETFILTERIV)(ALuint, ALenum, ALint*); +typedef void (AL_APIENTRY *LPALGETFILTERF)(ALuint, ALenum, ALfloat*); +typedef void (AL_APIENTRY *LPALGETFILTERFV)(ALuint, ALenum, ALfloat*); + +/* Auxiliary Effect Slot object function types. */ +typedef void (AL_APIENTRY *LPALGENAUXILIARYEFFECTSLOTS)(ALsizei, ALuint*); +typedef void (AL_APIENTRY *LPALDELETEAUXILIARYEFFECTSLOTS)(ALsizei, ALuint*); +typedef ALboolean (AL_APIENTRY *LPALISAUXILIARYEFFECTSLOT)(ALuint); +typedef void (AL_APIENTRY *LPALAUXILIARYEFFECTSLOTI)(ALuint, ALenum, ALint); +typedef void (AL_APIENTRY *LPALAUXILIARYEFFECTSLOTIV)(ALuint, ALenum, ALint*); +typedef void (AL_APIENTRY *LPALAUXILIARYEFFECTSLOTF)(ALuint, ALenum, ALfloat); +typedef void (AL_APIENTRY *LPALAUXILIARYEFFECTSLOTFV)(ALuint, ALenum, ALfloat*); +typedef void (AL_APIENTRY *LPALGETAUXILIARYEFFECTSLOTI)(ALuint, ALenum, ALint*); +typedef void (AL_APIENTRY *LPALGETAUXILIARYEFFECTSLOTIV)(ALuint, ALenum, ALint*); +typedef void (AL_APIENTRY *LPALGETAUXILIARYEFFECTSLOTF)(ALuint, ALenum, ALfloat*); +typedef void (AL_APIENTRY *LPALGETAUXILIARYEFFECTSLOTFV)(ALuint, ALenum, ALfloat*); + +#ifdef AL_ALEXT_PROTOTYPES +AL_API ALvoid AL_APIENTRY alGenEffects(ALsizei n, ALuint *effects); +AL_API ALvoid AL_APIENTRY alDeleteEffects(ALsizei n, ALuint *effects); +AL_API ALboolean AL_APIENTRY alIsEffect(ALuint effect); +AL_API ALvoid AL_APIENTRY alEffecti(ALuint effect, ALenum param, ALint iValue); +AL_API ALvoid AL_APIENTRY alEffectiv(ALuint effect, ALenum param, ALint *piValues); +AL_API ALvoid AL_APIENTRY alEffectf(ALuint effect, ALenum param, ALfloat flValue); +AL_API ALvoid AL_APIENTRY alEffectfv(ALuint effect, ALenum param, ALfloat *pflValues); +AL_API ALvoid AL_APIENTRY alGetEffecti(ALuint effect, ALenum param, ALint *piValue); +AL_API ALvoid AL_APIENTRY alGetEffectiv(ALuint effect, ALenum param, ALint *piValues); +AL_API ALvoid AL_APIENTRY alGetEffectf(ALuint effect, ALenum param, ALfloat *pflValue); +AL_API ALvoid AL_APIENTRY alGetEffectfv(ALuint effect, ALenum param, ALfloat *pflValues); + +AL_API ALvoid AL_APIENTRY alGenFilters(ALsizei n, ALuint *filters); +AL_API ALvoid AL_APIENTRY alDeleteFilters(ALsizei n, ALuint *filters); +AL_API ALboolean AL_APIENTRY alIsFilter(ALuint filter); +AL_API ALvoid AL_APIENTRY alFilteri(ALuint filter, ALenum param, ALint iValue); +AL_API ALvoid AL_APIENTRY alFilteriv(ALuint filter, ALenum param, ALint *piValues); +AL_API ALvoid AL_APIENTRY alFilterf(ALuint filter, ALenum param, ALfloat flValue); +AL_API ALvoid AL_APIENTRY alFilterfv(ALuint filter, ALenum param, ALfloat *pflValues); +AL_API ALvoid AL_APIENTRY alGetFilteri(ALuint filter, ALenum param, ALint *piValue); +AL_API ALvoid AL_APIENTRY alGetFilteriv(ALuint filter, ALenum param, ALint *piValues); +AL_API ALvoid AL_APIENTRY alGetFilterf(ALuint filter, ALenum param, ALfloat *pflValue); +AL_API ALvoid AL_APIENTRY alGetFilterfv(ALuint filter, ALenum param, ALfloat *pflValues); + +AL_API ALvoid AL_APIENTRY alGenAuxiliaryEffectSlots(ALsizei n, ALuint *effectslots); +AL_API ALvoid AL_APIENTRY alDeleteAuxiliaryEffectSlots(ALsizei n, ALuint *effectslots); +AL_API ALboolean AL_APIENTRY alIsAuxiliaryEffectSlot(ALuint effectslot); +AL_API ALvoid AL_APIENTRY alAuxiliaryEffectSloti(ALuint effectslot, ALenum param, ALint iValue); +AL_API ALvoid AL_APIENTRY alAuxiliaryEffectSlotiv(ALuint effectslot, ALenum param, ALint *piValues); +AL_API ALvoid AL_APIENTRY alAuxiliaryEffectSlotf(ALuint effectslot, ALenum param, ALfloat flValue); +AL_API ALvoid AL_APIENTRY alAuxiliaryEffectSlotfv(ALuint effectslot, ALenum param, ALfloat *pflValues); +AL_API ALvoid AL_APIENTRY alGetAuxiliaryEffectSloti(ALuint effectslot, ALenum param, ALint *piValue); +AL_API ALvoid AL_APIENTRY alGetAuxiliaryEffectSlotiv(ALuint effectslot, ALenum param, ALint *piValues); +AL_API ALvoid AL_APIENTRY alGetAuxiliaryEffectSlotf(ALuint effectslot, ALenum param, ALfloat *pflValue); +AL_API ALvoid AL_APIENTRY alGetAuxiliaryEffectSlotfv(ALuint effectslot, ALenum param, ALfloat *pflValues); +#endif + +/* Filter ranges and defaults. */ + +/* Lowpass filter */ +#define LOWPASS_MIN_GAIN (0.0f) +#define LOWPASS_MAX_GAIN (1.0f) +#define LOWPASS_DEFAULT_GAIN (1.0f) + +#define LOWPASS_MIN_GAINHF (0.0f) +#define LOWPASS_MAX_GAINHF (1.0f) +#define LOWPASS_DEFAULT_GAINHF (1.0f) + +/* Highpass filter */ +#define HIGHPASS_MIN_GAIN (0.0f) +#define HIGHPASS_MAX_GAIN (1.0f) +#define HIGHPASS_DEFAULT_GAIN (1.0f) + +#define HIGHPASS_MIN_GAINLF (0.0f) +#define HIGHPASS_MAX_GAINLF (1.0f) +#define HIGHPASS_DEFAULT_GAINLF (1.0f) + +/* Bandpass filter */ +#define BANDPASS_MIN_GAIN (0.0f) +#define BANDPASS_MAX_GAIN (1.0f) +#define BANDPASS_DEFAULT_GAIN (1.0f) + +#define BANDPASS_MIN_GAINHF (0.0f) +#define BANDPASS_MAX_GAINHF (1.0f) +#define BANDPASS_DEFAULT_GAINHF (1.0f) + +#define BANDPASS_MIN_GAINLF (0.0f) +#define BANDPASS_MAX_GAINLF (1.0f) +#define BANDPASS_DEFAULT_GAINLF (1.0f) + + +/* Effect parameter ranges and defaults. */ + +/* Standard reverb effect */ +#define AL_REVERB_MIN_DENSITY (0.0f) +#define AL_REVERB_MAX_DENSITY (1.0f) +#define AL_REVERB_DEFAULT_DENSITY (1.0f) + +#define AL_REVERB_MIN_DIFFUSION (0.0f) +#define AL_REVERB_MAX_DIFFUSION (1.0f) +#define AL_REVERB_DEFAULT_DIFFUSION (1.0f) + +#define AL_REVERB_MIN_GAIN (0.0f) +#define AL_REVERB_MAX_GAIN (1.0f) +#define AL_REVERB_DEFAULT_GAIN (0.32f) + +#define AL_REVERB_MIN_GAINHF (0.0f) +#define AL_REVERB_MAX_GAINHF (1.0f) +#define AL_REVERB_DEFAULT_GAINHF (0.89f) + +#define AL_REVERB_MIN_DECAY_TIME (0.1f) +#define AL_REVERB_MAX_DECAY_TIME (20.0f) +#define AL_REVERB_DEFAULT_DECAY_TIME (1.49f) + +#define AL_REVERB_MIN_DECAY_HFRATIO (0.1f) +#define AL_REVERB_MAX_DECAY_HFRATIO (2.0f) +#define AL_REVERB_DEFAULT_DECAY_HFRATIO (0.83f) + +#define AL_REVERB_MIN_REFLECTIONS_GAIN (0.0f) +#define AL_REVERB_MAX_REFLECTIONS_GAIN (3.16f) +#define AL_REVERB_DEFAULT_REFLECTIONS_GAIN (0.05f) + +#define AL_REVERB_MIN_REFLECTIONS_DELAY (0.0f) +#define AL_REVERB_MAX_REFLECTIONS_DELAY (0.3f) +#define AL_REVERB_DEFAULT_REFLECTIONS_DELAY (0.007f) + +#define AL_REVERB_MIN_LATE_REVERB_GAIN (0.0f) +#define AL_REVERB_MAX_LATE_REVERB_GAIN (10.0f) +#define AL_REVERB_DEFAULT_LATE_REVERB_GAIN (1.26f) + +#define AL_REVERB_MIN_LATE_REVERB_DELAY (0.0f) +#define AL_REVERB_MAX_LATE_REVERB_DELAY (0.1f) +#define AL_REVERB_DEFAULT_LATE_REVERB_DELAY (0.011f) + +#define AL_REVERB_MIN_AIR_ABSORPTION_GAINHF (0.892f) +#define AL_REVERB_MAX_AIR_ABSORPTION_GAINHF (1.0f) +#define AL_REVERB_DEFAULT_AIR_ABSORPTION_GAINHF (0.994f) + +#define AL_REVERB_MIN_ROOM_ROLLOFF_FACTOR (0.0f) +#define AL_REVERB_MAX_ROOM_ROLLOFF_FACTOR (10.0f) +#define AL_REVERB_DEFAULT_ROOM_ROLLOFF_FACTOR (0.0f) + +#define AL_REVERB_MIN_DECAY_HFLIMIT AL_FALSE +#define AL_REVERB_MAX_DECAY_HFLIMIT AL_TRUE +#define AL_REVERB_DEFAULT_DECAY_HFLIMIT AL_TRUE + +/* EAX reverb effect */ +#define AL_EAXREVERB_MIN_DENSITY (0.0f) +#define AL_EAXREVERB_MAX_DENSITY (1.0f) +#define AL_EAXREVERB_DEFAULT_DENSITY (1.0f) + +#define AL_EAXREVERB_MIN_DIFFUSION (0.0f) +#define AL_EAXREVERB_MAX_DIFFUSION (1.0f) +#define AL_EAXREVERB_DEFAULT_DIFFUSION (1.0f) + +#define AL_EAXREVERB_MIN_GAIN (0.0f) +#define AL_EAXREVERB_MAX_GAIN (1.0f) +#define AL_EAXREVERB_DEFAULT_GAIN (0.32f) + +#define AL_EAXREVERB_MIN_GAINHF (0.0f) +#define AL_EAXREVERB_MAX_GAINHF (1.0f) +#define AL_EAXREVERB_DEFAULT_GAINHF (0.89f) + +#define AL_EAXREVERB_MIN_GAINLF (0.0f) +#define AL_EAXREVERB_MAX_GAINLF (1.0f) +#define AL_EAXREVERB_DEFAULT_GAINLF (1.0f) + +#define AL_EAXREVERB_MIN_DECAY_TIME (0.1f) +#define AL_EAXREVERB_MAX_DECAY_TIME (20.0f) +#define AL_EAXREVERB_DEFAULT_DECAY_TIME (1.49f) + +#define AL_EAXREVERB_MIN_DECAY_HFRATIO (0.1f) +#define AL_EAXREVERB_MAX_DECAY_HFRATIO (2.0f) +#define AL_EAXREVERB_DEFAULT_DECAY_HFRATIO (0.83f) + +#define AL_EAXREVERB_MIN_DECAY_LFRATIO (0.1f) +#define AL_EAXREVERB_MAX_DECAY_LFRATIO (2.0f) +#define AL_EAXREVERB_DEFAULT_DECAY_LFRATIO (1.0f) + +#define AL_EAXREVERB_MIN_REFLECTIONS_GAIN (0.0f) +#define AL_EAXREVERB_MAX_REFLECTIONS_GAIN (3.16f) +#define AL_EAXREVERB_DEFAULT_REFLECTIONS_GAIN (0.05f) + +#define AL_EAXREVERB_MIN_REFLECTIONS_DELAY (0.0f) +#define AL_EAXREVERB_MAX_REFLECTIONS_DELAY (0.3f) +#define AL_EAXREVERB_DEFAULT_REFLECTIONS_DELAY (0.007f) + +#define AL_EAXREVERB_DEFAULT_REFLECTIONS_PAN_XYZ (0.0f) + +#define AL_EAXREVERB_MIN_LATE_REVERB_GAIN (0.0f) +#define AL_EAXREVERB_MAX_LATE_REVERB_GAIN (10.0f) +#define AL_EAXREVERB_DEFAULT_LATE_REVERB_GAIN (1.26f) + +#define AL_EAXREVERB_MIN_LATE_REVERB_DELAY (0.0f) +#define AL_EAXREVERB_MAX_LATE_REVERB_DELAY (0.1f) +#define AL_EAXREVERB_DEFAULT_LATE_REVERB_DELAY (0.011f) + +#define AL_EAXREVERB_DEFAULT_LATE_REVERB_PAN_XYZ (0.0f) + +#define AL_EAXREVERB_MIN_ECHO_TIME (0.075f) +#define AL_EAXREVERB_MAX_ECHO_TIME (0.25f) +#define AL_EAXREVERB_DEFAULT_ECHO_TIME (0.25f) + +#define AL_EAXREVERB_MIN_ECHO_DEPTH (0.0f) +#define AL_EAXREVERB_MAX_ECHO_DEPTH (1.0f) +#define AL_EAXREVERB_DEFAULT_ECHO_DEPTH (0.0f) + +#define AL_EAXREVERB_MIN_MODULATION_TIME (0.04f) +#define AL_EAXREVERB_MAX_MODULATION_TIME (4.0f) +#define AL_EAXREVERB_DEFAULT_MODULATION_TIME (0.25f) + +#define AL_EAXREVERB_MIN_MODULATION_DEPTH (0.0f) +#define AL_EAXREVERB_MAX_MODULATION_DEPTH (1.0f) +#define AL_EAXREVERB_DEFAULT_MODULATION_DEPTH (0.0f) + +#define AL_EAXREVERB_MIN_AIR_ABSORPTION_GAINHF (0.892f) +#define AL_EAXREVERB_MAX_AIR_ABSORPTION_GAINHF (1.0f) +#define AL_EAXREVERB_DEFAULT_AIR_ABSORPTION_GAINHF (0.994f) + +#define AL_EAXREVERB_MIN_HFREFERENCE (1000.0f) +#define AL_EAXREVERB_MAX_HFREFERENCE (20000.0f) +#define AL_EAXREVERB_DEFAULT_HFREFERENCE (5000.0f) + +#define AL_EAXREVERB_MIN_LFREFERENCE (20.0f) +#define AL_EAXREVERB_MAX_LFREFERENCE (1000.0f) +#define AL_EAXREVERB_DEFAULT_LFREFERENCE (250.0f) + +#define AL_EAXREVERB_MIN_ROOM_ROLLOFF_FACTOR (0.0f) +#define AL_EAXREVERB_MAX_ROOM_ROLLOFF_FACTOR (10.0f) +#define AL_EAXREVERB_DEFAULT_ROOM_ROLLOFF_FACTOR (0.0f) + +#define AL_EAXREVERB_MIN_DECAY_HFLIMIT AL_FALSE +#define AL_EAXREVERB_MAX_DECAY_HFLIMIT AL_TRUE +#define AL_EAXREVERB_DEFAULT_DECAY_HFLIMIT AL_TRUE + +/* Chorus effect */ +#define AL_CHORUS_WAVEFORM_SINUSOID (0) +#define AL_CHORUS_WAVEFORM_TRIANGLE (1) + +#define AL_CHORUS_MIN_WAVEFORM (0) +#define AL_CHORUS_MAX_WAVEFORM (1) +#define AL_CHORUS_DEFAULT_WAVEFORM (1) + +#define AL_CHORUS_MIN_PHASE (-180) +#define AL_CHORUS_MAX_PHASE (180) +#define AL_CHORUS_DEFAULT_PHASE (90) + +#define AL_CHORUS_MIN_RATE (0.0f) +#define AL_CHORUS_MAX_RATE (10.0f) +#define AL_CHORUS_DEFAULT_RATE (1.1f) + +#define AL_CHORUS_MIN_DEPTH (0.0f) +#define AL_CHORUS_MAX_DEPTH (1.0f) +#define AL_CHORUS_DEFAULT_DEPTH (0.1f) + +#define AL_CHORUS_MIN_FEEDBACK (-1.0f) +#define AL_CHORUS_MAX_FEEDBACK (1.0f) +#define AL_CHORUS_DEFAULT_FEEDBACK (0.25f) + +#define AL_CHORUS_MIN_DELAY (0.0f) +#define AL_CHORUS_MAX_DELAY (0.016f) +#define AL_CHORUS_DEFAULT_DELAY (0.016f) + +/* Distortion effect */ +#define AL_DISTORTION_MIN_EDGE (0.0f) +#define AL_DISTORTION_MAX_EDGE (1.0f) +#define AL_DISTORTION_DEFAULT_EDGE (0.2f) + +#define AL_DISTORTION_MIN_GAIN (0.01f) +#define AL_DISTORTION_MAX_GAIN (1.0f) +#define AL_DISTORTION_DEFAULT_GAIN (0.05f) + +#define AL_DISTORTION_MIN_LOWPASS_CUTOFF (80.0f) +#define AL_DISTORTION_MAX_LOWPASS_CUTOFF (24000.0f) +#define AL_DISTORTION_DEFAULT_LOWPASS_CUTOFF (8000.0f) + +#define AL_DISTORTION_MIN_EQCENTER (80.0f) +#define AL_DISTORTION_MAX_EQCENTER (24000.0f) +#define AL_DISTORTION_DEFAULT_EQCENTER (3600.0f) + +#define AL_DISTORTION_MIN_EQBANDWIDTH (80.0f) +#define AL_DISTORTION_MAX_EQBANDWIDTH (24000.0f) +#define AL_DISTORTION_DEFAULT_EQBANDWIDTH (3600.0f) + +/* Echo effect */ +#define AL_ECHO_MIN_DELAY (0.0f) +#define AL_ECHO_MAX_DELAY (0.207f) +#define AL_ECHO_DEFAULT_DELAY (0.1f) + +#define AL_ECHO_MIN_LRDELAY (0.0f) +#define AL_ECHO_MAX_LRDELAY (0.404f) +#define AL_ECHO_DEFAULT_LRDELAY (0.1f) + +#define AL_ECHO_MIN_DAMPING (0.0f) +#define AL_ECHO_MAX_DAMPING (0.99f) +#define AL_ECHO_DEFAULT_DAMPING (0.5f) + +#define AL_ECHO_MIN_FEEDBACK (0.0f) +#define AL_ECHO_MAX_FEEDBACK (1.0f) +#define AL_ECHO_DEFAULT_FEEDBACK (0.5f) + +#define AL_ECHO_MIN_SPREAD (-1.0f) +#define AL_ECHO_MAX_SPREAD (1.0f) +#define AL_ECHO_DEFAULT_SPREAD (-1.0f) + +/* Flanger effect */ +#define AL_FLANGER_WAVEFORM_SINUSOID (0) +#define AL_FLANGER_WAVEFORM_TRIANGLE (1) + +#define AL_FLANGER_MIN_WAVEFORM (0) +#define AL_FLANGER_MAX_WAVEFORM (1) +#define AL_FLANGER_DEFAULT_WAVEFORM (1) + +#define AL_FLANGER_MIN_PHASE (-180) +#define AL_FLANGER_MAX_PHASE (180) +#define AL_FLANGER_DEFAULT_PHASE (0) + +#define AL_FLANGER_MIN_RATE (0.0f) +#define AL_FLANGER_MAX_RATE (10.0f) +#define AL_FLANGER_DEFAULT_RATE (0.27f) + +#define AL_FLANGER_MIN_DEPTH (0.0f) +#define AL_FLANGER_MAX_DEPTH (1.0f) +#define AL_FLANGER_DEFAULT_DEPTH (1.0f) + +#define AL_FLANGER_MIN_FEEDBACK (-1.0f) +#define AL_FLANGER_MAX_FEEDBACK (1.0f) +#define AL_FLANGER_DEFAULT_FEEDBACK (-0.5f) + +#define AL_FLANGER_MIN_DELAY (0.0f) +#define AL_FLANGER_MAX_DELAY (0.004f) +#define AL_FLANGER_DEFAULT_DELAY (0.002f) + +/* Frequency shifter effect */ +#define AL_FREQUENCY_SHIFTER_MIN_FREQUENCY (0.0f) +#define AL_FREQUENCY_SHIFTER_MAX_FREQUENCY (24000.0f) +#define AL_FREQUENCY_SHIFTER_DEFAULT_FREQUENCY (0.0f) + +#define AL_FREQUENCY_SHIFTER_MIN_LEFT_DIRECTION (0) +#define AL_FREQUENCY_SHIFTER_MAX_LEFT_DIRECTION (2) +#define AL_FREQUENCY_SHIFTER_DEFAULT_LEFT_DIRECTION (0) + +#define AL_FREQUENCY_SHIFTER_DIRECTION_DOWN (0) +#define AL_FREQUENCY_SHIFTER_DIRECTION_UP (1) +#define AL_FREQUENCY_SHIFTER_DIRECTION_OFF (2) + +#define AL_FREQUENCY_SHIFTER_MIN_RIGHT_DIRECTION (0) +#define AL_FREQUENCY_SHIFTER_MAX_RIGHT_DIRECTION (2) +#define AL_FREQUENCY_SHIFTER_DEFAULT_RIGHT_DIRECTION (0) + +/* Vocal morpher effect */ +#define AL_VOCAL_MORPHER_MIN_PHONEMEA (0) +#define AL_VOCAL_MORPHER_MAX_PHONEMEA (29) +#define AL_VOCAL_MORPHER_DEFAULT_PHONEMEA (0) + +#define AL_VOCAL_MORPHER_MIN_PHONEMEA_COARSE_TUNING (-24) +#define AL_VOCAL_MORPHER_MAX_PHONEMEA_COARSE_TUNING (24) +#define AL_VOCAL_MORPHER_DEFAULT_PHONEMEA_COARSE_TUNING (0) + +#define AL_VOCAL_MORPHER_MIN_PHONEMEB (0) +#define AL_VOCAL_MORPHER_MAX_PHONEMEB (29) +#define AL_VOCAL_MORPHER_DEFAULT_PHONEMEB (10) + +#define AL_VOCAL_MORPHER_MIN_PHONEMEB_COARSE_TUNING (-24) +#define AL_VOCAL_MORPHER_MAX_PHONEMEB_COARSE_TUNING (24) +#define AL_VOCAL_MORPHER_DEFAULT_PHONEMEB_COARSE_TUNING (0) + +#define AL_VOCAL_MORPHER_PHONEME_A (0) +#define AL_VOCAL_MORPHER_PHONEME_E (1) +#define AL_VOCAL_MORPHER_PHONEME_I (2) +#define AL_VOCAL_MORPHER_PHONEME_O (3) +#define AL_VOCAL_MORPHER_PHONEME_U (4) +#define AL_VOCAL_MORPHER_PHONEME_AA (5) +#define AL_VOCAL_MORPHER_PHONEME_AE (6) +#define AL_VOCAL_MORPHER_PHONEME_AH (7) +#define AL_VOCAL_MORPHER_PHONEME_AO (8) +#define AL_VOCAL_MORPHER_PHONEME_EH (9) +#define AL_VOCAL_MORPHER_PHONEME_ER (10) +#define AL_VOCAL_MORPHER_PHONEME_IH (11) +#define AL_VOCAL_MORPHER_PHONEME_IY (12) +#define AL_VOCAL_MORPHER_PHONEME_UH (13) +#define AL_VOCAL_MORPHER_PHONEME_UW (14) +#define AL_VOCAL_MORPHER_PHONEME_B (15) +#define AL_VOCAL_MORPHER_PHONEME_D (16) +#define AL_VOCAL_MORPHER_PHONEME_F (17) +#define AL_VOCAL_MORPHER_PHONEME_G (18) +#define AL_VOCAL_MORPHER_PHONEME_J (19) +#define AL_VOCAL_MORPHER_PHONEME_K (20) +#define AL_VOCAL_MORPHER_PHONEME_L (21) +#define AL_VOCAL_MORPHER_PHONEME_M (22) +#define AL_VOCAL_MORPHER_PHONEME_N (23) +#define AL_VOCAL_MORPHER_PHONEME_P (24) +#define AL_VOCAL_MORPHER_PHONEME_R (25) +#define AL_VOCAL_MORPHER_PHONEME_S (26) +#define AL_VOCAL_MORPHER_PHONEME_T (27) +#define AL_VOCAL_MORPHER_PHONEME_V (28) +#define AL_VOCAL_MORPHER_PHONEME_Z (29) + +#define AL_VOCAL_MORPHER_WAVEFORM_SINUSOID (0) +#define AL_VOCAL_MORPHER_WAVEFORM_TRIANGLE (1) +#define AL_VOCAL_MORPHER_WAVEFORM_SAWTOOTH (2) + +#define AL_VOCAL_MORPHER_MIN_WAVEFORM (0) +#define AL_VOCAL_MORPHER_MAX_WAVEFORM (2) +#define AL_VOCAL_MORPHER_DEFAULT_WAVEFORM (0) + +#define AL_VOCAL_MORPHER_MIN_RATE (0.0f) +#define AL_VOCAL_MORPHER_MAX_RATE (10.0f) +#define AL_VOCAL_MORPHER_DEFAULT_RATE (1.41f) + +/* Pitch shifter effect */ +#define AL_PITCH_SHIFTER_MIN_COARSE_TUNE (-12) +#define AL_PITCH_SHIFTER_MAX_COARSE_TUNE (12) +#define AL_PITCH_SHIFTER_DEFAULT_COARSE_TUNE (12) + +#define AL_PITCH_SHIFTER_MIN_FINE_TUNE (-50) +#define AL_PITCH_SHIFTER_MAX_FINE_TUNE (50) +#define AL_PITCH_SHIFTER_DEFAULT_FINE_TUNE (0) + +/* Ring modulator effect */ +#define AL_RING_MODULATOR_MIN_FREQUENCY (0.0f) +#define AL_RING_MODULATOR_MAX_FREQUENCY (8000.0f) +#define AL_RING_MODULATOR_DEFAULT_FREQUENCY (440.0f) + +#define AL_RING_MODULATOR_MIN_HIGHPASS_CUTOFF (0.0f) +#define AL_RING_MODULATOR_MAX_HIGHPASS_CUTOFF (24000.0f) +#define AL_RING_MODULATOR_DEFAULT_HIGHPASS_CUTOFF (800.0f) + +#define AL_RING_MODULATOR_SINUSOID (0) +#define AL_RING_MODULATOR_SAWTOOTH (1) +#define AL_RING_MODULATOR_SQUARE (2) + +#define AL_RING_MODULATOR_MIN_WAVEFORM (0) +#define AL_RING_MODULATOR_MAX_WAVEFORM (2) +#define AL_RING_MODULATOR_DEFAULT_WAVEFORM (0) + +/* Autowah effect */ +#define AL_AUTOWAH_MIN_ATTACK_TIME (0.0001f) +#define AL_AUTOWAH_MAX_ATTACK_TIME (1.0f) +#define AL_AUTOWAH_DEFAULT_ATTACK_TIME (0.06f) + +#define AL_AUTOWAH_MIN_RELEASE_TIME (0.0001f) +#define AL_AUTOWAH_MAX_RELEASE_TIME (1.0f) +#define AL_AUTOWAH_DEFAULT_RELEASE_TIME (0.06f) + +#define AL_AUTOWAH_MIN_RESONANCE (2.0f) +#define AL_AUTOWAH_MAX_RESONANCE (1000.0f) +#define AL_AUTOWAH_DEFAULT_RESONANCE (1000.0f) + +#define AL_AUTOWAH_MIN_PEAK_GAIN (0.00003f) +#define AL_AUTOWAH_MAX_PEAK_GAIN (31621.0f) +#define AL_AUTOWAH_DEFAULT_PEAK_GAIN (11.22f) + +/* Compressor effect */ +#define AL_COMPRESSOR_MIN_ONOFF (0) +#define AL_COMPRESSOR_MAX_ONOFF (1) +#define AL_COMPRESSOR_DEFAULT_ONOFF (1) + +/* Equalizer effect */ +#define AL_EQUALIZER_MIN_LOW_GAIN (0.126f) +#define AL_EQUALIZER_MAX_LOW_GAIN (7.943f) +#define AL_EQUALIZER_DEFAULT_LOW_GAIN (1.0f) + +#define AL_EQUALIZER_MIN_LOW_CUTOFF (50.0f) +#define AL_EQUALIZER_MAX_LOW_CUTOFF (800.0f) +#define AL_EQUALIZER_DEFAULT_LOW_CUTOFF (200.0f) + +#define AL_EQUALIZER_MIN_MID1_GAIN (0.126f) +#define AL_EQUALIZER_MAX_MID1_GAIN (7.943f) +#define AL_EQUALIZER_DEFAULT_MID1_GAIN (1.0f) + +#define AL_EQUALIZER_MIN_MID1_CENTER (200.0f) +#define AL_EQUALIZER_MAX_MID1_CENTER (3000.0f) +#define AL_EQUALIZER_DEFAULT_MID1_CENTER (500.0f) + +#define AL_EQUALIZER_MIN_MID1_WIDTH (0.01f) +#define AL_EQUALIZER_MAX_MID1_WIDTH (1.0f) +#define AL_EQUALIZER_DEFAULT_MID1_WIDTH (1.0f) + +#define AL_EQUALIZER_MIN_MID2_GAIN (0.126f) +#define AL_EQUALIZER_MAX_MID2_GAIN (7.943f) +#define AL_EQUALIZER_DEFAULT_MID2_GAIN (1.0f) + +#define AL_EQUALIZER_MIN_MID2_CENTER (1000.0f) +#define AL_EQUALIZER_MAX_MID2_CENTER (8000.0f) +#define AL_EQUALIZER_DEFAULT_MID2_CENTER (3000.0f) + +#define AL_EQUALIZER_MIN_MID2_WIDTH (0.01f) +#define AL_EQUALIZER_MAX_MID2_WIDTH (1.0f) +#define AL_EQUALIZER_DEFAULT_MID2_WIDTH (1.0f) + +#define AL_EQUALIZER_MIN_HIGH_GAIN (0.126f) +#define AL_EQUALIZER_MAX_HIGH_GAIN (7.943f) +#define AL_EQUALIZER_DEFAULT_HIGH_GAIN (1.0f) + +#define AL_EQUALIZER_MIN_HIGH_CUTOFF (4000.0f) +#define AL_EQUALIZER_MAX_HIGH_CUTOFF (16000.0f) +#define AL_EQUALIZER_DEFAULT_HIGH_CUTOFF (6000.0f) + + +/* Source parameter value ranges and defaults. */ +#define AL_MIN_AIR_ABSORPTION_FACTOR (0.0f) +#define AL_MAX_AIR_ABSORPTION_FACTOR (10.0f) +#define AL_DEFAULT_AIR_ABSORPTION_FACTOR (0.0f) + +#define AL_MIN_ROOM_ROLLOFF_FACTOR (0.0f) +#define AL_MAX_ROOM_ROLLOFF_FACTOR (10.0f) +#define AL_DEFAULT_ROOM_ROLLOFF_FACTOR (0.0f) + +#define AL_MIN_CONE_OUTER_GAINHF (0.0f) +#define AL_MAX_CONE_OUTER_GAINHF (1.0f) +#define AL_DEFAULT_CONE_OUTER_GAINHF (1.0f) + +#define AL_MIN_DIRECT_FILTER_GAINHF_AUTO AL_FALSE +#define AL_MAX_DIRECT_FILTER_GAINHF_AUTO AL_TRUE +#define AL_DEFAULT_DIRECT_FILTER_GAINHF_AUTO AL_TRUE + +#define AL_MIN_AUXILIARY_SEND_FILTER_GAIN_AUTO AL_FALSE +#define AL_MAX_AUXILIARY_SEND_FILTER_GAIN_AUTO AL_TRUE +#define AL_DEFAULT_AUXILIARY_SEND_FILTER_GAIN_AUTO AL_TRUE + +#define AL_MIN_AUXILIARY_SEND_FILTER_GAINHF_AUTO AL_FALSE +#define AL_MAX_AUXILIARY_SEND_FILTER_GAINHF_AUTO AL_TRUE +#define AL_DEFAULT_AUXILIARY_SEND_FILTER_GAINHF_AUTO AL_TRUE + + +/* Listener parameter value ranges and defaults. */ +#define AL_MIN_METERS_PER_UNIT FLT_MIN +#define AL_MAX_METERS_PER_UNIT FLT_MAX +#define AL_DEFAULT_METERS_PER_UNIT (1.0f) + + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* AL_EFX_H */ diff --git a/src/sound/except.h b/src/sound/except.h new file mode 100644 index 0000000000..10b4841309 --- /dev/null +++ b/src/sound/except.h @@ -0,0 +1,34 @@ +#ifndef __EXCEPT_H +#define __EXCEPT_H + +#ifdef _MSC_VER +//========================================================================== +// +// CheckException +// +//========================================================================== + +#ifndef FACILITY_VISUALCPP +#define FACILITY_VISUALCPP ((LONG)0x6d) +#endif +#define VcppException(sev,err) ((sev) | (FACILITY_VISUALCPP<<16) | err) + +inline int CheckException(DWORD code) +{ + if (code == VcppException(ERROR_SEVERITY_ERROR,ERROR_MOD_NOT_FOUND) || + code == VcppException(ERROR_SEVERITY_ERROR,ERROR_PROC_NOT_FOUND)) + { + return EXCEPTION_EXECUTE_HANDLER; + } + return EXCEPTION_CONTINUE_SEARCH; +} + + +#else + +#define __try +#define __except(a) if (0) + +#endif + +#endif \ No newline at end of file diff --git a/src/sound/fmod_wrap.h b/src/sound/fmod_wrap.h index f77eed36c6..be676d9ddc 100644 --- a/src/sound/fmod_wrap.h +++ b/src/sound/fmod_wrap.h @@ -2,6 +2,8 @@ #ifndef FMOD_WRAP_H #define FMOD_WRAP_H +#ifndef NO_FMOD + #if !defined(_WIN32) || defined(_MSC_VER) // Use the real C++ interface if it's supported on this platform. #include "fmod.hpp" @@ -610,3 +612,4 @@ namespace FMOD #endif #endif +#endif diff --git a/src/sound/fmodsound.cpp b/src/sound/fmodsound.cpp index 269720c485..73e4195557 100644 --- a/src/sound/fmodsound.cpp +++ b/src/sound/fmodsound.cpp @@ -52,6 +52,7 @@ extern HWND Window; #include #endif +#include "except.h" #include "templates.h" #include "fmodsound.h" #include "c_cvars.h" @@ -62,6 +63,7 @@ extern HWND Window; #include "v_palette.h" #include "cmdlib.h" #include "s_sound.h" +#include "files.h" #if FMOD_VERSION > 0x42899 && FMOD_VERSION < 0x43400 #error You are trying to compile with an unsupported version of FMOD. @@ -79,6 +81,35 @@ extern HWND Window; #define SPECTRUM_SIZE 256 +// PUBLIC DATA DEFINITIONS ------------------------------------------------- + +ReverbContainer *ForcedEnvironment; + +CVAR (Int, snd_driver, 0, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) +CVAR (Int, snd_buffercount, 0, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) +CVAR (Bool, snd_hrtf, false, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) +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. +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; + } +} + +#ifndef NO_FMOD #if FMOD_VERSION < 0x43400 #define FMOD_OPENSTATE_PLAYING FMOD_OPENSTATE_STREAMING #endif @@ -112,34 +143,6 @@ EXTERN_CVAR (Int, snd_channels) extern int sfx_empty; -// PUBLIC DATA DEFINITIONS ------------------------------------------------- - -ReverbContainer *ForcedEnvironment; - -CVAR (Int, snd_driver, 0, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) -CVAR (Int, snd_buffercount, 0, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) -CVAR (Bool, snd_hrtf, false, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) -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 (Bool, snd_profile, false, 0) -CVAR (String, snd_midipatchset, "", CVAR_ARCHIVE|CVAR_GLOBALCONFIG); - -// 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; @@ -301,14 +304,21 @@ class FMODStreamCapsule : public SoundStream public: FMODStreamCapsule(FMOD::Sound *stream, FMODSoundRenderer *owner, const char *url) : Owner(owner), Stream(NULL), Channel(NULL), - UserData(NULL), Callback(NULL), URL(url), Ended(false) + UserData(NULL), Callback(NULL), URL(url), Reader(NULL), Ended(false) { SetStream(stream); } + FMODStreamCapsule(FMOD::Sound *stream, FMODSoundRenderer *owner, FileReader *reader) + : Owner(owner), Stream(NULL), Channel(NULL), + UserData(NULL), Callback(NULL), Reader(reader), Ended(false) + { + SetStream(stream); + } + FMODStreamCapsule(void *udata, SoundStreamCallback callback, FMODSoundRenderer *owner) : Owner(owner), Stream(NULL), Channel(NULL), - UserData(udata), Callback(callback), Ended(false) + UserData(udata), Callback(callback), Reader(NULL), Ended(false) {} ~FMODStreamCapsule() @@ -321,6 +331,10 @@ public: { Stream->release(); } + if (Reader != NULL) + { + delete Reader; + } } void SetStream(FMOD::Sound *stream) @@ -590,6 +604,7 @@ private: FMOD::Channel *Channel; void *UserData; SoundStreamCallback Callback; + FileReader *Reader; FString URL; bool Ended; bool JustStarted; @@ -619,30 +634,6 @@ bool FMODSoundRenderer::IsValid() return InitSuccess; } -#ifdef _MSC_VER -//========================================================================== -// -// CheckException -// -//========================================================================== - -#ifndef FACILITY_VISUALCPP -#define FACILITY_VISUALCPP ((LONG)0x6d) -#endif -#define VcppException(sev,err) ((sev) | (FACILITY_VISUALCPP<<16) | err) - -static int CheckException(DWORD code) -{ - if (code == VcppException(ERROR_SEVERITY_ERROR,ERROR_MOD_NOT_FOUND) || - code == VcppException(ERROR_SEVERITY_ERROR,ERROR_PROC_NOT_FOUND)) - { - return EXCEPTION_EXECUTE_HANDLER; - } - return EXCEPTION_CONTINUE_SEARCH; -} - -#endif - //========================================================================== // // FMODSoundRenderer :: Init @@ -680,14 +671,8 @@ bool FMODSoundRenderer::Init() Printf("I_InitSound: Initializing FMOD\n"); - // Create a System object and initialize. -#ifdef _MSC_VER - __try { -#endif - result = FMOD::System_Create(&Sys); -#ifdef _MSC_VER - } - __except(CheckException(GetExceptionCode())) + // 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" @@ -697,7 +682,9 @@ bool FMODSoundRenderer::Init() ".dll\n"); return false; } -#endif + + // Create a System object and initialize. + result = FMOD::System_Create(&Sys); if (result != FMOD_OK) { Sys = NULL; @@ -1604,83 +1591,171 @@ static void SetCustomLoopPts(FMOD::Sound *sound) //========================================================================== // -// FMODSoundRenderer :: OpenStream +// open_reader_callback +// close_reader_callback +// read_reader_callback +// seek_reader_callback // -// Creates a streaming sound from a file on disk. +// FMOD_CREATESOUNDEXINFO callbacks to handle reading resource data from a +// FileReader. // //========================================================================== -SoundStream *FMODSoundRenderer::OpenStream(const char *filename_or_data, int flags, int offset, int length) +static FMOD_RESULT F_CALLBACK open_reader_callback(const char *name, int unicode, unsigned int *filesize, void **handle, void **userdata) { - FMOD_MODE mode; - FMOD_CREATESOUNDEXINFO exinfo; - FMOD::Sound *stream; - FMOD_RESULT result; - bool url; - FString patches; + FileReader *reader = NULL; + if(sscanf(name, "_FileReader_%p", &reader) != 1) + { + Printf("Invalid name in callback: %s\n", name); + return FMOD_ERR_FILE_NOTFOUND; + } - InitCreateSoundExInfo(&exinfo); - mode = FMOD_SOFTWARE | FMOD_2D | FMOD_CREATESTREAM; - if (flags & SoundStream::Loop) - { - mode |= FMOD_LOOP_NORMAL; - } - if (offset == -1) - { - mode |= FMOD_OPENMEMORY; - offset = 0; - } - exinfo.length = length; - exinfo.fileoffset = offset; - if ((*snd_midipatchset)[0] != '\0') - { + *filesize = reader->GetLength(); + *handle = reader; + *userdata = reader; + return FMOD_OK; +} + +static FMOD_RESULT F_CALLBACK close_reader_callback(void *handle, void *userdata) +{ + return FMOD_OK; +} + +static FMOD_RESULT F_CALLBACK read_reader_callback(void *handle, void *buffer, unsigned int sizebytes, unsigned int *bytesread, void *userdata) +{ + FileReader *reader = reinterpret_cast(handle); + *bytesread = reader->Read(buffer, sizebytes); + if(*bytesread > 0) return FMOD_OK; + return FMOD_ERR_FILE_EOF; +} + +static FMOD_RESULT F_CALLBACK seek_reader_callback(void *handle, unsigned int pos, void *userdata) +{ + FileReader *reader = reinterpret_cast(handle); + if(reader->Seek(pos, SEEK_SET) == 0) + return FMOD_OK; + return FMOD_ERR_FILE_COULDNOTSEEK; +} + + +//========================================================================== +// +// FMODSoundRenderer :: OpenStream +// +// Creates a streaming sound from a FileReader. +// +//========================================================================== + +SoundStream *FMODSoundRenderer::OpenStream(FileReader *reader, int flags) +{ + FMOD_MODE mode; + FMOD_CREATESOUNDEXINFO exinfo; + FMOD::Sound *stream; + FMOD_RESULT result; + FString patches; + FString name; + + InitCreateSoundExInfo(&exinfo); + exinfo.useropen = open_reader_callback; + exinfo.userclose = close_reader_callback; + exinfo.userread = read_reader_callback; + exinfo.userseek = seek_reader_callback; + + mode = FMOD_SOFTWARE | FMOD_2D | FMOD_CREATESTREAM; + if(flags & SoundStream::Loop) + mode |= FMOD_LOOP_NORMAL; + if((*snd_midipatchset)[0] != '\0') + { #ifdef _WIN32 - // If the path does not contain any path separators, automatically - // prepend $PROGDIR to the path. - if (strcspn(snd_midipatchset, ":/\\") == strlen(snd_midipatchset)) - { - patches << "$PROGDIR/" << snd_midipatchset; - patches = NicePath(patches); - } - else + // If the path does not contain any path separators, automatically + // prepend $PROGDIR to the path. + if (strcspn(snd_midipatchset, ":/\\") == strlen(snd_midipatchset)) + { + patches << "$PROGDIR/" << snd_midipatchset; + patches = NicePath(patches); + } + else #endif - { - patches = NicePath(snd_midipatchset); - } - exinfo.dlsname = patches; - } + { + patches = NicePath(snd_midipatchset); + } + exinfo.dlsname = patches; + } - url = (offset == 0 && length == 0 && strstr(filename_or_data, "://") > filename_or_data); - if (url) - { - // Use a larger buffer for URLs so that it's less likely to be effected - // by hiccups in the data rate from the remote server. - Sys->setStreamBufferSize(64*1024, FMOD_TIMEUNIT_RAWBYTES); - } - result = Sys->createSound(filename_or_data, mode, &exinfo, &stream); - if (url) - { - // Restore standard buffer size. - Sys->setStreamBufferSize(16*1024, FMOD_TIMEUNIT_RAWBYTES); - } - if (result == FMOD_ERR_FORMAT && exinfo.dlsname != NULL) - { - // FMOD_ERR_FORMAT could refer to either the main sound file or - // to the DLS instrument set. Try again without special DLS - // instruments to see if that lets it succeed. - exinfo.dlsname = NULL; - result = Sys->createSound(filename_or_data, mode, &exinfo, &stream); - if (result == FMOD_OK) - { - Printf("%s is an unsupported format.\n", *snd_midipatchset); - } - } - if (result == FMOD_OK) - { - SetCustomLoopPts(stream); - return new FMODStreamCapsule(stream, this, url ? filename_or_data : NULL); - } - return NULL; + name.Format("_FileReader_%p", reader); + result = Sys->createSound(name, mode, &exinfo, &stream); + if(result == FMOD_ERR_FORMAT && exinfo.dlsname != NULL) + { + // FMOD_ERR_FORMAT could refer to either the main sound file or + // to the DLS instrument set. Try again without special DLS + // instruments to see if that lets it succeed. + exinfo.dlsname = NULL; + result = Sys->createSound(name, mode, &exinfo, &stream); + if (result == FMOD_OK) + { + Printf("%s is an unsupported format.\n", *snd_midipatchset); + } + } + if(result != FMOD_OK) + return NULL; + + SetCustomLoopPts(stream); + return new FMODStreamCapsule(stream, this, reader); +} + +SoundStream *FMODSoundRenderer::OpenStream(const char *url, int flags) +{ + FMOD_MODE mode; + FMOD_CREATESOUNDEXINFO exinfo; + FMOD::Sound *stream; + FMOD_RESULT result; + FString patches; + + InitCreateSoundExInfo(&exinfo); + mode = FMOD_SOFTWARE | FMOD_2D | FMOD_CREATESTREAM; + if(flags & SoundStream::Loop) + mode |= FMOD_LOOP_NORMAL; + if((*snd_midipatchset)[0] != '\0') + { +#ifdef _WIN32 + // If the path does not contain any path separators, automatically + // prepend $PROGDIR to the path. + if (strcspn(snd_midipatchset, ":/\\") == strlen(snd_midipatchset)) + { + patches << "$PROGDIR/" << snd_midipatchset; + patches = NicePath(patches); + } + else +#endif + { + patches = NicePath(snd_midipatchset); + } + exinfo.dlsname = patches; + } + + // Use a larger buffer for URLs so that it's less likely to be effected + // by hiccups in the data rate from the remote server. + Sys->setStreamBufferSize(64*1024, FMOD_TIMEUNIT_RAWBYTES); + + result = Sys->createSound(url, mode, &exinfo, &stream); + if(result == FMOD_ERR_FORMAT && exinfo.dlsname != NULL) + { + exinfo.dlsname = NULL; + result = Sys->createSound(url, mode, &exinfo, &stream); + if(result == FMOD_OK) + { + Printf("%s is an unsupported format.\n", *snd_midipatchset); + } + } + + // Restore standard buffer size. + Sys->setStreamBufferSize(16*1024, FMOD_TIMEUNIT_RAWBYTES); + + if(result != FMOD_OK) + return NULL; + + SetCustomLoopPts(stream); + return new FMODStreamCapsule(stream, this, url); } //========================================================================== @@ -3098,3 +3173,45 @@ FMOD_RESULT FMODSoundRenderer::SetSystemReverbProperties(const REVERB_PROPERTIES #endif } +#endif // NO_FMOD + + +//========================================================================== +// +// IsFModExPresent +// +// Check if FMod can be used +// +//========================================================================== + +bool IsFModExPresent() +{ +#ifdef NO_FMOD + return false; +#elif !defined _WIN32 + return true; // on non-Windows we cannot delay load the library so it has to be present. +#else + static bool cached_result; + static bool done = false; + + if (!done) + { + done = true; + + FMOD::System *Sys; + FMOD_RESULT result; + __try + { + result = FMOD::System_Create(&Sys); + } + __except (CheckException(GetExceptionCode())) + { + // FMod could not be delay loaded + return false; + } + if (result == FMOD_OK) Sys->release(); + cached_result = true; + } + return cached_result; +#endif +} diff --git a/src/sound/fmodsound.h b/src/sound/fmodsound.h index 93b70ce872..8963f7d3b7 100644 --- a/src/sound/fmodsound.h +++ b/src/sound/fmodsound.h @@ -2,6 +2,8 @@ #define FMODSOUND_H #include "i_sound.h" + +#ifndef NO_FMOD #include "fmod_wrap.h" class FMODSoundRenderer : public SoundRenderer @@ -22,9 +24,8 @@ public: // Streaming sounds. SoundStream *CreateStream (SoundStreamCallback callback, int buffsamples, int flags, int samplerate, void *userdata); - SoundStream *OpenStream (const char *filename, int flags, int offset, int length); - long PlayStream (SoundStream *stream, int volume); - void StopStream (SoundStream *stream); + SoundStream *OpenStream (FileReader *reader, int flags); + SoundStream *OpenStream (const char *url, int flags); // Starts a sound. FISoundChannel *StartSound (SoundHandle sfx, float vol, int pitch, int chanflags, FISoundChannel *reuse_chan); @@ -126,3 +127,4 @@ private: }; #endif +#endif diff --git a/src/sound/i_music.cpp b/src/sound/i_music.cpp index 84d1d81014..3f8fac0da6 100644 --- a/src/sound/i_music.cpp +++ b/src/sound/i_music.cpp @@ -99,7 +99,7 @@ EXTERN_CVAR (Int, snd_mididevice) static bool MusicDown = true; -static BYTE *ungzip(BYTE *data, int *size); +static bool ungzip(BYTE *data, int size, TArray &newdata); MusInfo *currSong; int nomusic = 0; @@ -301,21 +301,21 @@ MusInfo *MusInfo::GetWaveDumper(const char *filename, int rate) // //========================================================================== -static MIDIStreamer *CreateMIDIStreamer(FILE *file, BYTE *musiccache, int len, EMidiDevice devtype, EMIDIType miditype) +static MIDIStreamer *CreateMIDIStreamer(FileReader &reader, EMidiDevice devtype, EMIDIType miditype) { switch (miditype) { case MIDI_MUS: - return new MUSSong2(file, musiccache, len, devtype); + return new MUSSong2(reader, devtype); case MIDI_MIDI: - return new MIDISong2(file, musiccache, len, devtype); + return new MIDISong2(reader, devtype); case MIDI_HMI: - return new HMISong(file, musiccache, len, devtype); + return new HMISong(reader, devtype); case MIDI_XMI: - return new XMISong(file, musiccache, len, devtype); + return new XMISong(reader, devtype); default: return NULL; @@ -377,100 +377,61 @@ static EMIDIType IdentifyMIDIType(DWORD *id, int size) // //========================================================================== -MusInfo *I_RegisterSong (const char *filename, BYTE *musiccache, int offset, int len, int device) +MusInfo *I_RegisterSong (FileReader *reader, int device) { - FILE *file; MusInfo *info = NULL; const char *fmt; DWORD id[32/4]; - BYTE *ungzipped; - int i; if (nomusic) { + delete reader; return 0; } - if (offset != -1) + if(reader->Read(id, 32) != 32 || reader->Seek(-32, SEEK_CUR) != 0) { - file = fopen (filename, "rb"); - if (file == NULL) - { - return 0; - } - - if (len == 0 && offset == 0) - { - fseek (file, 0, SEEK_END); - len = ftell (file); - fseek (file, 0, SEEK_SET); - } - else - { - fseek (file, offset, SEEK_SET); - } - if (len < 32) - { - return 0; - } - if (fread (id, 4, 32/4, file) != 32/4) - { - fclose (file); - return 0; - } - fseek (file, -32, SEEK_CUR); - } - else - { - file = NULL; - if (len < 32) - { - return 0; - } - for (i = 0; i < 32/4; ++i) - { - id[i] = ((DWORD *)musiccache)[i]; - } + delete reader; + return 0; } #ifndef _WIN32 - // non-Windows platforms don't support MDEV_MMAPI so map to MDEV_FMOD + // non-Windows platforms don't support MDEV_MMAPI so map to MDEV_SNDSYS if (device == MDEV_MMAPI) - device = MDEV_FMOD; + device = MDEV_SNDSYS; #endif - // Check for gzip compression. Some formats are expected to have players - // that can handle it, so it simplifies things if we make all songs - // gzippable. - ungzipped = NULL; - if ((id[0] & MAKE_ID(255,255,255,0)) == GZIP_ID) + // Check for gzip compression. Some formats are expected to have players + // that can handle it, so it simplifies things if we make all songs + // gzippable. + if ((id[0] & MAKE_ID(255, 255, 255, 0)) == GZIP_ID) { - if (offset != -1) + int len = reader->GetLength(); + BYTE *gzipped = new BYTE[len]; + if (reader->Read(gzipped, len) != len) { - BYTE *gzipped = new BYTE[len]; - if (fread(gzipped, 1, len, file) != (size_t)len) - { - delete[] gzipped; - fclose(file); - return NULL; - } - ungzipped = ungzip(gzipped, &len); delete[] gzipped; - } - else - { - ungzipped = ungzip(musiccache, &len); - } - if (ungzipped == NULL) - { - fclose(file); + delete reader; return NULL; } - musiccache = ungzipped; - for (i = 0; i < 32/4; ++i) + delete reader; + + MemoryArrayReader *memreader = new MemoryArrayReader(NULL, 0); + if (!ungzip(gzipped, len, memreader->GetArray())) { - id[i] = ((DWORD *)musiccache)[i]; + delete[] gzipped; + delete memreader; + return 0; } + delete[] gzipped; + memreader->UpdateLength(); + + if (memreader->Read(id, 32) != 32 || memreader->Seek(-32, SEEK_CUR) != 0) + { + delete memreader; + return 0; + } + reader = memreader; } EMIDIType miditype = IdentifyMIDIType(id, sizeof(id)); @@ -478,22 +439,22 @@ MusInfo *I_RegisterSong (const char *filename, BYTE *musiccache, int offset, int { EMidiDevice devtype = (EMidiDevice)device; -retry_as_fmod: - info = CreateMIDIStreamer(file, musiccache, len, devtype, miditype); +retry_as_sndsys: + info = CreateMIDIStreamer(*reader, devtype, miditype); if (info != NULL && !info->IsValid()) { delete info; info = NULL; } - if (info == NULL && devtype != MDEV_FMOD && snd_mididevice < 0) + if (info == NULL && devtype != MDEV_SNDSYS && snd_mididevice < 0) { - devtype = MDEV_FMOD; - goto retry_as_fmod; + devtype = MDEV_SNDSYS; + goto retry_as_sndsys; } #ifdef _WIN32 if (info == NULL && devtype != MDEV_MMAPI && snd_mididevice >= 0) { - info = CreateMIDIStreamer(file, musiccache, len, MDEV_MMAPI, miditype); + info = CreateMIDIStreamer(*reader, MDEV_MMAPI, miditype); } #endif } @@ -504,74 +465,62 @@ retry_as_fmod: (id[0] == MAKE_ID('D','B','R','A') && id[1] == MAKE_ID('W','O','P','L')) || // DosBox Raw OPL (id[0] == MAKE_ID('A','D','L','I') && *((BYTE *)id + 4) == 'B')) // Martin Fernandez's modified IMF { - info = new OPLMUSSong (file, musiccache, len); + info = new OPLMUSSong (*reader); } // Check for game music else if ((fmt = GME_CheckFormat(id[0])) != NULL && fmt[0] != '\0') { - info = GME_OpenSong(file, musiccache, len, fmt); + info = GME_OpenSong(*reader, fmt); } // Check for module formats else { - info = MOD_OpenSong(file, musiccache, len); + info = MOD_OpenSong(*reader); } - if (info == NULL) - { - // Check for CDDA "format" - if (id[0] == (('R')|(('I')<<8)|(('F')<<16)|(('F')<<24))) - { - if (file != NULL) - { - DWORD subid; + if (info == NULL) + { + // Check for CDDA "format" + if (id[0] == (('R')|(('I')<<8)|(('F')<<16)|(('F')<<24))) + { + DWORD subid; - fseek (file, 8, SEEK_CUR); - if (fread (&subid, 4, 1, file) != 1) - { - fclose (file); - return 0; - } - fseek (file, -12, SEEK_CUR); + reader->Seek(8, SEEK_CUR); + if (reader->Read (&subid, 4) != 4) + { + delete reader; + return 0; + } + reader->Seek(-12, SEEK_CUR); - if (subid == (('C')|(('D')<<8)|(('D')<<16)|(('A')<<24))) - { - // This is a CDDA file - info = new CDDAFile (file, len); - } - } - } + if (subid == (('C')|(('D')<<8)|(('D')<<16)|(('A')<<24))) + { + // This is a CDDA file + info = new CDDAFile (*reader); + } + } - // no FMOD => no modules/streams - // 1024 bytes is an arbitrary restriction. It's assumed that anything - // smaller than this can't possibly be a valid music file if it hasn't - // been identified already, so don't even bother trying to load it. - // Of course MIDIs shorter than 1024 bytes should pass. - if (info == NULL && (len >= 1024 || id[0] == MAKE_ID('M','T','h','d'))) - { - // Let FMOD figure out what it is. - if (file != NULL) - { - fclose (file); - file = NULL; - } - info = new StreamSong (offset >= 0 ? filename : (const char *)musiccache, offset, len); - } - } + // no support in sound system => no modules/streams + // 1024 bytes is an arbitrary restriction. It's assumed that anything + // smaller than this can't possibly be a valid music file if it hasn't + // been identified already, so don't even bother trying to load it. + // Of course MIDIs shorter than 1024 bytes should pass. + if (info == NULL && (reader->GetLength() >= 1024 || id[0] == MAKE_ID('M','T','h','d'))) + { + // Let the sound system figure out what it is. + info = new StreamSong (reader); + // Assumed ownership + reader = NULL; + } + } + + if (reader != NULL) delete reader; if (info && !info->IsValid ()) { delete info; info = NULL; } - if (file != NULL) - { - fclose (file); - } - if (ungzipped != NULL) - { - delete[] ungzipped; - } return info; } @@ -605,7 +554,7 @@ MusInfo *I_RegisterURLSong (const char *url) { StreamSong *song; - song = new StreamSong(url, 0, 0); + song = new StreamSong(url); if (song->IsValid()) { return song; @@ -623,13 +572,12 @@ MusInfo *I_RegisterURLSong (const char *url) // //========================================================================== -BYTE *ungzip(BYTE *data, int *complen) +static bool ungzip(BYTE *data, int complen, TArray &newdata) { - const BYTE *max = data + *complen - 8; + const BYTE *max = data + complen - 8; const BYTE *compstart = data + 10; BYTE flags = data[3]; unsigned isize; - BYTE *newdata; z_stream stream; int err; @@ -658,16 +606,16 @@ BYTE *ungzip(BYTE *data, int *complen) } if (compstart >= max - 1) { - return NULL; + return false; } // Decompress - isize = LittleLong(*(DWORD *)(data + *complen - 4)); - newdata = new BYTE[isize]; + isize = LittleLong(*(DWORD *)(data + complen - 4)); + newdata.Resize(isize); stream.next_in = (Bytef *)compstart; stream.avail_in = (uInt)(max - compstart); - stream.next_out = newdata; + stream.next_out = &newdata[0]; stream.avail_out = isize; stream.zalloc = (alloc_func)0; stream.zfree = (free_func)0; @@ -675,25 +623,20 @@ BYTE *ungzip(BYTE *data, int *complen) err = inflateInit2(&stream, -MAX_WBITS); if (err != Z_OK) { - delete[] newdata; - return NULL; + return false; } - err = inflate(&stream, Z_FINISH); if (err != Z_STREAM_END) { inflateEnd(&stream); - delete[] newdata; - return NULL; + return false; } err = inflateEnd(&stream); if (err != Z_OK) { - delete[] newdata; - return NULL; + return false; } - *complen = isize; - return newdata; + return true; } //========================================================================== diff --git a/src/sound/i_music.h b/src/sound/i_music.h index d587d31290..f6a6dbadd8 100644 --- a/src/sound/i_music.h +++ b/src/sound/i_music.h @@ -52,7 +52,7 @@ void I_SetMusicVolume (float volume); // Registers a song handle to song data. class MusInfo; -MusInfo *I_RegisterSong (const char *file, BYTE *musiccache, int offset, int length, int device); +MusInfo *I_RegisterSong (FileReader *reader, int device); MusInfo *I_RegisterCDSong (int track, int cdid = 0); MusInfo *I_RegisterURLSong (const char *url); diff --git a/src/sound/i_musicinterns.h b/src/sound/i_musicinterns.h index 2133e07c63..77aa313122 100644 --- a/src/sound/i_musicinterns.h +++ b/src/sound/i_musicinterns.h @@ -23,6 +23,7 @@ #include "i_sound.h" #include "i_music.h" #include "s_sound.h" +#include "files.h" void I_InitMusicWin32 (); void I_ShutdownMusicWin32 (); @@ -168,16 +169,16 @@ protected: bool bLooping; }; -// FMOD pseudo-MIDI device -------------------------------------------------- +// Sound System pseudo-MIDI device ------------------------------------------ -class FMODMIDIDevice : public PseudoMIDIDevice +class SndSysMIDIDevice : public PseudoMIDIDevice { public: int Open(void (*callback)(unsigned int, void *, DWORD, DWORD), void *userdata); bool Preprocess(MIDIStreamer *song, bool looping); }; -// MIDI file played with TiMidity++ and possibly streamed through FMOD ------ +// MIDI file played with TiMidity++ and possibly streamed through the Sound System class TimidityPPMIDIDevice : public PseudoMIDIDevice { @@ -500,7 +501,7 @@ protected: class MUSSong2 : public MIDIStreamer { public: - MUSSong2(FILE *file, BYTE *musiccache, int length, EMidiDevice type); + MUSSong2(FileReader &reader, EMidiDevice type); ~MUSSong2(); MusInfo *GetOPLDumper(const char *filename); @@ -526,7 +527,7 @@ protected: class MIDISong2 : public MIDIStreamer { public: - MIDISong2(FILE *file, BYTE *musiccache, int length, EMidiDevice type); + MIDISong2(FileReader &reader, EMidiDevice type); ~MIDISong2(); MusInfo *GetOPLDumper(const char *filename); @@ -583,7 +584,7 @@ protected: class HMISong : public MIDIStreamer { public: - HMISong(FILE *file, BYTE *musiccache, int length, EMidiDevice type); + HMISong(FileReader &reader, EMidiDevice type); ~HMISong(); MusInfo *GetOPLDumper(const char *filename); @@ -626,7 +627,7 @@ protected: class XMISong : public MIDIStreamer { public: - XMISong(FILE *file, BYTE *musiccache, int length, EMidiDevice type); + XMISong(FileReader &reader, EMidiDevice type); ~XMISong(); MusInfo *GetOPLDumper(const char *filename); @@ -660,12 +661,13 @@ protected: EventSource EventDue; }; -// Anything supported by FMOD out of the box -------------------------------- +// Anything supported by the sound system out of the box -------------------- class StreamSong : public MusInfo { public: - StreamSong (const char *file, int offset, int length); + StreamSong (FileReader *reader); + StreamSong (const char *url); ~StreamSong (); void Play (bool looping, int subsong); void Pause (); @@ -683,12 +685,12 @@ protected: SoundStream *m_Stream; }; -// MUS file played by a software OPL2 synth and streamed through FMOD ------- +// MUS file played by a software OPL2 synth and streamed through the sound system class OPLMUSSong : public StreamSong { public: - OPLMUSSong (FILE *file, BYTE *musiccache, int length); + OPLMUSSong (FileReader &reader); ~OPLMUSSong (); void Play (bool looping, int subsong); bool IsPlaying (); @@ -737,17 +739,17 @@ protected: class CDDAFile : public CDSong { public: - CDDAFile (FILE *file, int length); + CDDAFile (FileReader &reader); }; // Module played via foo_dumb ----------------------------------------------- -MusInfo *MOD_OpenSong(FILE *file, BYTE *musiccache, int len); +MusInfo *MOD_OpenSong(FileReader &reader); // Music played via Game Music Emu ------------------------------------------ const char *GME_CheckFormat(uint32 header); -MusInfo *GME_OpenSong(FILE *file, BYTE *musiccache, int len, const char *fmt); +MusInfo *GME_OpenSong(FileReader &reader, const char *fmt); // -------------------------------------------------------------------------- diff --git a/src/sound/i_sound.cpp b/src/sound/i_sound.cpp index 07cd43a4b6..78c4526e86 100644 --- a/src/sound/i_sound.cpp +++ b/src/sound/i_sound.cpp @@ -52,7 +52,12 @@ extern HINSTANCE g_hInst; #include "doomtype.h" #include +#include "except.h" #include "fmodsound.h" +#include "oalsound.h" + +#include "mpg123_decoder.h" +#include "sndfile_decoder.h" #include "m_swap.h" #include "stats.h" @@ -77,6 +82,16 @@ CVAR (Int, snd_samplerate, 0, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) CVAR (Int, snd_buffersize, 0, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) CVAR (String, snd_output, "default", CVAR_ARCHIVE|CVAR_GLOBALCONFIG) +#ifndef NO_FMOD +#define DEF_BACKEND "fmod" +#elif !defined(NO_OPENAL) +#define DEF_BACKEND "openal" +#else +#define DEF_BACKEND "null" +#endif + +CVAR(String, snd_backend, DEF_BACKEND, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) + // killough 2/21/98: optionally use varying pitched sounds CVAR (Bool, snd_pitched, false, CVAR_ARCHIVE) @@ -159,8 +174,9 @@ public: { return NULL; } - SoundStream *OpenStream (const char *filename, int flags, int offset, int length) + SoundStream *OpenStream (FileReader *reader, int flags) { + delete reader; return NULL; } @@ -242,6 +258,7 @@ void I_InitSound () nosound = !!Args->CheckParm ("-nosound"); nosfx = !!Args->CheckParm ("-nosfx"); + GSnd = NULL; if (nosound) { GSnd = new NullSoundRenderer; @@ -249,9 +266,51 @@ void I_InitSound () return; } - GSnd = new FMODSoundRenderer; - - if (!GSnd->IsValid ()) + // This has been extended to allow falling back from FMod to OpenAL and vice versa if the currently active sound system cannot be found. + if (stricmp(snd_backend, "null") == 0) + { + GSnd = new NullSoundRenderer; + } + else if(stricmp(snd_backend, "fmod") == 0) + { + #ifndef NO_FMOD + if (IsFModExPresent()) + { + GSnd = new FMODSoundRenderer; + } + #endif + #ifndef NO_OPENAL + if ((!GSnd || !GSnd->IsValid()) && IsOpenALPresent()) + { + Printf (TEXTCOLOR_RED"FMod Ex Sound init failed. Trying OpenAL.\n"); + GSnd = new OpenALSoundRenderer; + snd_backend = "openal"; + } + #endif + } + else if(stricmp(snd_backend, "openal") == 0) + { + #ifndef NO_OPENAL + if (IsOpenALPresent()) + { + GSnd = new OpenALSoundRenderer; + } + #endif + #ifndef NO_FMOD + if ((!GSnd || !GSnd->IsValid()) && IsFModExPresent()) + { + Printf (TEXTCOLOR_RED"OpenAL Sound init failed. Trying FMod Ex.\n"); + GSnd = new FMODSoundRenderer; + snd_backend = "fmod"; + } + #endif + } + else + { + Printf (TEXTCOLOR_RED"%s: Unknown sound system specified\n", *snd_backend); + snd_backend = "null"; + } + if (!GSnd || !GSnd->IsValid ()) { I_CloseSound(); GSnd = new NullSoundRenderer; @@ -283,6 +342,27 @@ void I_ShutdownSound() } } +const char *GetSampleTypeName(enum SampleType type) +{ + switch(type) + { + case SampleType_UInt8: return "Unsigned 8-bit"; + case SampleType_Int16: return "Signed 16-bit"; + } + return "(invalid sample type)"; +} + +const char *GetChannelConfigName(enum ChannelConfig chan) +{ + switch(chan) + { + case ChannelConfig_Mono: return "Mono"; + case ChannelConfig_Stereo: return "Stereo"; + } + return "(invalid channel config)"; +} + + CCMD (snd_status) { GSnd->PrintStatus (); @@ -321,9 +401,28 @@ FString SoundRenderer::GatherStats () return "No stats for this sound renderer."; } -short *SoundRenderer::DecodeSample(int outlen, const void *coded, int sizebytes, ECodecType type) +short *SoundRenderer::DecodeSample(int outlen, const void *coded, int sizebytes, ECodecType ctype) { - return NULL; + MemoryReader reader((const char*)coded, sizebytes); + short *samples = (short*)calloc(1, outlen); + ChannelConfig chans; + SampleType type; + int srate; + + SoundDecoder *decoder = CreateDecoder(&reader); + if(!decoder) return samples; + + decoder->getInfo(&srate, &chans, &type); + if(chans != ChannelConfig_Mono || type != SampleType_Int16) + { + DPrintf("Sample is not 16-bit mono\n"); + delete decoder; + return samples; + } + + decoder->read((char*)samples, outlen); + delete decoder; + return samples; } void SoundRenderer::DrawWaveDebug(int mode) @@ -504,3 +603,51 @@ SoundHandle SoundRenderer::LoadSoundVoc(BYTE *sfxdata, int length) return retval; } +SoundStream *SoundRenderer::OpenStream(const char *url, int flags) +{ + return 0; +} + +SoundDecoder *SoundRenderer::CreateDecoder(FileReader *reader) +{ + SoundDecoder *decoder = NULL; + int pos = reader->Tell(); + +#ifdef HAVE_MPG123 + decoder = new MPG123Decoder; + if (decoder->open(reader)) + return decoder; + reader->Seek(pos, SEEK_SET); + + delete decoder; + decoder = NULL; +#endif +#ifdef HAVE_SNDFILE + decoder = new SndFileDecoder; + if (decoder->open(reader)) + return decoder; + reader->Seek(pos, SEEK_SET); + + delete decoder; + decoder = NULL; +#endif + return decoder; +} + + +// Default readAll implementation, for decoders that can't do anything better +TArray SoundDecoder::readAll() +{ + TArray output; + unsigned total = 0; + unsigned got; + + output.Resize(total+32768); + while((got=(unsigned)read(&output[total], output.Size()-total)) > 0) + { + total += got; + output.Resize(total*2); + } + output.Resize(total); + return output; +} diff --git a/src/sound/i_sound.h b/src/sound/i_sound.h index adce8796b5..153fe38634 100644 --- a/src/sound/i_sound.h +++ b/src/sound/i_sound.h @@ -38,6 +38,8 @@ #include "doomtype.h" #include "i_soundinternal.h" +class FileReader; + enum ECodecType { CODEC_Unknown, @@ -82,6 +84,8 @@ public: typedef bool (*SoundStreamCallback)(SoundStream *stream, void *buff, int len, void *userdata); +struct SoundDecoder; + class SoundRenderer { public: @@ -101,7 +105,8 @@ public: // Streaming sounds. virtual SoundStream *CreateStream (SoundStreamCallback callback, int buffbytes, int flags, int samplerate, void *userdata) = 0; - virtual SoundStream *OpenStream (const char *filename, int flags, int offset, int length) = 0; + virtual SoundStream *OpenStream (FileReader *reader, int flags) = 0; + virtual SoundStream *OpenStream (const char *url, int flags); // Starts a sound. virtual FISoundChannel *StartSound (SoundHandle sfx, float vol, int pitch, int chanflags, FISoundChannel *reuse_chan) = 0; @@ -142,6 +147,7 @@ public: virtual void UpdateListener (SoundListener *) = 0; virtual void UpdateSounds () = 0; + virtual void UpdateMusic() {} virtual bool IsValid () = 0; virtual void PrintStatus () = 0; @@ -150,6 +156,9 @@ public: virtual short *DecodeSample(int outlen, const void *coded, int sizebytes, ECodecType type); virtual void DrawWaveDebug(int mode); + +protected: + virtual SoundDecoder *CreateDecoder(FileReader *reader); }; extern SoundRenderer *GSnd; @@ -166,4 +175,7 @@ FISoundChannel *S_GetChannel(void *syschan); extern ReverbContainer *DefaultEnvironments[26]; +bool IsFModExPresent(); +bool IsOpenALPresent(); + #endif diff --git a/src/sound/i_soundinternal.h b/src/sound/i_soundinternal.h index 157c9c87bc..60acc43c07 100644 --- a/src/sound/i_soundinternal.h +++ b/src/sound/i_soundinternal.h @@ -1,7 +1,13 @@ #ifndef __SNDINT_H #define __SNDINT_H +#include + #include "basictypes.h" +#include "vectors.h" +#include "tarray.h" + +class FileReader; // For convenience, this structure matches FMOD_REVERB_PROPERTIES. // Since I can't very well #include system-specific stuff in the @@ -96,8 +102,46 @@ struct FISoundChannel // callback that can't be passed a sound channel pointer FRolloffInfo Rolloff; float DistanceScale; + float DistanceSqr; + bool ManualRolloff; }; +enum SampleType +{ + SampleType_UInt8, + SampleType_Int16 +}; +enum ChannelConfig +{ + ChannelConfig_Mono, + ChannelConfig_Stereo +}; + +const char *GetSampleTypeName(enum SampleType type); +const char *GetChannelConfigName(enum ChannelConfig chan); + +struct SoundDecoder +{ + virtual void getInfo(int *samplerate, ChannelConfig *chans, SampleType *type) = 0; + + virtual size_t read(char *buffer, size_t bytes) = 0; + virtual TArray readAll(); + virtual bool seek(size_t ms_offset) = 0; + virtual size_t getSampleOffset() = 0; + virtual size_t getSampleLength() { return 0; } + + SoundDecoder() { } + virtual ~SoundDecoder() { } + +protected: + virtual bool open(FileReader *reader) = 0; + friend class SoundRenderer; + +private: + // Make non-copyable + SoundDecoder(const SoundDecoder &rhs); + SoundDecoder& operator=(const SoundDecoder &rhs); +}; #endif diff --git a/src/sound/mpg123_decoder.cpp b/src/sound/mpg123_decoder.cpp new file mode 100644 index 0000000000..551fe84346 --- /dev/null +++ b/src/sound/mpg123_decoder.cpp @@ -0,0 +1,206 @@ +#ifdef _WIN32 +#define WIN32_LEAN_AND_MEAN +#include +#define USE_WINDOWS_DWORD +#endif +#include "except.h" + +#include "mpg123_decoder.h" +#include "files.h" + +#ifdef HAVE_MPG123 +static bool inited = false; + + +off_t MPG123Decoder::file_lseek(void *handle, off_t offset, int whence) +{ + MPG123Decoder *self = reinterpret_cast(handle); + FileReader *reader = self->Reader; + + if(whence == SEEK_SET) + offset += self->StartOffset; + else if(whence == SEEK_CUR) + { + if(offset < 0 && reader->Tell()+offset < self->StartOffset) + return -1; + } + else if(whence == SEEK_END) + { + if(offset < 0 && reader->GetLength()+offset < self->StartOffset) + return -1; + } + + if(reader->Seek(offset, whence) != 0) + return -1; + return reader->Tell() - self->StartOffset; +} + +ssize_t MPG123Decoder::file_read(void *handle, void *buffer, size_t bytes) +{ + FileReader *reader = reinterpret_cast(handle)->Reader; + return reader->Read(buffer, bytes); +} + + +MPG123Decoder::~MPG123Decoder() +{ + if(MPG123) + { + mpg123_close(MPG123); + mpg123_delete(MPG123); + MPG123 = 0; + } +} + +bool MPG123Decoder::open(FileReader *reader) +{ + if(!inited) + { + __try + { + if(mpg123_init() != MPG123_OK) + return false; + inited = true; + } + __except (CheckException(GetExceptionCode())) + { + // this means that the delay loaded decoder DLL was not found. + return false; + } + } + + Reader = reader; + StartOffset = 0; + + char data[10]; + if(file_read(this, data, 10) != 10) + return false; + + int start_offset = 0; + // Check for ID3 tags and skip them + if(memcmp(data, "ID3", 3) == 0 && + (BYTE)data[3] <= 4 && (BYTE)data[4] != 0xff && + (data[5]&0x0f) == 0 && (data[6]&0x80) == 0 && + (data[7]&0x80) == 0 && (data[8]&0x80) == 0 && + (data[9]&0x80) == 0) + { + // ID3v2 + start_offset = (data[6]<<21) | (data[7]<<14) | + (data[8]<< 7) | (data[9] ); + start_offset += ((data[5]&0x10) ? 20 : 10); + } + + StartOffset = start_offset; + if(file_lseek(this, 0, SEEK_SET) != 0) + return false; + + // Check for a frame header + bool frame_ok = false; + if(file_read(this, data, 3) == 3) + { + if((BYTE)data[0] == 0xff && + ((data[1]&0xfe) == 0xfa/*MPEG-1*/ || (data[1]&0xfe) == 0xf2/*MPEG-2*/)) + { + int brate_idx = (data[2]>>4) & 0x0f; + int srate_idx = (data[2]>>2) & 0x03; + if(brate_idx != 0 && brate_idx != 15 && srate_idx != 3) + frame_ok = (file_lseek(this, 0, SEEK_SET) == 0); + } + } + + if(frame_ok) + { + MPG123 = mpg123_new(NULL, NULL); + if(mpg123_replace_reader_handle(MPG123, file_read, file_lseek, NULL) == MPG123_OK && + mpg123_open_handle(MPG123, this) == MPG123_OK) + { + int enc, channels; + long srate; + + if(mpg123_getformat(MPG123, &srate, &channels, &enc) == MPG123_OK) + { + if((channels == 1 || channels == 2) && srate > 0 && + mpg123_format_none(MPG123) == MPG123_OK && + mpg123_format(MPG123, srate, channels, MPG123_ENC_SIGNED_16) == MPG123_OK) + { + // All OK + Done = false; + return true; + } + } + mpg123_close(MPG123); + } + mpg123_delete(MPG123); + MPG123 = 0; + } + + return false; +} + +void MPG123Decoder::getInfo(int *samplerate, ChannelConfig *chans, SampleType *type) +{ + int enc = 0, channels = 0; + long srate = 0; + + mpg123_getformat(MPG123, &srate, &channels, &enc); + + *samplerate = srate; + + if(channels == 2) + *chans = ChannelConfig_Stereo; + else + *chans = ChannelConfig_Mono; + + *type = SampleType_Int16; +} + +size_t MPG123Decoder::read(char *buffer, size_t bytes) +{ + size_t amt = 0; + while(!Done && bytes > 0) + { + size_t got = 0; + int ret = mpg123_read(MPG123, (unsigned char*)buffer, bytes, &got); + + bytes -= got; + buffer += got; + amt += got; + + if(ret == MPG123_NEW_FORMAT || ret == MPG123_DONE || got == 0) + { + Done = true; + break; + } + } + return amt; +} + +bool MPG123Decoder::seek(size_t ms_offset) +{ + int enc, channels; + long srate; + + if(mpg123_getformat(MPG123, &srate, &channels, &enc) == MPG123_OK) + { + size_t smp_offset = (size_t)((double)ms_offset / 1000. * srate); + if(mpg123_seek(MPG123, smp_offset, SEEK_SET) >= 0) + { + Done = false; + return true; + } + } + return false; +} + +size_t MPG123Decoder::getSampleOffset() +{ + return mpg123_tell(MPG123); +} + +size_t MPG123Decoder::getSampleLength() +{ + off_t len = mpg123_length(MPG123); + return (len > 0) ? len : 0; +} + +#endif diff --git a/src/sound/mpg123_decoder.h b/src/sound/mpg123_decoder.h new file mode 100644 index 0000000000..1d5b883ead --- /dev/null +++ b/src/sound/mpg123_decoder.h @@ -0,0 +1,44 @@ +#ifndef MPG123_DECODER_H +#define MPG123_DECODER_H + +#include "i_soundinternal.h" + +#ifdef HAVE_MPG123 + +#ifdef _MSC_VER +typedef int ssize_t; +#endif +#include "mpg123.h" + +struct MPG123Decoder : public SoundDecoder +{ + virtual void getInfo(int *samplerate, ChannelConfig *chans, SampleType *type); + + virtual size_t read(char *buffer, size_t bytes); + virtual bool seek(size_t ms_offset); + virtual size_t getSampleOffset(); + virtual size_t getSampleLength(); + + MPG123Decoder() : MPG123(0) { } + virtual ~MPG123Decoder(); + +protected: + virtual bool open(FileReader *reader); + +private: + mpg123_handle *MPG123; + bool Done; + + FileReader *Reader; + int StartOffset; + static off_t file_lseek(void *handle, off_t offset, int whence); + static ssize_t file_read(void *handle, void *buffer, size_t bytes); + + // Make non-copyable + MPG123Decoder(const MPG123Decoder &rhs); + MPG123Decoder& operator=(const MPG123Decoder &rhs); +}; + +#endif + +#endif /* MPG123_DECODER_H */ diff --git a/src/sound/music_cd.cpp b/src/sound/music_cd.cpp index 5b400ba821..1d04f53114 100644 --- a/src/sound/music_cd.cpp +++ b/src/sound/music_cd.cpp @@ -1,5 +1,6 @@ #include "i_musicinterns.h" #include "i_cd.h" +#include "files.h" void CDSong::Play (bool looping, int subsong) { @@ -78,31 +79,31 @@ bool CDSong::IsPlaying () return m_Status != STATE_Stopped; } -CDDAFile::CDDAFile (FILE *file, int length) +CDDAFile::CDDAFile (FileReader &reader) : CDSong () { DWORD chunk; WORD track; DWORD discid; - long endpos = ftell (file) + length - 8; + long endpos = reader.Tell() + reader.GetLength() - 8; // I_RegisterSong already identified this as a CDDA file, so we // just need to check the contents we're interested in. - fseek (file, 12, SEEK_CUR); + reader.Seek(12, SEEK_CUR); - while (ftell (file) < endpos) + while (reader.Tell() < endpos) { - fread (&chunk, 4, 1, file); + reader.Read(&chunk, 4); if (chunk != (('f')|(('m')<<8)|(('t')<<16)|((' ')<<24))) { - fread (&chunk, 4, 1, file); - fseek (file, chunk, SEEK_CUR); + reader.Read(&chunk, 4); + reader.Seek(chunk, SEEK_CUR); } else { - fseek (file, 6, SEEK_CUR); - fread (&track, 2, 1, file); - fread (&discid, 4, 1, file); + reader.Seek(6, SEEK_CUR); + reader.Read(&track, 2); + reader.Read(&discid, 4); if (CD_InitID (discid) && CD_CheckTrack (track)) { diff --git a/src/sound/music_dumb.cpp b/src/sound/music_dumb.cpp index 33a85caf3d..8b196e7cda 100644 --- a/src/sound/music_dumb.cpp +++ b/src/sound/music_dumb.cpp @@ -30,6 +30,7 @@ #include "c_cvars.h" #include "i_sound.h" #include "i_system.h" +#include "files.h" #undef CDECL // w32api's windef.h defines this #include "../dumb/include/dumb.h" @@ -531,31 +532,24 @@ static DUMBFILE_SYSTEM mem_dfs = { // //========================================================================== -DUMBFILE *dumb_read_allfile(dumbfile_mem_status *filestate, BYTE *start, FILE *file, BYTE *musiccache, int lenhave, int lenfull) +DUMBFILE *dumb_read_allfile(dumbfile_mem_status *filestate, BYTE *start, FileReader &reader, int lenhave, int lenfull) { filestate->size = lenfull; filestate->offset = 0; if (lenhave >= lenfull) - { filestate->ptr = (BYTE *)start; - return dumbfile_open_ex(filestate, &mem_dfs); - } - if (musiccache != NULL) - { - filestate->ptr = (BYTE *)musiccache; - } - else - { - BYTE *mem = new BYTE[lenfull]; - memcpy(mem, start, lenhave); - if (fread(mem + lenhave, 1, lenfull - lenhave, file) != (size_t)(lenfull - lenhave)) - { - delete[] mem; - return NULL; - } - filestate->ptr = mem; - } - return dumbfile_open_ex(filestate, &mem_dfs); + else + { + BYTE *mem = new BYTE[lenfull]; + memcpy(mem, start, lenhave); + if (reader.Read(mem + lenhave, lenfull - lenhave) != (lenfull - lenhave)) + { + delete[] mem; + return NULL; + } + filestate->ptr = mem; + } + return dumbfile_open_ex(filestate, &mem_dfs); } //========================================================================== @@ -753,7 +747,7 @@ static void MOD_SetAutoChip(DUH *duh) // //========================================================================== -MusInfo *MOD_OpenSong(FILE *file, BYTE *musiccache, int size) +MusInfo *MOD_OpenSong(FileReader &reader) { DUH *duh = 0; int headsize; @@ -777,40 +771,36 @@ MusInfo *MOD_OpenSong(FILE *file, BYTE *musiccache, int size) atterm(dumb_exit); + int size = reader.GetLength(); + fpos = reader.Tell(); + filestate.ptr = start; filestate.offset = 0; headsize = MIN((int)sizeof(start), size); - if (musiccache != NULL) - { - memcpy(start, musiccache, headsize); - } - else - { - fpos = ftell(file); - if ((size_t)headsize != fread(start, 1, headsize, file)) - { - return NULL; - } - } + + if (headsize != reader.Read(start, headsize)) + { + return NULL; + } if (size >= 4 && dstart[0] == MAKE_ID('I','M','P','M')) { is_it = true; - if ((f = dumb_read_allfile(&filestate, start, file, musiccache, headsize, size))) + if ((f = dumb_read_allfile(&filestate, start, reader, headsize, size))) { duh = dumb_read_it_quick(f); } } else if (size >= 17 && !memcmp(start, "Extended Module: ", 17)) { - if ((f = dumb_read_allfile(&filestate, start, file, musiccache, headsize, size))) + if ((f = dumb_read_allfile(&filestate, start, reader, headsize, size))) { duh = dumb_read_xm_quick(f); } } else if (size >= 0x30 && dstart[11] == MAKE_ID('S','C','R','M')) { - if ((f = dumb_read_allfile(&filestate, start, file, musiccache, headsize, size))) + if ((f = dumb_read_allfile(&filestate, start, reader, headsize, size))) { duh = dumb_read_s3m_quick(f); } @@ -821,7 +811,7 @@ MusInfo *MOD_OpenSong(FILE *file, BYTE *musiccache, int size) !memcmp( &start[20], "BMOD2STM", 8 ) || !memcmp( &start[20], "WUZAMOD!", 8 ) ) ) { - if ((f = dumb_read_allfile(&filestate, start, file, musiccache, headsize, size))) + if ((f = dumb_read_allfile(&filestate, start, reader, headsize, size))) { duh = dumb_read_stm_quick(f); } @@ -830,21 +820,21 @@ MusInfo *MOD_OpenSong(FILE *file, BYTE *musiccache, int size) ((start[0] == 0x69 && start[1] == 0x66) || (start[0] == 0x4A && start[1] == 0x4E))) { - if ((f = dumb_read_allfile(&filestate, start, file, musiccache, headsize, size))) + if ((f = dumb_read_allfile(&filestate, start, reader, headsize, size))) { duh = dumb_read_669_quick(f); } } else if (size >= 0x30 && dstart[11] == MAKE_ID('P','T','M','F')) { - if ((f = dumb_read_allfile(&filestate, start, file, musiccache, headsize, size))) + if ((f = dumb_read_allfile(&filestate, start, reader, headsize, size))) { duh = dumb_read_ptm_quick(f); } } else if (size >= 4 && dstart[0] == MAKE_ID('P','S','M',' ')) { - if ((f = dumb_read_allfile(&filestate, start, file, musiccache, headsize, size))) + if ((f = dumb_read_allfile(&filestate, start, reader, headsize, size))) { duh = dumb_read_psm_quick(f, 0/*start_order*/); /*start_order = 0;*/ @@ -852,14 +842,14 @@ MusInfo *MOD_OpenSong(FILE *file, BYTE *musiccache, int size) } else if (size >= 4 && dstart[0] == (DWORD)MAKE_ID('P','S','M',254)) { - if ((f = dumb_read_allfile(&filestate, start, file, musiccache, headsize, size))) + if ((f = dumb_read_allfile(&filestate, start, reader, headsize, size))) { duh = dumb_read_old_psm_quick(f); } } else if (size >= 3 && start[0] == 'M' && start[1] == 'T' && start[2] == 'M') { - if ((f = dumb_read_allfile(&filestate, start, file, musiccache, headsize, size))) + if ((f = dumb_read_allfile(&filestate, start, reader, headsize, size))) { duh = dumb_read_mtm_quick(f); } @@ -869,7 +859,7 @@ MusInfo *MOD_OpenSong(FILE *file, BYTE *musiccache, int size) dstart[2] == MAKE_ID('A','M',' ',' ') || dstart[2] == MAKE_ID('A','M','F','F'))) { - if ((f = dumb_read_allfile(&filestate, start, file, musiccache, headsize, size))) + if ((f = dumb_read_allfile(&filestate, start, reader, headsize, size))) { duh = dumb_read_riff_quick(f); } @@ -878,7 +868,7 @@ MusInfo *MOD_OpenSong(FILE *file, BYTE *musiccache, int size) !memcmp( start, "ASYLUM Music Format", 19 ) && !memcmp( start + 19, " V1.0", 5 ) ) { - if ((f = dumb_read_allfile(&filestate, start, file, musiccache, headsize, size))) + if ((f = dumb_read_allfile(&filestate, start, reader, headsize, size))) { duh = dumb_read_asy_quick(f); } @@ -887,7 +877,7 @@ MusInfo *MOD_OpenSong(FILE *file, BYTE *musiccache, int size) dstart[0] == MAKE_ID('O','K','T','A') && dstart[1] == MAKE_ID('S','O','N','G')) { - if ((f = dumb_read_allfile(&filestate, start, file, musiccache, headsize, size))) + if ((f = dumb_read_allfile(&filestate, start, reader, headsize, size))) { duh = dumb_read_okt_quick(f); } @@ -898,12 +888,9 @@ MusInfo *MOD_OpenSong(FILE *file, BYTE *musiccache, int size) is_dos = false; if (filestate.ptr == (BYTE *)start) { - if (!(f = dumb_read_allfile(&filestate, start, file, musiccache, headsize, size))) + if (!(f = dumb_read_allfile(&filestate, start, reader, headsize, size))) { - if (file != NULL) - { - fseek(file, fpos, SEEK_SET); - } + reader.Seek(fpos, SEEK_SET); return NULL; } } @@ -914,8 +901,8 @@ MusInfo *MOD_OpenSong(FILE *file, BYTE *musiccache, int size) // No way to get the filename, so we can't check for a .mod extension, and // therefore, trying to load an old 15-instrument SoundTracker module is not // safe. We'll restrict MOD loading to 31-instrument modules with known - // signatures and let FMOD worry about 15-instrument ones. (Assuming it even - // supports them; I have not checked.) + // signatures and let the sound system worry about 15-instrument ones. + // (Assuming it even supports them) duh = dumb_read_mod_quick(f, TRUE); } @@ -944,12 +931,9 @@ MusInfo *MOD_OpenSong(FILE *file, BYTE *musiccache, int size) else { // Reposition file pointer for other codecs to do their checks. - if (file != NULL) - { - fseek(file, fpos, SEEK_SET); - } + reader.Seek(fpos, SEEK_SET); } - if (filestate.ptr != (BYTE *)start && filestate.ptr != musiccache) + if (filestate.ptr != (BYTE *)start) { delete[] const_cast(filestate.ptr); } diff --git a/src/sound/music_gme.cpp b/src/sound/music_gme.cpp index 7f017b7d2c..973092ee52 100644 --- a/src/sound/music_gme.cpp +++ b/src/sound/music_gme.cpp @@ -42,6 +42,7 @@ #include "critsec.h" #include #include "v_text.h" +#include "files.h" // MACROS ------------------------------------------------------------------ @@ -104,7 +105,7 @@ const char *GME_CheckFormat(uint32 id) // //========================================================================== -MusInfo *GME_OpenSong(FILE *file, BYTE *musiccache, int len, const char *fmt) +MusInfo *GME_OpenSong(FileReader &reader, const char *fmt) { gme_type_t type; gme_err_t err; @@ -123,29 +124,26 @@ MusInfo *GME_OpenSong(FILE *file, BYTE *musiccache, int len, const char *fmt) { return NULL; } - if (musiccache != NULL) - { - song = musiccache; - } - else - { - song = new BYTE[len]; - if (fread(song, 1, len, file) != (size_t)len) - { - delete[] song; - gme_delete(emu); - return NULL; - } - } + + int fpos = reader.Tell(); + int len = reader.GetLength(); + song = new BYTE[len]; + if (reader.Read(song, len) != len) + { + delete[] song; + gme_delete(emu); + reader.Seek(fpos, SEEK_SET); + return NULL; + } + err = gme_load_data(emu, song, len); - if (song != musiccache) - { - delete[] song; - } + delete[] song; + if (err != NULL) { Printf("Failed loading song: %s\n", err); gme_delete(emu); + reader.Seek(fpos, SEEK_SET); return NULL; } return new GMESong(emu, sample_rate); diff --git a/src/sound/music_hmi_midiout.cpp b/src/sound/music_hmi_midiout.cpp index 15b50da87f..5fef706dd8 100644 --- a/src/sound/music_hmi_midiout.cpp +++ b/src/sound/music_hmi_midiout.cpp @@ -38,6 +38,7 @@ #include "templates.h" #include "doomdef.h" #include "m_swap.h" +#include "files.h" // MACROS ------------------------------------------------------------------ @@ -127,7 +128,7 @@ extern char MIDI_CommonLengths[15]; // //========================================================================== -HMISong::HMISong (FILE *file, BYTE *musiccache, int len, EMidiDevice type) +HMISong::HMISong (FileReader &reader, EMidiDevice type) : MIDIStreamer(type), MusHeader(0), Tracks(0) { #ifdef _WIN32 @@ -136,6 +137,7 @@ HMISong::HMISong (FILE *file, BYTE *musiccache, int len, EMidiDevice type) return; } #endif + int len = reader.GetLength(); if (len < 0x100) { // Way too small to be HMI. return; @@ -143,15 +145,8 @@ HMISong::HMISong (FILE *file, BYTE *musiccache, int len, EMidiDevice type) MusHeader = new BYTE[len]; SongLen = len; NumTracks = 0; - if (file != NULL) - { - if (fread(MusHeader, 1, len, file) != (size_t)len) - return; - } - else - { - memcpy(MusHeader, musiccache, len); - } + if (reader.Read(MusHeader, len) != len) + return; // Do some validation of the MIDI file if (memcmp(MusHeader, HMI_SONG_MAGIC, sizeof(HMI_SONG_MAGIC)) == 0) diff --git a/src/sound/music_midi_base.cpp b/src/sound/music_midi_base.cpp index e0d67683d7..649f9002ec 100644 --- a/src/sound/music_midi_base.cpp +++ b/src/sound/music_midi_base.cpp @@ -33,7 +33,7 @@ static void AddDefaultMidiDevices(FOptionValues *opt) pair[p+1].Value = -3.0; pair[p+2].Text = "TiMidity++"; pair[p+2].Value = -2.0; - pair[p+3].Text = "FMOD"; + pair[p+3].Text = "Sound System"; pair[p+3].Value = -1.0; } @@ -172,7 +172,7 @@ CCMD (snd_listmididevices) PrintMidiDevice (-4, "Gravis Ultrasound Emulation", MOD_SWSYNTH, 0); PrintMidiDevice (-3, "Emulated OPL FM Synth", MOD_FMSYNTH, 0); PrintMidiDevice (-2, "TiMidity++", MOD_SWSYNTH, 0); - PrintMidiDevice (-1, "FMOD", MOD_SWSYNTH, 0); + PrintMidiDevice (-1, "Sound System", 0, 0); if (nummididevices != 0) { for (id = 0; id < nummididevices; ++id) @@ -217,6 +217,6 @@ CCMD (snd_listmididevices) Printf("%s-4. Gravis Ultrasound Emulation\n", -4 == snd_mididevice ? TEXTCOLOR_BOLD : ""); Printf("%s-3. Emulated OPL FM Synth\n", -3 == snd_mididevice ? TEXTCOLOR_BOLD : ""); Printf("%s-2. TiMidity++\n", -2 == snd_mididevice ? TEXTCOLOR_BOLD : ""); - Printf("%s-1. FMOD\n", -1 == snd_mididevice ? TEXTCOLOR_BOLD : ""); + Printf("%s-1. Sound System\n", -1 == snd_mididevice ? TEXTCOLOR_BOLD : ""); } #endif diff --git a/src/sound/music_midi_timidity.cpp b/src/sound/music_midi_timidity.cpp index a8c099d520..ec2843c790 100644 --- a/src/sound/music_midi_timidity.cpp +++ b/src/sound/music_midi_timidity.cpp @@ -611,7 +611,7 @@ int TimidityPPMIDIDevice::Resume() { if (LaunchTimidity()) { - // Assume success if not mixing with FMOD + // Assume success if not mixing with the sound system if (Stream == NULL || Stream->Play(true, timidity_mastervolume)) { Started = true; diff --git a/src/sound/music_midistream.cpp b/src/sound/music_midistream.cpp index 5cb04ed0ee..6c85c06af8 100644 --- a/src/sound/music_midistream.cpp +++ b/src/sound/music_midistream.cpp @@ -215,13 +215,13 @@ EMidiDevice MIDIStreamer::SelectMIDIDevice(EMidiDevice device) - if explicitly selected by $mididevice - when snd_mididevice is -2 and no midi device is set for the song - - FMod: + - Sound System: - if explicitly selected by $mididevice - when snd_mididevice is -1 and no midi device is set for the song - as fallback when both OPL and Timidity failed unless snd_mididevice is >= 0 - MMAPI (Win32 only): - - if explicitly selected by $mididevice (non-Win32 redirects this to FMOD) + - if explicitly selected by $mididevice (non-Win32 redirects this to Sound System) - when snd_mididevice is >= 0 and no midi device is set for the song - as fallback when both OPL and Timidity failed and snd_mididevice is >= 0 */ @@ -233,7 +233,7 @@ EMidiDevice MIDIStreamer::SelectMIDIDevice(EMidiDevice device) } switch (snd_mididevice) { - case -1: return MDEV_FMOD; + case -1: return MDEV_SNDSYS; case -2: return MDEV_TIMIDITY; case -3: return MDEV_OPL; case -4: return MDEV_GUS; @@ -244,7 +244,7 @@ EMidiDevice MIDIStreamer::SelectMIDIDevice(EMidiDevice device) #ifdef _WIN32 return MDEV_MMAPI; #else - return MDEV_FMOD; + return MDEV_SNDSYS; #endif } } @@ -271,8 +271,8 @@ MIDIDevice *MIDIStreamer::CreateMIDIDevice(EMidiDevice devtype) const return new FluidSynthMIDIDevice; #endif - case MDEV_FMOD: - return new FMODMIDIDevice; + case MDEV_SNDSYS: + return new SndSysMIDIDevice; case MDEV_GUS: return new TimidityMIDIDevice; @@ -285,8 +285,8 @@ MIDIDevice *MIDIStreamer::CreateMIDIDevice(EMidiDevice devtype) const catch (CRecoverableError &err) { // The creation of an OPL MIDI device can abort with an error if no GENMIDI lump can be found. - Printf("Unable to create OPL MIDI device: %s\nFalling back to FModEx playback", err.GetMessage()); - return new FMODMIDIDevice; + Printf("Unable to create OPL MIDI device: %s\nFalling back to Sound System playback", err.GetMessage()); + return new SndSysMIDIDevice; } case MDEV_TIMIDITY: diff --git a/src/sound/music_mus_midiout.cpp b/src/sound/music_mus_midiout.cpp index ee84239d6d..5f7d2d5031 100644 --- a/src/sound/music_mus_midiout.cpp +++ b/src/sound/music_mus_midiout.cpp @@ -42,6 +42,7 @@ #include "templates.h" #include "doomdef.h" #include "m_swap.h" +#include "files.h" // MACROS ------------------------------------------------------------------ @@ -91,7 +92,7 @@ static const BYTE CtrlTranslate[15] = // //========================================================================== -MUSSong2::MUSSong2 (FILE *file, BYTE *musiccache, int len, EMidiDevice type) +MUSSong2::MUSSong2 (FileReader &reader, EMidiDevice type) : MIDIStreamer(type), MusHeader(0), MusBuffer(0) { #ifdef _WIN32 @@ -104,11 +105,7 @@ MUSSong2::MUSSong2 (FILE *file, BYTE *musiccache, int len, EMidiDevice type) BYTE front[32]; int start; - if (file == NULL) - { - memcpy(front, musiccache, sizeof(front)); - } - else if (fread(front, 1, sizeof(front), file) != sizeof(front)) + if (reader.Read(front, sizeof(front)) != sizeof(front)) { return; } @@ -124,24 +121,17 @@ MUSSong2::MUSSong2 (FILE *file, BYTE *musiccache, int len, EMidiDevice type) } // Read the remainder of the song. - len = int(len - start); + int len = int(reader.GetLength() - start); if (len < (int)sizeof(MusHeader)) { // It's too short. return; } MusHeader = (MUSHeader *)new BYTE[len]; - if (file == NULL) - { - memcpy(MusHeader, musiccache + start, len); - } - else - { - memcpy(MusHeader, front + start, sizeof(front) - start); - if (fread((BYTE *)MusHeader + sizeof(front) - start, 1, len - (sizeof(front) - start), file) != (size_t)(len - (32 - start))) - { - return; - } - } + memcpy(MusHeader, front + start, sizeof(front) - start); + if (reader.Read((BYTE *)MusHeader + sizeof(front) - start, len - (sizeof(front) - start)) != (len - (32 - start))) + { + return; + } // Do some validation of the MUS file. if (LittleShort(MusHeader->NumChans) > 15) diff --git a/src/sound/music_mus_opl.cpp b/src/sound/music_mus_opl.cpp index fb0d78df5f..3b5012ad94 100644 --- a/src/sound/music_mus_opl.cpp +++ b/src/sound/music_mus_opl.cpp @@ -22,11 +22,11 @@ CUSTOM_CVAR (Int, opl_numchips, 2, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) CVAR(Int, opl_core, 0, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) -OPLMUSSong::OPLMUSSong (FILE *file, BYTE *musiccache, int len) +OPLMUSSong::OPLMUSSong (FileReader &reader) { int samples = int(OPL_SAMPLE_RATE / 14); - Music = new OPLmusicFile (file, musiccache, len); + Music = new OPLmusicFile (&reader); m_Stream = GSnd->CreateStream (FillStream, samples*4, (opl_core == 0 ? SoundStream::Mono : 0) | SoundStream::Float, int(OPL_SAMPLE_RATE), this); diff --git a/src/sound/music_pseudo_mididevice.cpp b/src/sound/music_pseudo_mididevice.cpp index 6c1c90b4cb..14a1758713 100644 --- a/src/sound/music_pseudo_mididevice.cpp +++ b/src/sound/music_pseudo_mididevice.cpp @@ -38,6 +38,7 @@ #include "templates.h" #include "doomdef.h" #include "m_swap.h" +#include "files.h" // MACROS ------------------------------------------------------------------ @@ -234,29 +235,30 @@ FString PseudoMIDIDevice::GetStats() //========================================================================== // -// FMODMIDIDevice :: Open +// SndSysMIDIDevice :: Open // //========================================================================== -int FMODMIDIDevice::Open(void (*callback)(unsigned int, void *, DWORD, DWORD), void *userdata) +int SndSysMIDIDevice::Open(void (*callback)(unsigned int, void *, DWORD, DWORD), void *userdata) { return 0; } //========================================================================== // -// FMODMIDIDevice :: Preprocess +// SndSysMIDIDevice :: Preprocess // // Create a standard MIDI file and stream it. // //========================================================================== -bool FMODMIDIDevice::Preprocess(MIDIStreamer *song, bool looping) +bool SndSysMIDIDevice::Preprocess(MIDIStreamer *song, bool looping) { - TArray midi; + MemoryArrayReader *reader = new MemoryArrayReader(NULL, 0); + song->CreateSMF(reader->GetArray(), looping ? 0 : 1); + reader->UpdateLength(); - song->CreateSMF(midi, looping ? 0 : 1); - bLooping = looping; - Stream = GSnd->OpenStream((char *)&midi[0], looping ? SoundStream::Loop : 0, -1, midi.Size()); - return false; + bLooping = looping; + Stream = GSnd->OpenStream(reader, looping ? SoundStream::Loop : 0); + return false; } diff --git a/src/sound/music_smf_midiout.cpp b/src/sound/music_smf_midiout.cpp index 6fd51f1490..d5d307ec7e 100644 --- a/src/sound/music_smf_midiout.cpp +++ b/src/sound/music_smf_midiout.cpp @@ -41,6 +41,7 @@ #include "templates.h" #include "doomdef.h" #include "m_swap.h" +#include "files.h" // MACROS ------------------------------------------------------------------ @@ -101,7 +102,7 @@ char MIDI_CommonLengths[15] = { 0, 1, 2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; // //========================================================================== -MIDISong2::MIDISong2 (FILE *file, BYTE *musiccache, int len, EMidiDevice type) +MIDISong2::MIDISong2 (FileReader &reader, EMidiDevice type) : MIDIStreamer(type), MusHeader(0), Tracks(0) { int p; @@ -113,17 +114,10 @@ MIDISong2::MIDISong2 (FILE *file, BYTE *musiccache, int len, EMidiDevice type) return; } #endif - MusHeader = new BYTE[len]; - SongLen = len; - if (file != NULL) - { - if (fread(MusHeader, 1, len, file) != (size_t)len) - return; - } - else - { - memcpy(MusHeader, musiccache, len); - } + SongLen = reader.GetLength(); + MusHeader = new BYTE[SongLen]; + if (reader.Read(MusHeader, SongLen) != SongLen) + return; // Do some validation of the MIDI file if (MusHeader[4] != 0 || MusHeader[5] != 0 || MusHeader[6] != 0 || MusHeader[7] != 6) @@ -153,7 +147,7 @@ MIDISong2::MIDISong2 (FILE *file, BYTE *musiccache, int len, EMidiDevice type) Tracks = new TrackInfo[NumTracks]; // Gather information about each track - for (i = 0, p = 14; i < NumTracks && p < len + 8; ++i) + for (i = 0, p = 14; i < NumTracks && p < SongLen + 8; ++i) { DWORD chunkLen = (MusHeader[p+4]<<24) | @@ -161,9 +155,9 @@ MIDISong2::MIDISong2 (FILE *file, BYTE *musiccache, int len, EMidiDevice type) (MusHeader[p+6]<<8) | (MusHeader[p+7]); - if (chunkLen + p + 8 > (DWORD)len) + if (chunkLen + p + 8 > (DWORD)SongLen) { // Track too long, so truncate it - chunkLen = len - p - 8; + chunkLen = SongLen - p - 8; } if (MusHeader[p+0] == 'M' && diff --git a/src/sound/music_stream.cpp b/src/sound/music_stream.cpp index 72c825a28a..7505cb89b5 100644 --- a/src/sound/music_stream.cpp +++ b/src/sound/music_stream.cpp @@ -52,9 +52,14 @@ StreamSong::~StreamSong () } } -StreamSong::StreamSong (const char *filename_or_data, int offset, int len) +StreamSong::StreamSong (FileReader *reader) { - m_Stream = GSnd->OpenStream (filename_or_data, SoundStream::Loop, offset, len); + m_Stream = GSnd->OpenStream (reader, SoundStream::Loop); +} + +StreamSong::StreamSong (const char *url) +{ + m_Stream = GSnd->OpenStream (url, SoundStream::Loop); } bool StreamSong::IsPlaying () diff --git a/src/sound/music_xmi_midiout.cpp b/src/sound/music_xmi_midiout.cpp index fe1761181c..6c179a16a0 100644 --- a/src/sound/music_xmi_midiout.cpp +++ b/src/sound/music_xmi_midiout.cpp @@ -38,6 +38,7 @@ #include "templates.h" #include "doomdef.h" #include "m_swap.h" +#include "files.h" // MACROS ------------------------------------------------------------------ @@ -107,7 +108,7 @@ extern char MIDI_CommonLengths[15]; // //========================================================================== -XMISong::XMISong (FILE *file, BYTE *musiccache, int len, EMidiDevice type) +XMISong::XMISong (FileReader &reader, EMidiDevice type) : MIDIStreamer(type), MusHeader(0), Songs(0) { #ifdef _WIN32 @@ -116,20 +117,13 @@ XMISong::XMISong (FILE *file, BYTE *musiccache, int len, EMidiDevice type) return; } #endif - MusHeader = new BYTE[len]; - SongLen = len; - if (file != NULL) - { - if (fread(MusHeader, 1, len, file) != (size_t)len) - return; - } - else - { - memcpy(MusHeader, musiccache, len); - } + SongLen = reader.GetLength(); + MusHeader = new BYTE[SongLen]; + if (reader.Read(MusHeader, SongLen) != SongLen) + return; // Find all the songs in this file. - NumSongs = FindXMIDforms(MusHeader, len, NULL); + NumSongs = FindXMIDforms(MusHeader, SongLen, NULL); if (NumSongs == 0) { return; @@ -147,7 +141,7 @@ XMISong::XMISong (FILE *file, BYTE *musiccache, int len, EMidiDevice type) Songs = new TrackInfo[NumSongs]; memset(Songs, 0, sizeof(*Songs) * NumSongs); - FindXMIDforms(MusHeader, len, Songs); + FindXMIDforms(MusHeader, SongLen, Songs); CurrSong = Songs; DPrintf("XMI song count: %d\n", NumSongs); } diff --git a/src/sound/oalsound.cpp b/src/sound/oalsound.cpp new file mode 100644 index 0000000000..6d7cb210e6 --- /dev/null +++ b/src/sound/oalsound.cpp @@ -0,0 +1,1973 @@ +/* +** oalsound.cpp +** System interface for sound; uses OpenAL +** +**--------------------------------------------------------------------------- +** Copyright 2008-2010 Chris Robinson +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1. Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** 2. Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in the +** documentation and/or other materials provided with the distribution. +** 3. The name of the author may not be used to endorse or promote products +** derived from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +**--------------------------------------------------------------------------- +** +*/ + +#ifdef _WIN32 +#define WIN32_LEAN_AND_MEAN +#include +#define USE_WINDOWS_DWORD +#endif + +#include "except.h" +#include "doomstat.h" +#include "templates.h" +#include "oalsound.h" +#include "c_cvars.h" +#include "c_dispatch.h" +#include "i_system.h" +#include "v_text.h" +#include "gi.h" +#include "actor.h" +#include "r_state.h" +#include "w_wad.h" +#include "i_music.h" +#include "i_musicinterns.h" +#include "tempfiles.h" + + +CVAR (String, snd_aldevice, "Default", CVAR_ARCHIVE|CVAR_GLOBALCONFIG) +CVAR (Bool, snd_efx, true, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) + + +bool IsOpenALPresent() +{ +#ifdef NO_OPENAL + return false; +#elif !defined _WIN32 + return true; // on non-Windows we cannot delay load the library so it has to be present. +#else + static bool cached_result; + static bool done = false; + + if (!done) + { + done = true; + + __try + { + // just call one function from the API to force loading the DLL + alcGetError(NULL); + } + __except (CheckException(GetExceptionCode())) + { + // FMod could not be delay loaded + return false; + } + cached_result = true; + } + return cached_result; +#endif +} + +void I_BuildALDeviceList(FOptionValues *opt) +{ + opt->mValues.Resize(1); + opt->mValues[0].TextValue = "Default"; + opt->mValues[0].Text = "Default"; + +#ifndef NO_OPENAL + if (IsOpenALPresent()) + { + const ALCchar *names = (alcIsExtensionPresent(NULL, "ALC_ENUMERATE_ALL_EXT") ? + alcGetString(NULL, ALC_ALL_DEVICES_SPECIFIER) : + alcGetString(NULL, ALC_DEVICE_SPECIFIER)); + if (!names) + Printf("Failed to get device list: %s\n", alcGetString(NULL, alcGetError(NULL))); + else while (*names) + { + unsigned int i = opt->mValues.Reserve(1); + opt->mValues[i].TextValue = names; + opt->mValues[i].Text = names; + + names += strlen(names) + 1; + } + } +#endif +} + +#ifndef NO_OPENAL + + +EXTERN_CVAR (Int, snd_channels) +EXTERN_CVAR (Int, snd_samplerate) +EXTERN_CVAR (Bool, snd_waterreverb) +EXTERN_CVAR (Bool, snd_pitched) + + +#define MAKE_PTRID(x) ((void*)(uintptr_t)(x)) +#define GET_PTRID(x) ((uint32)(uintptr_t)(x)) + + +static ALenum checkALError(const char *fn, unsigned int ln) +{ + ALenum err = alGetError(); + if(err != AL_NO_ERROR) + { + if(strchr(fn, '/')) + fn = strrchr(fn, '/')+1; + else if(strchr(fn, '\\')) + fn = strrchr(fn, '\\')+1; + Printf(">>>>>>>>>>>> Received AL error %s (%#x), %s:%u\n", alGetString(err), err, fn, ln); + } + return err; +} +#define getALError() checkALError(__FILE__, __LINE__) + +static ALCenum checkALCError(ALCdevice *device, const char *fn, unsigned int ln) +{ + ALCenum err = alcGetError(device); + if(err != ALC_NO_ERROR) + { + if(strchr(fn, '/')) + fn = strrchr(fn, '/')+1; + else if(strchr(fn, '\\')) + fn = strrchr(fn, '\\')+1; + Printf(">>>>>>>>>>>> Received ALC error %s (%#x), %s:%u\n", alcGetString(device, err), err, fn, ln); + } + return err; +} +#define getALCError(d) checkALCError((d), __FILE__, __LINE__) + + +// Fallback methods for when AL_SOFT_deferred_updates isn't available. In most +// cases these don't actually do anything, except on some Creative drivers +// where they act as appropriate fallbacks. +static ALvoid AL_APIENTRY _wrap_DeferUpdatesSOFT(void) +{ + alcSuspendContext(alcGetCurrentContext()); +} + +static ALvoid AL_APIENTRY _wrap_ProcessUpdatesSOFT(void) +{ + alcProcessContext(alcGetCurrentContext()); +} + + +class OpenALSoundStream : public SoundStream +{ + OpenALSoundRenderer *Renderer; + + SoundStreamCallback Callback; + void *UserData; + + TArray Data; + + ALsizei SampleRate; + ALenum Format; + ALsizei FrameSize; + + static const int BufferCount = 4; + ALuint Buffers[BufferCount]; + ALuint Source; + + bool Playing; + bool Looping; + ALfloat Volume; + + + FileReader *Reader; + SoundDecoder *Decoder; + static bool DecoderCallback(SoundStream *_sstream, void *ptr, int length, void *user) + { + OpenALSoundStream *self = static_cast(_sstream); + if(length < 0) return false; + + size_t got = self->Decoder->read((char*)ptr, length); + if(got < (unsigned int)length) + { + if(!self->Looping || !self->Decoder->seek(0)) + return false; + got += self->Decoder->read((char*)ptr+got, length-got); + } + + return (got == (unsigned int)length); + } + + + bool SetupSource() + { + /* Get a source, killing the farthest, lowest-priority sound if needed */ + if(Renderer->FreeSfx.Size() == 0) + { + FSoundChan *lowest = Renderer->FindLowestChannel(); + if(lowest) Renderer->StopChannel(lowest); + + if(Renderer->FreeSfx.Size() == 0) + return false; + } + Renderer->FreeSfx.Pop(Source); + + /* Set the default properties for localized playback */ + alSource3f(Source, AL_DIRECTION, 0.f, 0.f, 0.f); + alSource3f(Source, AL_VELOCITY, 0.f, 0.f, 0.f); + alSource3f(Source, AL_POSITION, 0.f, 0.f, 0.f); + alSourcef(Source, AL_MAX_GAIN, 1.f); + alSourcef(Source, AL_GAIN, 1.f); + alSourcef(Source, AL_PITCH, 1.f); + alSourcef(Source, AL_ROLLOFF_FACTOR, 0.f); + alSourcef(Source, AL_SEC_OFFSET, 0.f); + alSourcei(Source, AL_SOURCE_RELATIVE, AL_TRUE); + alSourcei(Source, AL_LOOPING, AL_FALSE); + if(Renderer->EnvSlot) + { + alSourcef(Source, AL_ROOM_ROLLOFF_FACTOR, 0.f); + alSourcef(Source, AL_AIR_ABSORPTION_FACTOR, 0.f); + alSourcei(Source, AL_DIRECT_FILTER, AL_FILTER_NULL); + alSource3i(Source, AL_AUXILIARY_SEND_FILTER, 0, 0, AL_FILTER_NULL); + } + + alGenBuffers(BufferCount, Buffers); + return (getALError() == AL_NO_ERROR); + } + +public: + OpenALSoundStream(OpenALSoundRenderer *renderer) + : Renderer(renderer), Source(0), Playing(false), Looping(false), Volume(1.0f), Reader(NULL), Decoder(NULL) + { + Renderer->Streams.Push(this); + memset(Buffers, 0, sizeof(Buffers)); + } + + virtual ~OpenALSoundStream() + { + if(Source) + { + alSourceRewind(Source); + alSourcei(Source, AL_BUFFER, 0); + + Renderer->FreeSfx.Push(Source); + Source = 0; + } + + if(Buffers[0]) + { + alDeleteBuffers(BufferCount, &Buffers[0]); + memset(Buffers, 0, sizeof(Buffers)); + } + getALError(); + + Renderer->Streams.Delete(Renderer->Streams.Find(this)); + Renderer = NULL; + + delete Decoder; + delete Reader; + } + + + virtual bool Play(bool loop, float vol) + { + SetVolume(vol); + + if(Playing) + return true; + + /* Clear the buffer queue, then fill and queue each buffer */ + alSourcei(Source, AL_BUFFER, 0); + for(int i = 0;i < BufferCount;i++) + { + if(!Callback(this, &Data[0], Data.Size(), UserData)) + { + if(i == 0) + return false; + break; + } + + alBufferData(Buffers[i], Format, &Data[0], Data.Size(), SampleRate); + alSourceQueueBuffers(Source, 1, &Buffers[i]); + } + if(getALError() != AL_NO_ERROR) + return false; + + alSourcePlay(Source); + Playing = (getALError()==AL_NO_ERROR); + + return Playing; + } + + virtual void Stop() + { + if(!Playing) + return; + + alSourceStop(Source); + alSourcei(Source, AL_BUFFER, 0); + getALError(); + + Playing = false; + } + + virtual void SetVolume(float vol) + { + Volume = vol; + UpdateVolume(); + } + + void UpdateVolume() + { + alSourcef(Source, AL_GAIN, Renderer->MusicVolume*Volume); + getALError(); + } + + virtual bool SetPaused(bool pause) + { + if(pause) + alSourcePause(Source); + else + alSourcePlay(Source); + return (getALError()==AL_NO_ERROR); + } + + virtual bool SetPosition(unsigned int ms_pos) + { + if(!Decoder->seek(ms_pos)) + return false; + + if(!Playing) + return true; + // Stop the source so that all buffers become processed, then call + // IsEnded() to refill and restart the source queue with the new + // position. + alSourceStop(Source); + getALError(); + return !IsEnded(); + } + + virtual unsigned int GetPosition() + { + ALint offset, queued, state; + alGetSourcei(Source, AL_SAMPLE_OFFSET, &offset); + alGetSourcei(Source, AL_BUFFERS_QUEUED, &queued); + alGetSourcei(Source, AL_SOURCE_STATE, &state); + if(getALError() != AL_NO_ERROR) + return 0; + + size_t pos = Decoder->getSampleOffset(); + if(state != AL_STOPPED) + { + size_t rem = queued*(Data.Size()/FrameSize) - offset; + if(pos > rem) pos -= rem; + else pos = 0; + } + return (unsigned int)(pos * 1000.0 / SampleRate); + } + + virtual bool IsEnded() + { + if(!Playing) + return true; + + ALint state, processed; + alGetSourcei(Source, AL_SOURCE_STATE, &state); + alGetSourcei(Source, AL_BUFFERS_PROCESSED, &processed); + + Playing = (getALError()==AL_NO_ERROR); + if(!Playing) + return true; + + // For each processed buffer in the queue... + while(processed > 0) + { + ALuint bufid; + + // Unqueue the oldest buffer, fill it with more data, and queue it + // on the end + alSourceUnqueueBuffers(Source, 1, &bufid); + processed--; + + if(Callback(this, &Data[0], Data.Size(), UserData)) + { + alBufferData(bufid, Format, &Data[0], Data.Size(), SampleRate); + alSourceQueueBuffers(Source, 1, &bufid); + } + } + + // If the source is not playing or paused, and there are buffers queued, + // then there was an underrun. Restart the source. + Playing = (getALError()==AL_NO_ERROR); + if(Playing && state != AL_PLAYING && state != AL_PAUSED) + { + ALint queued = 0; + alGetSourcei(Source, AL_BUFFERS_QUEUED, &queued); + + Playing = (getALError() == AL_NO_ERROR) && (queued > 0); + if(Playing) + { + alSourcePlay(Source); + Playing = (getALError()==AL_NO_ERROR); + } + } + + return !Playing; + } + + FString GetStats() + { + FString stats; + size_t pos, len; + ALfloat volume; + ALint offset; + ALint processed; + ALint queued; + ALint state; + ALenum err; + + alGetSourcef(Source, AL_GAIN, &volume); + alGetSourcei(Source, AL_SAMPLE_OFFSET, &offset); + alGetSourcei(Source, AL_BUFFERS_PROCESSED, &processed); + alGetSourcei(Source, AL_BUFFERS_QUEUED, &queued); + alGetSourcei(Source, AL_SOURCE_STATE, &state); + if((err=alGetError()) != AL_NO_ERROR) + { + stats = "Error getting stats: "; + stats += alGetString(err); + return stats; + } + + stats = (state == AL_INITIAL) ? "Buffering" : (state == AL_STOPPED) ? "Underrun" : + (state == AL_PLAYING || state == AL_PAUSED) ? "Ready" : "Unknown state"; + + pos = Decoder->getSampleOffset(); + len = Decoder->getSampleLength(); + if(state == AL_STOPPED) + offset = BufferCount * (Data.Size()/FrameSize); + else + { + size_t rem = queued*(Data.Size()/FrameSize) - offset; + if(pos > rem) pos -= rem; + else if(len > 0) pos += len - rem; + else pos = 0; + } + pos = (size_t)(pos * 1000.0 / SampleRate); + len = (size_t)(len * 1000.0 / SampleRate); + stats.AppendFormat(",%3u%% buffered", 100 - 100*offset/(BufferCount*(Data.Size()/FrameSize))); + stats.AppendFormat(", %zu.%03zu", pos/1000, pos%1000); + if(len > 0) + stats.AppendFormat(" / %zu.%03zu", len/1000, len%1000); + if(state == AL_PAUSED) + stats += ", paused"; + if(state == AL_PLAYING) + stats += ", playing"; + stats.AppendFormat(", %uHz", SampleRate); + if(!Playing) + stats += " XX"; + return stats; + } + + bool Init(SoundStreamCallback callback, int buffbytes, int flags, int samplerate, void *userdata) + { + if(!SetupSource()) + return false; + + Callback = callback; + UserData = userdata; + SampleRate = samplerate; + + Format = AL_NONE; + if((flags&Bits8)) /* Signed or unsigned? We assume unsigned 8-bit... */ + { + if((flags&Mono)) Format = AL_FORMAT_MONO8; + else Format = AL_FORMAT_STEREO8; + } + else if((flags&Float)) + { + if(alIsExtensionPresent("AL_EXT_FLOAT32")) + { + if((flags&Mono)) Format = AL_FORMAT_MONO_FLOAT32; + else Format = AL_FORMAT_STEREO_FLOAT32; + } + } + else if((flags&Bits32)) + { + } + else + { + if((flags&Mono)) Format = AL_FORMAT_MONO16; + else Format = AL_FORMAT_STEREO16; + } + + if(Format == AL_NONE) + { + Printf("Unsupported format: 0x%x\n", flags); + return false; + } + + FrameSize = 1; + if((flags&Bits8)) + FrameSize *= 1; + else if((flags&(Bits32|Float))) + FrameSize *= 4; + else + FrameSize *= 2; + + if((flags&Mono)) + FrameSize *= 1; + else + FrameSize *= 2; + + buffbytes += FrameSize-1; + buffbytes -= buffbytes%FrameSize; + Data.Resize(buffbytes); + + return true; + } + + bool Init(FileReader *reader, bool loop) + { + if(!SetupSource()) + { + delete reader; + return false; + } + + if(Decoder) delete Decoder; + if(Reader) delete Reader; + Reader = reader; + Decoder = Renderer->CreateDecoder(Reader); + if(!Decoder) return false; + + Callback = DecoderCallback; + UserData = NULL; + Format = AL_NONE; + FrameSize = 1; + + ChannelConfig chans; + SampleType type; + int srate; + + Decoder->getInfo(&srate, &chans, &type); + if(chans == ChannelConfig_Mono) + { + if(type == SampleType_UInt8) Format = AL_FORMAT_MONO8; + if(type == SampleType_Int16) Format = AL_FORMAT_MONO16; + FrameSize *= 1; + } + if(chans == ChannelConfig_Stereo) + { + if(type == SampleType_UInt8) Format = AL_FORMAT_STEREO8; + if(type == SampleType_Int16) Format = AL_FORMAT_STEREO16; + FrameSize *= 2; + } + if(type == SampleType_UInt8) FrameSize *= 1; + if(type == SampleType_Int16) FrameSize *= 2; + + if(Format == AL_NONE) + { + Printf("Unsupported audio format: %s, %s\n", GetChannelConfigName(chans), + GetSampleTypeName(type)); + return false; + } + SampleRate = srate; + Looping = loop; + + Data.Resize((size_t)(0.2 * SampleRate) * FrameSize); + + return true; + } +}; + + +extern ReverbContainer *ForcedEnvironment; + +#define AREA_SOUND_RADIUS (128.f) + +#define PITCH_MULT (0.7937005f) /* Approx. 4 semitones lower; what Nash suggested */ + +#define PITCH(pitch) (snd_pitched ? (pitch)/128.f : 1.f) + + +static float GetRolloff(const FRolloffInfo *rolloff, float distance) +{ + if(distance <= rolloff->MinDistance) + return 1.f; + // Logarithmic rolloff has no max distance where it goes silent. + if(rolloff->RolloffType == ROLLOFF_Log) + return rolloff->MinDistance / + (rolloff->MinDistance + rolloff->RolloffFactor*(distance-rolloff->MinDistance)); + if(distance >= rolloff->MaxDistance) + return 0.f; + + float volume = (rolloff->MaxDistance - distance) / (rolloff->MaxDistance - rolloff->MinDistance); + if(rolloff->RolloffType == ROLLOFF_Linear) + return volume; + + if(rolloff->RolloffType == ROLLOFF_Custom && S_SoundCurve != NULL) + return S_SoundCurve[int(S_SoundCurveSize * (1.f - volume))] / 127.f; + return (powf(10.f, volume) - 1.f) / 9.f; +} + +ALCdevice *OpenALSoundRenderer::InitDevice() +{ + ALCdevice *device = NULL; + if (IsOpenALPresent()) + { + if(strcmp(snd_aldevice, "Default") != 0) + { + device = alcOpenDevice(*snd_aldevice); + if(!device) + Printf(TEXTCOLOR_BLUE" Failed to open device "TEXTCOLOR_BOLD"%s"TEXTCOLOR_BLUE". Trying default.\n", *snd_aldevice); + } + + if(!device) + { + device = alcOpenDevice(NULL); + if(!device) + { + Printf(TEXTCOLOR_RED" Could not open audio device\n"); + } + } + } + else + { + Printf(TEXTCOLOR_ORANGE"Failed to load openal32.dll\n"); + } + return device; +} + + +template +static void LoadALFunc(const char *name, T *x) +{ *x = reinterpret_cast(alGetProcAddress(name)); } + +#define LOAD_FUNC(x) (LoadALFunc(#x, &x)) +OpenALSoundRenderer::OpenALSoundRenderer() + : Device(NULL), Context(NULL), SFXPaused(0), PrevEnvironment(NULL), EnvSlot(0) +{ + EnvFilters[0] = EnvFilters[1] = 0; + + Printf("I_InitSound: Initializing OpenAL\n"); + + Device = InitDevice(); + if (Device == NULL) return; + + const ALCchar *current = NULL; + if(alcIsExtensionPresent(Device, "ALC_ENUMERATE_ALL_EXT")) + current = alcGetString(Device, ALC_ALL_DEVICES_SPECIFIER); + if(alcGetError(Device) != ALC_NO_ERROR || !current) + current = alcGetString(Device, ALC_DEVICE_SPECIFIER); + Printf(" Opened device "TEXTCOLOR_ORANGE"%s\n", current); + + ALCint major=0, minor=0; + alcGetIntegerv(Device, ALC_MAJOR_VERSION, 1, &major); + alcGetIntegerv(Device, ALC_MINOR_VERSION, 1, &minor); + DPrintf(" ALC Version: "TEXTCOLOR_BLUE"%d.%d\n", major, minor); + DPrintf(" ALC Extensions: "TEXTCOLOR_ORANGE"%s\n", alcGetString(Device, ALC_EXTENSIONS)); + + TArray attribs; + if(*snd_samplerate > 0) + { + attribs.Push(ALC_FREQUENCY); + attribs.Push(*snd_samplerate); + } + // Make sure one source is capable of stereo output with the rest doing + // mono, without running out of voices + attribs.Push(ALC_MONO_SOURCES); + attribs.Push(MAX(*snd_channels, 2) - 1); + attribs.Push(ALC_STEREO_SOURCES); + attribs.Push(1); + // Other attribs..? + attribs.Push(0); + + Context = alcCreateContext(Device, &attribs[0]); + if(!Context || alcMakeContextCurrent(Context) == ALC_FALSE) + { + Printf(TEXTCOLOR_RED" Failed to setup context: %s\n", alcGetString(Device, alcGetError(Device))); + if(Context) + alcDestroyContext(Context); + Context = NULL; + alcCloseDevice(Device); + Device = NULL; + return; + } + attribs.Clear(); + + DPrintf(" Vendor: "TEXTCOLOR_ORANGE"%s\n", alGetString(AL_VENDOR)); + DPrintf(" Renderer: "TEXTCOLOR_ORANGE"%s\n", alGetString(AL_RENDERER)); + DPrintf(" Version: "TEXTCOLOR_ORANGE"%s\n", alGetString(AL_VERSION)); + DPrintf(" Extensions: "TEXTCOLOR_ORANGE"%s\n", alGetString(AL_EXTENSIONS)); + + ALC.EXT_EFX = !!alcIsExtensionPresent(Device, "ALC_EXT_EFX"); + ALC.EXT_disconnect = !!alcIsExtensionPresent(Device, "ALC_EXT_disconnect");; + AL.EXT_source_distance_model = !!alIsExtensionPresent("AL_EXT_source_distance_model"); + AL.SOFT_deferred_updates = !!alIsExtensionPresent("AL_SOFT_deferred_updates"); + AL.SOFT_loop_points = !!alIsExtensionPresent("AL_SOFT_loop_points"); + + alDopplerFactor(0.5f); + alSpeedOfSound(343.3f * 96.0f); + alDistanceModel(AL_INVERSE_DISTANCE); + if(AL.EXT_source_distance_model) + alEnable(AL_SOURCE_DISTANCE_MODEL); + + if(AL.SOFT_deferred_updates) + { + LOAD_FUNC(alDeferUpdatesSOFT); + LOAD_FUNC(alProcessUpdatesSOFT); + } + else + { + alDeferUpdatesSOFT = _wrap_DeferUpdatesSOFT; + alProcessUpdatesSOFT = _wrap_ProcessUpdatesSOFT; + } + + ALenum err = getALError(); + if(err != AL_NO_ERROR) + { + alcMakeContextCurrent(NULL); + alcDestroyContext(Context); + Context = NULL; + alcCloseDevice(Device); + Device = NULL; + return; + } + + ALCint numMono=0, numStereo=0; + alcGetIntegerv(Device, ALC_MONO_SOURCES, 1, &numMono); + alcGetIntegerv(Device, ALC_STEREO_SOURCES, 1, &numStereo); + + Sources.Resize(MIN(MAX(*snd_channels, 2), numMono+numStereo)); + for(size_t i = 0;i < Sources.Size();i++) + { + alGenSources(1, &Sources[i]); + if(getALError() != AL_NO_ERROR) + { + Sources.Resize(i); + Sources.ShrinkToFit(); + break; + } + } + if(Sources.Size() == 0) + { + Printf(TEXTCOLOR_RED" Error: could not generate any sound sources!\n"); + alcMakeContextCurrent(NULL); + alcDestroyContext(Context); + Context = NULL; + alcCloseDevice(Device); + Device = NULL; + return; + } + FreeSfx = Sources; + DPrintf(" Allocated "TEXTCOLOR_BLUE"%u"TEXTCOLOR_NORMAL" sources\n", Sources.Size()); + + WasInWater = false; + if(*snd_efx && ALC.EXT_EFX) + { + // EFX function pointers + LOAD_FUNC(alGenEffects); + LOAD_FUNC(alDeleteEffects); + LOAD_FUNC(alIsEffect); + LOAD_FUNC(alEffecti); + LOAD_FUNC(alEffectiv); + LOAD_FUNC(alEffectf); + LOAD_FUNC(alEffectfv); + LOAD_FUNC(alGetEffecti); + LOAD_FUNC(alGetEffectiv); + LOAD_FUNC(alGetEffectf); + LOAD_FUNC(alGetEffectfv); + + LOAD_FUNC(alGenFilters); + LOAD_FUNC(alDeleteFilters); + LOAD_FUNC(alIsFilter); + LOAD_FUNC(alFilteri); + LOAD_FUNC(alFilteriv); + LOAD_FUNC(alFilterf); + LOAD_FUNC(alFilterfv); + LOAD_FUNC(alGetFilteri); + LOAD_FUNC(alGetFilteriv); + LOAD_FUNC(alGetFilterf); + LOAD_FUNC(alGetFilterfv); + + LOAD_FUNC(alGenAuxiliaryEffectSlots); + LOAD_FUNC(alDeleteAuxiliaryEffectSlots); + LOAD_FUNC(alIsAuxiliaryEffectSlot); + LOAD_FUNC(alAuxiliaryEffectSloti); + LOAD_FUNC(alAuxiliaryEffectSlotiv); + LOAD_FUNC(alAuxiliaryEffectSlotf); + LOAD_FUNC(alAuxiliaryEffectSlotfv); + LOAD_FUNC(alGetAuxiliaryEffectSloti); + LOAD_FUNC(alGetAuxiliaryEffectSlotiv); + LOAD_FUNC(alGetAuxiliaryEffectSlotf); + LOAD_FUNC(alGetAuxiliaryEffectSlotfv); + if(getALError() == AL_NO_ERROR) + { + ALuint envReverb; + alGenEffects(1, &envReverb); + if(getALError() == AL_NO_ERROR) + { + alEffecti(envReverb, AL_EFFECT_TYPE, AL_EFFECT_EAXREVERB); + if(alGetError() == AL_NO_ERROR) + DPrintf(" EAX Reverb found\n"); + alEffecti(envReverb, AL_EFFECT_TYPE, AL_EFFECT_REVERB); + if(alGetError() == AL_NO_ERROR) + DPrintf(" Standard Reverb found\n"); + + alDeleteEffects(1, &envReverb); + getALError(); + } + + alGenAuxiliaryEffectSlots(1, &EnvSlot); + alGenFilters(2, EnvFilters); + if(getALError() == AL_NO_ERROR) + { + alFilteri(EnvFilters[0], AL_FILTER_TYPE, AL_FILTER_LOWPASS); + alFilteri(EnvFilters[1], AL_FILTER_TYPE, AL_FILTER_LOWPASS); + if(getALError() == AL_NO_ERROR) + DPrintf(" Lowpass found\n"); + else + { + alDeleteFilters(2, EnvFilters); + EnvFilters[0] = EnvFilters[1] = 0; + alDeleteAuxiliaryEffectSlots(1, &EnvSlot); + EnvSlot = 0; + getALError(); + } + } + else + { + alDeleteFilters(2, EnvFilters); + alDeleteAuxiliaryEffectSlots(1, &EnvSlot); + EnvFilters[0] = EnvFilters[1] = 0; + EnvSlot = 0; + getALError(); + } + } + } + + if(EnvSlot) + Printf(" EFX enabled\n"); +} +#undef LOAD_FUNC + +OpenALSoundRenderer::~OpenALSoundRenderer() +{ + if(!Device) + return; + + while(Streams.Size() > 0) + delete Streams[0]; + + alDeleteSources(Sources.Size(), &Sources[0]); + Sources.Clear(); + FreeSfx.Clear(); + SfxGroup.Clear(); + PausableSfx.Clear(); + ReverbSfx.Clear(); + + if(EnvEffects.CountUsed() > 0) + { + EffectMapIter iter(EnvEffects); + EffectMap::Pair *pair; + while(iter.NextPair(pair)) + alDeleteEffects(1, &(pair->Value)); + } + EnvEffects.Clear(); + + if(EnvSlot) + { + alDeleteAuxiliaryEffectSlots(1, &EnvSlot); + alDeleteFilters(2, EnvFilters); + } + EnvSlot = 0; + EnvFilters[0] = EnvFilters[1] = 0; + + alcMakeContextCurrent(NULL); + alcDestroyContext(Context); + Context = NULL; + alcCloseDevice(Device); + Device = NULL; +} + +void OpenALSoundRenderer::SetSfxVolume(float volume) +{ + SfxVolume = volume; + + FSoundChan *schan = Channels; + while(schan) + { + if(schan->SysChannel != NULL) + { + ALuint source = GET_PTRID(schan->SysChannel); + volume = SfxVolume; + + alDeferUpdatesSOFT(); + alSourcef(source, AL_MAX_GAIN, volume); + alSourcef(source, AL_GAIN, volume * schan->Volume); + } + schan = schan->NextChan; + } + + getALError(); +} + +void OpenALSoundRenderer::SetMusicVolume(float volume) +{ + MusicVolume = volume; + for(uint32 i = 0;i < Streams.Size();++i) + Streams[i]->UpdateVolume(); +} + +unsigned int OpenALSoundRenderer::GetMSLength(SoundHandle sfx) +{ + if(sfx.data) + { + ALuint buffer = GET_PTRID(sfx.data); + if(alIsBuffer(buffer)) + { + ALint bits, channels, freq, size; + alGetBufferi(buffer, AL_BITS, &bits); + alGetBufferi(buffer, AL_CHANNELS, &channels); + alGetBufferi(buffer, AL_FREQUENCY, &freq); + alGetBufferi(buffer, AL_SIZE, &size); + if(getALError() == AL_NO_ERROR) + return (unsigned int)(size / (channels*bits/8) * 1000. / freq); + } + } + return 0; +} + +unsigned int OpenALSoundRenderer::GetSampleLength(SoundHandle sfx) +{ + if(sfx.data) + { + ALuint buffer = GET_PTRID(sfx.data); + ALint bits, channels, size; + alGetBufferi(buffer, AL_BITS, &bits); + alGetBufferi(buffer, AL_CHANNELS, &channels); + alGetBufferi(buffer, AL_SIZE, &size); + if(getALError() == AL_NO_ERROR) + return (ALsizei)(size / (channels * bits / 8)); + } + return 0; +} + +float OpenALSoundRenderer::GetOutputRate() +{ + ALCint rate = 44100; // Default, just in case + alcGetIntegerv(Device, ALC_FREQUENCY, 1, &rate); + return (float)rate; +} + + +SoundHandle OpenALSoundRenderer::LoadSoundRaw(BYTE *sfxdata, int length, int frequency, int channels, int bits, int loopstart, int loopend) +{ + SoundHandle retval = { NULL }; + + if(length == 0) return retval; + + if(bits == -8) + { + // Simple signed->unsigned conversion + for(int i = 0;i < length;i++) + sfxdata[i] ^= 0x80; + bits = -bits; + } + + ALenum format = AL_NONE; + if(bits == 16) + { + if(channels == 1) format = AL_FORMAT_MONO16; + if(channels == 2) format = AL_FORMAT_STEREO16; + } + else if(bits == 8) + { + if(channels == 1) format = AL_FORMAT_MONO8; + if(channels == 2) format = AL_FORMAT_STEREO8; + } + + if(format == AL_NONE || frequency <= 0) + { + Printf("Unhandled format: %d bit, %d channel, %d hz\n", bits, channels, frequency); + return retval; + } + length -= length%(channels*bits/8); + + ALenum err; + ALuint buffer = 0; + alGenBuffers(1, &buffer); + alBufferData(buffer, format, sfxdata, length, frequency); + if((err=getALError()) != AL_NO_ERROR) + { + Printf("Failed to buffer data: %s\n", alGetString(err)); + alDeleteBuffers(1, &buffer); + getALError(); + return retval; + } + + if((loopstart > 0 || loopend > 0) && AL.SOFT_loop_points) + { + if(loopstart < 0) + loopstart = 0; + if(loopend < loopstart) + loopend = length / (channels*bits/8); + + ALint loops[2] = { loopstart, loopend }; + DPrintf("Setting loop points %d -> %d\n", loops[0], loops[1]); + alBufferiv(buffer, AL_LOOP_POINTS_SOFT, loops); + getALError(); + } + else if(loopstart > 0 || loopend > 0) + { + static bool warned = false; + if(!warned) + Printf("Loop points not supported!\n"); + warned = true; + } + + retval.data = MAKE_PTRID(buffer); + return retval; +} + +SoundHandle OpenALSoundRenderer::LoadSound(BYTE *sfxdata, int length) +{ + SoundHandle retval = { NULL }; + MemoryReader reader((const char*)sfxdata, length); + ALenum format = AL_NONE; + ChannelConfig chans; + SampleType type; + int srate; + + SoundDecoder *decoder = CreateDecoder(&reader); + if(!decoder) return retval; + + decoder->getInfo(&srate, &chans, &type); + if(chans == ChannelConfig_Mono) + { + if(type == SampleType_UInt8) format = AL_FORMAT_MONO8; + if(type == SampleType_Int16) format = AL_FORMAT_MONO16; + } + if(chans == ChannelConfig_Stereo) + { + if(type == SampleType_UInt8) format = AL_FORMAT_STEREO8; + if(type == SampleType_Int16) format = AL_FORMAT_STEREO16; + } + + if(format == AL_NONE) + { + Printf("Unsupported audio format: %s, %s\n", GetChannelConfigName(chans), + GetSampleTypeName(type)); + delete decoder; + return retval; + } + + TArray data = decoder->readAll(); + + ALuint buffer = 0; + alGenBuffers(1, &buffer); + alBufferData(buffer, format, &data[0], data.Size(), srate); + + ALenum err; + if((err=getALError()) != AL_NO_ERROR) + { + Printf("Failed to buffer data: %s\n", alGetString(err)); + alDeleteBuffers(1, &buffer); + getALError(); + delete decoder; + return retval; + } + + retval.data = MAKE_PTRID(buffer); + delete decoder; + return retval; +} + +void OpenALSoundRenderer::UnloadSound(SoundHandle sfx) +{ + if(!sfx.data) + return; + + ALuint buffer = GET_PTRID(sfx.data); + FSoundChan *schan = Channels; + while(schan) + { + if(schan->SysChannel) + { + ALint bufID = 0; + alGetSourcei(GET_PTRID(schan->SysChannel), AL_BUFFER, &bufID); + if((ALuint)bufID == buffer) + { + FSoundChan *next = schan->NextChan; + StopChannel(schan); + schan = next; + continue; + } + } + schan = schan->NextChan; + } + + alDeleteBuffers(1, &buffer); + getALError(); +} + + +SoundStream *OpenALSoundRenderer::CreateStream(SoundStreamCallback callback, int buffbytes, int flags, int samplerate, void *userdata) +{ + OpenALSoundStream *stream = new OpenALSoundStream(this); + if (!stream->Init(callback, buffbytes, flags, samplerate, userdata)) + { + delete stream; + return NULL; + } + return stream; +} + +SoundStream *OpenALSoundRenderer::OpenStream(FileReader *reader, int flags) +{ + OpenALSoundStream *stream = new OpenALSoundStream(this); + if (!stream->Init(reader, !!(flags&SoundStream::Loop))) + { + delete stream; + return NULL; + } + return stream; +} + +FISoundChannel *OpenALSoundRenderer::StartSound(SoundHandle sfx, float vol, int pitch, int chanflags, FISoundChannel *reuse_chan) +{ + if(FreeSfx.Size() == 0) + { + FSoundChan *lowest = FindLowestChannel(); + if(lowest) StopChannel(lowest); + + if(FreeSfx.Size() == 0) + return NULL; + } + + ALuint buffer = GET_PTRID(sfx.data); + ALuint source = FreeSfx.Last(); + alSource3f(source, AL_POSITION, 0.f, 0.f, 0.f); + alSource3f(source, AL_VELOCITY, 0.f, 0.f, 0.f); + alSource3f(source, AL_DIRECTION, 0.f, 0.f, 0.f); + alSourcei(source, AL_SOURCE_RELATIVE, AL_TRUE); + + alSourcei(source, AL_LOOPING, (chanflags&SNDF_LOOP) ? AL_TRUE : AL_FALSE); + + alSourcef(source, AL_REFERENCE_DISTANCE, 1.f); + alSourcef(source, AL_MAX_DISTANCE, 1000.f); + alSourcef(source, AL_ROLLOFF_FACTOR, 0.f); + alSourcef(source, AL_MAX_GAIN, SfxVolume); + alSourcef(source, AL_GAIN, SfxVolume*vol); + + if(EnvSlot) + { + if(!(chanflags&SNDF_NOREVERB)) + { + alSourcei(source, AL_DIRECT_FILTER, EnvFilters[0]); + alSource3i(source, AL_AUXILIARY_SEND_FILTER, EnvSlot, 0, EnvFilters[1]); + } + else + { + alSourcei(source, AL_DIRECT_FILTER, AL_FILTER_NULL); + alSource3i(source, AL_AUXILIARY_SEND_FILTER, 0, 0, AL_FILTER_NULL); + } + alSourcef(source, AL_ROOM_ROLLOFF_FACTOR, 0.f); + } + if(WasInWater && !(chanflags&SNDF_NOREVERB)) + alSourcef(source, AL_PITCH, PITCH(pitch)*PITCH_MULT); + else + alSourcef(source, AL_PITCH, PITCH(pitch)); + + if(!reuse_chan) + alSourcef(source, AL_SEC_OFFSET, 0.f); + else + { + if((chanflags&SNDF_ABSTIME)) + alSourcef(source, AL_SEC_OFFSET, reuse_chan->StartTime.Lo/1000.f); + else + { + // FIXME: set offset based on the current time and the StartTime + alSourcef(source, AL_SEC_OFFSET, 0.f); + } + } + if(getALError() != AL_NO_ERROR) + return NULL; + + alSourcei(source, AL_BUFFER, buffer); + if((chanflags&SNDF_NOPAUSE) || !SFXPaused) + alSourcePlay(source); + if(getALError() != AL_NO_ERROR) + { + alSourcei(source, AL_BUFFER, 0); + getALError(); + return NULL; + } + + if(!(chanflags&SNDF_NOREVERB)) + ReverbSfx.Push(source); + if(!(chanflags&SNDF_NOPAUSE)) + PausableSfx.Push(source); + SfxGroup.Push(source); + FreeSfx.Pop(); + + FISoundChannel *chan = reuse_chan; + if(!chan) chan = S_GetChannel(MAKE_PTRID(source)); + else chan->SysChannel = MAKE_PTRID(source); + + chan->Rolloff.RolloffType = ROLLOFF_Log; + chan->Rolloff.RolloffFactor = 0.f; + chan->Rolloff.MinDistance = 1.f; + chan->DistanceScale = 1.f; + chan->DistanceSqr = 0.f; + chan->ManualRolloff = false; + + return chan; +} + +FISoundChannel *OpenALSoundRenderer::StartSound3D(SoundHandle sfx, SoundListener *listener, float vol, + FRolloffInfo *rolloff, float distscale, int pitch, int priority, const FVector3 &pos, const FVector3 &vel, + int channum, int chanflags, FISoundChannel *reuse_chan) +{ + float dist_sqr = (float)(pos - listener->position).LengthSquared(); + + if(FreeSfx.Size() == 0) + { + FSoundChan *lowest = FindLowestChannel(); + if(lowest) + { + if(lowest->Priority < priority || (lowest->Priority == priority && + lowest->DistanceSqr > dist_sqr)) + StopChannel(lowest); + } + if(FreeSfx.Size() == 0) + return NULL; + } + + bool manualRolloff = true; + ALuint buffer = GET_PTRID(sfx.data); + ALuint source = FreeSfx.Last(); + if(rolloff->RolloffType == ROLLOFF_Log) + { + if(AL.EXT_source_distance_model) + alSourcei(source, AL_DISTANCE_MODEL, AL_INVERSE_DISTANCE); + alSourcef(source, AL_REFERENCE_DISTANCE, rolloff->MinDistance/distscale); + alSourcef(source, AL_MAX_DISTANCE, (1000.f+rolloff->MinDistance)/distscale); + alSourcef(source, AL_ROLLOFF_FACTOR, rolloff->RolloffFactor); + manualRolloff = false; + } + else if(rolloff->RolloffType == ROLLOFF_Linear && AL.EXT_source_distance_model) + { + alSourcei(source, AL_DISTANCE_MODEL, AL_LINEAR_DISTANCE); + alSourcef(source, AL_REFERENCE_DISTANCE, rolloff->MinDistance/distscale); + alSourcef(source, AL_MAX_DISTANCE, rolloff->MaxDistance/distscale); + alSourcef(source, AL_ROLLOFF_FACTOR, 1.f); + manualRolloff = false; + } + if(manualRolloff) + { + // How manual rolloff works: + // + // If a sound is using Custom or Doom style rolloff, or Linear style + // when AL_EXT_source_distance_model is not supported, we have to play + // around a bit to get appropriate distance attenation. What we do is + // calculate the attenuation that should be applied, then given an + // Inverse Distance rolloff model with OpenAL, reverse the calculation + // to get the distance needed for that much attenuation. The Inverse + // Distance calculation is: + // + // Gain = MinDist / (MinDist + RolloffFactor*(Distance - MinDist)) + // + // Thus, the reverse is: + // + // Distance = (MinDist/Gain - MinDist)/RolloffFactor + MinDist + // + // This can be simplified by using a MinDist and RolloffFactor of 1, + // which makes it: + // + // Distance = 1.0f/Gain; + // + // The source position is then set that many units away from the + // listener position, and OpenAL takes care of the rest. + if(AL.EXT_source_distance_model) + alSourcei(source, AL_DISTANCE_MODEL, AL_INVERSE_DISTANCE); + alSourcef(source, AL_REFERENCE_DISTANCE, 1.f); + alSourcef(source, AL_MAX_DISTANCE, 100000.f); + alSourcef(source, AL_ROLLOFF_FACTOR, 1.f); + + FVector3 dir = pos - listener->position; + if(dir.DoesNotApproximatelyEqual(FVector3(0.f, 0.f, 0.f))) + { + float gain = GetRolloff(rolloff, sqrt(dist_sqr) * distscale); + dir.Resize((gain > 0.00001f) ? 1.f/gain : 100000.f); + } + if((chanflags&SNDF_AREA) && dist_sqr < AREA_SOUND_RADIUS*AREA_SOUND_RADIUS) + { + FVector3 amb(0.f, !(dir.Y>=0.f) ? -1.f : 1.f, 0.f); + float a = sqrt(dist_sqr) / AREA_SOUND_RADIUS; + dir = amb + (dir-amb)*a; + } + dir += listener->position; + + alSource3f(source, AL_POSITION, dir[0], dir[1], -dir[2]); + } + else if((chanflags&SNDF_AREA) && dist_sqr < AREA_SOUND_RADIUS*AREA_SOUND_RADIUS) + { + FVector3 dir = pos - listener->position; + + float mindist = rolloff->MinDistance/distscale; + FVector3 amb(0.f, !(dir.Y>=0.f) ? -mindist : mindist, 0.f); + float a = sqrt(dist_sqr) / AREA_SOUND_RADIUS; + dir = amb + (dir-amb)*a; + + dir += listener->position; + alSource3f(source, AL_POSITION, dir[0], dir[1], -dir[2]); + } + else + alSource3f(source, AL_POSITION, pos[0], pos[1], -pos[2]); + alSource3f(source, AL_VELOCITY, vel[0], vel[1], -vel[2]); + alSource3f(source, AL_DIRECTION, 0.f, 0.f, 0.f); + + alSourcei(source, AL_SOURCE_RELATIVE, AL_FALSE); + alSourcei(source, AL_LOOPING, (chanflags&SNDF_LOOP) ? AL_TRUE : AL_FALSE); + + alSourcef(source, AL_MAX_GAIN, SfxVolume); + alSourcef(source, AL_GAIN, SfxVolume); + + if(EnvSlot) + { + if(!(chanflags&SNDF_NOREVERB)) + { + alSourcei(source, AL_DIRECT_FILTER, EnvFilters[0]); + alSource3i(source, AL_AUXILIARY_SEND_FILTER, EnvSlot, 0, EnvFilters[1]); + } + else + { + alSourcei(source, AL_DIRECT_FILTER, AL_FILTER_NULL); + alSource3i(source, AL_AUXILIARY_SEND_FILTER, 0, 0, AL_FILTER_NULL); + } + alSourcef(source, AL_ROOM_ROLLOFF_FACTOR, 0.f); + } + if(WasInWater && !(chanflags&SNDF_NOREVERB)) + alSourcef(source, AL_PITCH, PITCH(pitch)*PITCH_MULT); + else + alSourcef(source, AL_PITCH, PITCH(pitch)); + + if(!reuse_chan) + alSourcef(source, AL_SEC_OFFSET, 0.f); + else + { + if((chanflags&SNDF_ABSTIME)) + alSourcef(source, AL_SEC_OFFSET, reuse_chan->StartTime.Lo/1000.f); + else + { + // FIXME: set offset based on the current time and the StartTime + alSourcef(source, AL_SAMPLE_OFFSET, 0.f); + } + } + if(getALError() != AL_NO_ERROR) + return NULL; + + alSourcei(source, AL_BUFFER, buffer); + if((chanflags&SNDF_NOPAUSE) || !SFXPaused) + alSourcePlay(source); + if(getALError() != AL_NO_ERROR) + { + alSourcei(source, AL_BUFFER, 0); + getALError(); + return NULL; + } + + if(!(chanflags&SNDF_NOREVERB)) + ReverbSfx.Push(source); + if(!(chanflags&SNDF_NOPAUSE)) + PausableSfx.Push(source); + SfxGroup.Push(source); + FreeSfx.Pop(); + + FISoundChannel *chan = reuse_chan; + if(!chan) chan = S_GetChannel(MAKE_PTRID(source)); + else chan->SysChannel = MAKE_PTRID(source); + + chan->Rolloff = *rolloff; + chan->DistanceScale = distscale; + chan->DistanceSqr = dist_sqr; + chan->ManualRolloff = manualRolloff; + + return chan; +} + +void OpenALSoundRenderer::ChannelVolume(FISoundChannel *chan, float volume) +{ + if(chan == NULL || chan->SysChannel == NULL) + return; + + alDeferUpdatesSOFT(); + + ALuint source = GET_PTRID(chan->SysChannel); + alSourcef(source, AL_GAIN, SfxVolume * volume); +} + +void OpenALSoundRenderer::StopChannel(FISoundChannel *chan) +{ + if(chan == NULL || chan->SysChannel == NULL) + return; + + ALuint source = GET_PTRID(chan->SysChannel); + // Release first, so it can be properly marked as evicted if it's being + // forcefully killed + S_ChannelEnded(chan); + + alSourceRewind(source); + alSourcei(source, AL_BUFFER, 0); + getALError(); + + uint32 i; + if((i=PausableSfx.Find(source)) < PausableSfx.Size()) + PausableSfx.Delete(i); + if((i=ReverbSfx.Find(source)) < ReverbSfx.Size()) + ReverbSfx.Delete(i); + + SfxGroup.Delete(SfxGroup.Find(source)); + FreeSfx.Push(source); +} + +unsigned int OpenALSoundRenderer::GetPosition(FISoundChannel *chan) +{ + if(chan == NULL || chan->SysChannel == NULL) + return 0; + + ALint pos; + alGetSourcei(GET_PTRID(chan->SysChannel), AL_SAMPLE_OFFSET, &pos); + if(getALError() == AL_NO_ERROR) + return pos; + return 0; +} + + +void OpenALSoundRenderer::SetSfxPaused(bool paused, int slot) +{ + int oldslots = SFXPaused; + + if(paused) + { + SFXPaused |= 1 << slot; + if(oldslots == 0 && PausableSfx.Size() > 0) + { + alSourcePausev(PausableSfx.Size(), &PausableSfx[0]); + getALError(); + PurgeStoppedSources(); + } + } + else + { + SFXPaused &= ~(1 << slot); + if(SFXPaused == 0 && oldslots != 0 && PausableSfx.Size() > 0) + { + alSourcePlayv(PausableSfx.Size(), &PausableSfx[0]); + getALError(); + } + } +} + +void OpenALSoundRenderer::SetInactive(SoundRenderer::EInactiveState state) +{ + switch(state) + { + case SoundRenderer::INACTIVE_Active: + alListenerf(AL_GAIN, 1.0f); + break; + + /* FIXME: This doesn't stop anything. */ + case SoundRenderer::INACTIVE_Complete: + case SoundRenderer::INACTIVE_Mute: + alListenerf(AL_GAIN, 0.0f); + break; + } +} + +void OpenALSoundRenderer::Sync(bool sync) +{ + if(sync) + { + if(SfxGroup.Size() > 0) + { + alSourcePausev(SfxGroup.Size(), &SfxGroup[0]); + getALError(); + PurgeStoppedSources(); + } + } + else + { + // Might already be something to handle this; basically, get a vector + // of all values in SfxGroup that are not also in PausableSfx (when + // SFXPaused is non-0). + TArray toplay = SfxGroup; + if(SFXPaused) + { + uint32 i = 0; + while(i < toplay.Size()) + { + uint32 p = PausableSfx.Find(toplay[i]); + if(p < PausableSfx.Size()) + toplay.Delete(i); + else + i++; + } + } + if(toplay.Size() > 0) + { + alSourcePlayv(toplay.Size(), &toplay[0]); + getALError(); + } + } +} + +void OpenALSoundRenderer::UpdateSoundParams3D(SoundListener *listener, FISoundChannel *chan, bool areasound, const FVector3 &pos, const FVector3 &vel) +{ + if(chan == NULL || chan->SysChannel == NULL) + return; + + alDeferUpdatesSOFT(); + + FVector3 dir = pos - listener->position; + chan->DistanceSqr = (float)dir.LengthSquared(); + + if(chan->ManualRolloff) + { + if(dir.DoesNotApproximatelyEqual(FVector3(0.f, 0.f, 0.f))) + { + float gain = GetRolloff(&chan->Rolloff, sqrt(chan->DistanceSqr) * chan->DistanceScale); + dir.Resize((gain > 0.00001f) ? 1.f/gain : 100000.f); + } + if(areasound && chan->DistanceSqr < AREA_SOUND_RADIUS*AREA_SOUND_RADIUS) + { + FVector3 amb(0.f, !(dir.Y>=0.f) ? -1.f : 1.f, 0.f); + float a = sqrt(chan->DistanceSqr) / AREA_SOUND_RADIUS; + dir = amb + (dir-amb)*a; + } + } + else if(areasound && chan->DistanceSqr < AREA_SOUND_RADIUS*AREA_SOUND_RADIUS) + { + float mindist = chan->Rolloff.MinDistance / chan->DistanceScale; + FVector3 amb(0.f, !(dir.Y>=0.f) ? -mindist : mindist, 0.f); + float a = sqrt(chan->DistanceSqr) / AREA_SOUND_RADIUS; + dir = amb + (dir-amb)*a; + } + dir += listener->position; + + ALuint source = GET_PTRID(chan->SysChannel); + alSource3f(source, AL_POSITION, dir[0], dir[1], -dir[2]); + alSource3f(source, AL_VELOCITY, vel[0], vel[1], -vel[2]); + getALError(); +} + +void OpenALSoundRenderer::UpdateListener(SoundListener *listener) +{ + if(!listener->valid) + return; + + alDeferUpdatesSOFT(); + + float angle = listener->angle; + ALfloat orient[6]; + // forward + orient[0] = cos(angle); + orient[1] = 0.f; + orient[2] = -sin(angle); + // up + orient[3] = 0.f; + orient[4] = 1.f; + orient[5] = 0.f; + + alListenerfv(AL_ORIENTATION, orient); + alListener3f(AL_POSITION, listener->position.X, + listener->position.Y, + -listener->position.Z); + alListener3f(AL_VELOCITY, listener->velocity.X, + listener->velocity.Y, + -listener->velocity.Z); + getALError(); + + const ReverbContainer *env = ForcedEnvironment; + if(!env) + { + env = listener->Environment; + if(!env) + env = DefaultEnvironments[0]; + } + if(env != PrevEnvironment || env->Modified) + { + PrevEnvironment = env; + DPrintf("Reverb Environment %s\n", env->Name); + + if(EnvSlot != 0) + LoadReverb(env); + + const_cast(env)->Modified = false; + } + + // NOTE: Moving into and out of water will undo pitch variations on sounds. + if(listener->underwater || env->SoftwareWater) + { + if(!WasInWater) + { + WasInWater = true; + + if(EnvSlot != 0 && *snd_waterreverb) + { + // Find the "Underwater" reverb environment + env = S_FindEnvironment(0x1600); + LoadReverb(env ? env : DefaultEnvironments[0]); + + alFilterf(EnvFilters[0], AL_LOWPASS_GAIN, 1.f); + alFilterf(EnvFilters[0], AL_LOWPASS_GAINHF, 0.125f); + alFilterf(EnvFilters[1], AL_LOWPASS_GAIN, 1.f); + alFilterf(EnvFilters[1], AL_LOWPASS_GAINHF, 1.f); + + // Apply the updated filters on the sources + for(uint32 i = 0;i < ReverbSfx.Size();++i) + { + alSourcei(ReverbSfx[i], AL_DIRECT_FILTER, EnvFilters[0]); + alSource3i(ReverbSfx[i], AL_AUXILIARY_SEND_FILTER, EnvSlot, 0, EnvFilters[1]); + } + } + + for(uint32 i = 0;i < ReverbSfx.Size();++i) + alSourcef(ReverbSfx[i], AL_PITCH, PITCH_MULT); + getALError(); + } + } + else if(WasInWater) + { + WasInWater = false; + + if(EnvSlot != 0) + { + LoadReverb(env); + + alFilterf(EnvFilters[0], AL_LOWPASS_GAIN, 1.f); + alFilterf(EnvFilters[0], AL_LOWPASS_GAINHF, 1.f); + alFilterf(EnvFilters[1], AL_LOWPASS_GAIN, 1.f); + alFilterf(EnvFilters[1], AL_LOWPASS_GAINHF, 1.f); + for(uint32 i = 0;i < ReverbSfx.Size();++i) + { + alSourcei(ReverbSfx[i], AL_DIRECT_FILTER, EnvFilters[0]); + alSource3i(ReverbSfx[i], AL_AUXILIARY_SEND_FILTER, EnvSlot, 0, EnvFilters[1]); + } + } + + for(uint32 i = 0;i < ReverbSfx.Size();++i) + alSourcef(ReverbSfx[i], AL_PITCH, 1.f); + getALError(); + } +} + +void OpenALSoundRenderer::UpdateSounds() +{ + alProcessUpdatesSOFT(); + + if(ALC.EXT_disconnect) + { + ALCint connected = ALC_TRUE; + alcGetIntegerv(Device, ALC_CONNECTED, 1, &connected); + if(connected == ALC_FALSE) + { + Printf("Sound device disconnected; restarting...\n"); + static char snd_reset[] = "snd_reset"; + AddCommandString(snd_reset); + return; + } + } + + PurgeStoppedSources(); +} + +void OpenALSoundRenderer::UpdateMusic() +{ + // For some reason this isn't being called? + for(uint32 i = 0;i < Streams.Size();++i) + Streams[i]->IsEnded(); +} + +bool OpenALSoundRenderer::IsValid() +{ + return Device != NULL; +} + +void OpenALSoundRenderer::MarkStartTime(FISoundChannel *chan) +{ + // FIXME: Get current time (preferably from the audio clock, but the system + // time will have to do) + chan->StartTime.AsOne = 0; +} + +float OpenALSoundRenderer::GetAudibility(FISoundChannel *chan) +{ + if(chan == NULL || chan->SysChannel == NULL) + return 0.f; + + ALuint source = GET_PTRID(chan->SysChannel); + ALfloat volume = 0.f; + + alGetSourcef(source, AL_GAIN, &volume); + getALError(); + + volume *= GetRolloff(&chan->Rolloff, sqrt(chan->DistanceSqr) * chan->DistanceScale); + return volume; +} + + +void OpenALSoundRenderer::PrintStatus() +{ + Printf("Output device: "TEXTCOLOR_ORANGE"%s\n", alcGetString(Device, ALC_DEVICE_SPECIFIER)); + getALCError(Device); + + ALCint frequency, major, minor, mono, stereo; + alcGetIntegerv(Device, ALC_FREQUENCY, 1, &frequency); + alcGetIntegerv(Device, ALC_MAJOR_VERSION, 1, &major); + alcGetIntegerv(Device, ALC_MINOR_VERSION, 1, &minor); + alcGetIntegerv(Device, ALC_MONO_SOURCES, 1, &mono); + alcGetIntegerv(Device, ALC_STEREO_SOURCES, 1, &stereo); + if(getALCError(Device) == AL_NO_ERROR) + { + Printf("Device sample rate: "TEXTCOLOR_BLUE"%d"TEXTCOLOR_NORMAL"hz\n", frequency); + Printf("ALC Version: "TEXTCOLOR_BLUE"%d.%d\n", major, minor); + Printf("ALC Extensions: "TEXTCOLOR_ORANGE"%s\n", alcGetString(Device, ALC_EXTENSIONS)); + Printf("Available sources: "TEXTCOLOR_BLUE"%d"TEXTCOLOR_NORMAL" ("TEXTCOLOR_BLUE"%d"TEXTCOLOR_NORMAL" mono, "TEXTCOLOR_BLUE"%d"TEXTCOLOR_NORMAL" stereo)\n", mono+stereo, mono, stereo); + } + if(!alcIsExtensionPresent(Device, "ALC_EXT_EFX")) + Printf("EFX not found\n"); + else + { + ALCint sends; + alcGetIntegerv(Device, ALC_EFX_MAJOR_VERSION, 1, &major); + alcGetIntegerv(Device, ALC_EFX_MINOR_VERSION, 1, &minor); + alcGetIntegerv(Device, ALC_MAX_AUXILIARY_SENDS, 1, &sends); + if(getALCError(Device) == AL_NO_ERROR) + { + Printf("EFX Version: "TEXTCOLOR_BLUE"%d.%d\n", major, minor); + Printf("Auxiliary sends: "TEXTCOLOR_BLUE"%d\n", sends); + } + } + Printf("Vendor: "TEXTCOLOR_ORANGE"%s\n", alGetString(AL_VENDOR)); + Printf("Renderer: "TEXTCOLOR_ORANGE"%s\n", alGetString(AL_RENDERER)); + Printf("Version: "TEXTCOLOR_ORANGE"%s\n", alGetString(AL_VERSION)); + Printf("Extensions: "TEXTCOLOR_ORANGE"%s\n", alGetString(AL_EXTENSIONS)); + getALError(); +} + +FString OpenALSoundRenderer::GatherStats() +{ + ALCint updates = 1; + alcGetIntegerv(Device, ALC_REFRESH, 1, &updates); + getALCError(Device); + + uint32 total = Sources.Size(); + uint32 used = SfxGroup.Size()+Streams.Size(); + uint32 unused = FreeSfx.Size(); + + FString out; + out.Format("%u sources ("TEXTCOLOR_YELLOW"%u"TEXTCOLOR_NORMAL" active, "TEXTCOLOR_YELLOW"%u"TEXTCOLOR_NORMAL" free), Update interval: "TEXTCOLOR_YELLOW"%d"TEXTCOLOR_NORMAL"ms", + total, used, unused, 1000/updates); + return out; +} + +void OpenALSoundRenderer::PrintDriversList() +{ + const ALCchar *drivers = (alcIsExtensionPresent(NULL, "ALC_ENUMERATE_ALL_EXT") ? + alcGetString(NULL, ALC_ALL_DEVICES_SPECIFIER) : + alcGetString(NULL, ALC_DEVICE_SPECIFIER)); + if(drivers == NULL) + { + Printf(TEXTCOLOR_YELLOW"Failed to retrieve device list: %s\n", alcGetString(NULL, alcGetError(NULL))); + return; + } + + const ALCchar *current = NULL; + if(alcIsExtensionPresent(Device, "ALC_ENUMERATE_ALL_EXT")) + current = alcGetString(Device, ALC_ALL_DEVICES_SPECIFIER); + if(alcGetError(Device) != ALC_NO_ERROR || !current) + current = alcGetString(Device, ALC_DEVICE_SPECIFIER); + if(current == NULL) + { + Printf(TEXTCOLOR_YELLOW"Failed to retrieve device name: %s\n", alcGetString(Device, alcGetError(Device))); + return; + } + + Printf("%c%s%2d. %s\n", ' ', ((strcmp(snd_aldevice, "Default") == 0) ? TEXTCOLOR_BOLD : ""), 0, + "Default"); + for(int i = 1;*drivers;i++) + { + Printf("%c%s%2d. %s\n", ((strcmp(current, drivers)==0) ? '*' : ' '), + ((strcmp(*snd_aldevice, drivers)==0) ? TEXTCOLOR_BOLD : ""), i, + drivers); + drivers += strlen(drivers)+1; + } +} + +void OpenALSoundRenderer::PurgeStoppedSources() +{ + // Release channels that are stopped + for(uint32 i = 0;i < SfxGroup.Size();++i) + { + ALuint src = SfxGroup[i]; + ALint state = AL_INITIAL; + alGetSourcei(src, AL_SOURCE_STATE, &state); + if(state == AL_INITIAL || state == AL_PLAYING || state == AL_PAUSED) + continue; + + FSoundChan *schan = Channels; + while(schan) + { + if(schan->SysChannel != NULL && src == GET_PTRID(schan->SysChannel)) + { + StopChannel(schan); + break; + } + schan = schan->NextChan; + } + } + getALError(); +} + +void OpenALSoundRenderer::LoadReverb(const ReverbContainer *env) +{ + ALuint *envReverb = EnvEffects.CheckKey(env->ID); + bool doLoad = (env->Modified || !envReverb); + + if(!envReverb) + { + bool ok = false; + + envReverb = &EnvEffects.Insert(env->ID, 0); + alGenEffects(1, envReverb); + if(getALError() == AL_NO_ERROR) + { + alEffecti(*envReverb, AL_EFFECT_TYPE, AL_EFFECT_EAXREVERB); + ok = (alGetError() == AL_NO_ERROR); + if(!ok) + { + alEffecti(*envReverb, AL_EFFECT_TYPE, AL_EFFECT_REVERB); + ok = (alGetError() == AL_NO_ERROR); + } + if(!ok) + { + alEffecti(*envReverb, AL_EFFECT_TYPE, AL_EFFECT_NULL); + ok = (alGetError() == AL_NO_ERROR); + } + if(!ok) + { + alDeleteEffects(1, envReverb); + getALError(); + } + } + if(!ok) + { + *envReverb = 0; + doLoad = false; + } + } + + if(doLoad) + { + const REVERB_PROPERTIES &props = env->Properties; + ALint type = AL_EFFECT_NULL; + + alGetEffecti(*envReverb, AL_EFFECT_TYPE, &type); +#define mB2Gain(x) ((float)pow(10., (x)/2000.)) + if(type == AL_EFFECT_EAXREVERB) + { + ALfloat reflectpan[3] = { props.ReflectionsPan0, + props.ReflectionsPan1, + props.ReflectionsPan2 }; + ALfloat latepan[3] = { props.ReverbPan0, props.ReverbPan1, + props.ReverbPan2 }; +#undef SETPARAM +#define SETPARAM(e,t,v) alEffectf((e), AL_EAXREVERB_##t, clamp((v), AL_EAXREVERB_MIN_##t, AL_EAXREVERB_MAX_##t)) + SETPARAM(*envReverb, DIFFUSION, props.EnvDiffusion); + SETPARAM(*envReverb, DENSITY, powf(props.EnvSize, 3.0f) * 0.0625f); + SETPARAM(*envReverb, GAIN, mB2Gain(props.Room)); + SETPARAM(*envReverb, GAINHF, mB2Gain(props.RoomHF)); + SETPARAM(*envReverb, GAINLF, mB2Gain(props.RoomLF)); + SETPARAM(*envReverb, DECAY_TIME, props.DecayTime); + SETPARAM(*envReverb, DECAY_HFRATIO, props.DecayHFRatio); + SETPARAM(*envReverb, DECAY_LFRATIO, props.DecayLFRatio); + SETPARAM(*envReverb, REFLECTIONS_GAIN, mB2Gain(props.Reflections)); + SETPARAM(*envReverb, REFLECTIONS_DELAY, props.ReflectionsDelay); + alEffectfv(*envReverb, AL_EAXREVERB_REFLECTIONS_PAN, reflectpan); + SETPARAM(*envReverb, LATE_REVERB_GAIN, mB2Gain(props.Reverb)); + SETPARAM(*envReverb, LATE_REVERB_DELAY, props.ReverbDelay); + alEffectfv(*envReverb, AL_EAXREVERB_LATE_REVERB_PAN, latepan); + SETPARAM(*envReverb, ECHO_TIME, props.EchoTime); + SETPARAM(*envReverb, ECHO_DEPTH, props.EchoDepth); + SETPARAM(*envReverb, MODULATION_TIME, props.ModulationTime); + SETPARAM(*envReverb, MODULATION_DEPTH, props.ModulationDepth); + SETPARAM(*envReverb, AIR_ABSORPTION_GAINHF, mB2Gain(props.AirAbsorptionHF)); + SETPARAM(*envReverb, HFREFERENCE, props.HFReference); + SETPARAM(*envReverb, LFREFERENCE, props.LFReference); + SETPARAM(*envReverb, ROOM_ROLLOFF_FACTOR, props.RoomRolloffFactor); + alEffecti(*envReverb, AL_EAXREVERB_DECAY_HFLIMIT, + (props.Flags&REVERB_FLAGS_DECAYHFLIMIT)?AL_TRUE:AL_FALSE); +#undef SETPARAM + } + else if(type == AL_EFFECT_REVERB) + { +#define SETPARAM(e,t,v) alEffectf((e), AL_REVERB_##t, clamp((v), AL_REVERB_MIN_##t, AL_REVERB_MAX_##t)) + SETPARAM(*envReverb, DIFFUSION, props.EnvDiffusion); + SETPARAM(*envReverb, DENSITY, powf(props.EnvSize, 3.0f) * 0.0625f); + SETPARAM(*envReverb, GAIN, mB2Gain(props.Room)); + SETPARAM(*envReverb, GAINHF, mB2Gain(props.RoomHF)); + SETPARAM(*envReverb, DECAY_TIME, props.DecayTime); + SETPARAM(*envReverb, DECAY_HFRATIO, props.DecayHFRatio); + SETPARAM(*envReverb, REFLECTIONS_GAIN, mB2Gain(props.Reflections)); + SETPARAM(*envReverb, REFLECTIONS_DELAY, props.ReflectionsDelay); + SETPARAM(*envReverb, LATE_REVERB_GAIN, mB2Gain(props.Reverb)); + SETPARAM(*envReverb, LATE_REVERB_DELAY, props.ReverbDelay); + SETPARAM(*envReverb, AIR_ABSORPTION_GAINHF, mB2Gain(props.AirAbsorptionHF)); + SETPARAM(*envReverb, ROOM_ROLLOFF_FACTOR, props.RoomRolloffFactor); + alEffecti(*envReverb, AL_REVERB_DECAY_HFLIMIT, + (props.Flags&REVERB_FLAGS_DECAYHFLIMIT)?AL_TRUE:AL_FALSE); +#undef SETPARAM + } +#undef mB2Gain + } + + alAuxiliaryEffectSloti(EnvSlot, AL_EFFECTSLOT_EFFECT, *envReverb); + getALError(); +} + +FSoundChan *OpenALSoundRenderer::FindLowestChannel() +{ + FSoundChan *schan = Channels; + FSoundChan *lowest = NULL; + while(schan) + { + if(schan->SysChannel != NULL) + { + if(!lowest || schan->Priority < lowest->Priority || + (schan->Priority == lowest->Priority && + schan->DistanceSqr > lowest->DistanceSqr)) + lowest = schan; + } + schan = schan->NextChan; + } + return lowest; +} + +#endif // NO_OPENAL diff --git a/src/sound/oalsound.h b/src/sound/oalsound.h new file mode 100644 index 0000000000..4f6bae3968 --- /dev/null +++ b/src/sound/oalsound.h @@ -0,0 +1,211 @@ +#ifndef OALSOUND_H +#define OALSOUND_H + +#include "i_sound.h" +#include "s_sound.h" +#include "menu/menu.h" + +#ifndef NO_OPENAL + +#include "al.h" +#include "alc.h" + +#ifndef ALC_ENUMERATE_ALL_EXT +#define ALC_ENUMERATE_ALL_EXT 1 +#define ALC_DEFAULT_ALL_DEVICES_SPECIFIER 0x1012 +#define ALC_ALL_DEVICES_SPECIFIER 0x1013 +#endif + +#ifndef ALC_EXT_disconnect +#define ALC_EXT_disconnect 1 +#define ALC_CONNECTED 0x313 +#endif + +#ifndef AL_EXT_source_distance_model +#define AL_EXT_source_distance_model 1 +#define AL_SOURCE_DISTANCE_MODEL 0x200 +#endif + +#ifndef AL_SOFT_loop_points +#define AL_SOFT_loop_points 1 +#define AL_LOOP_POINTS_SOFT 0x2015 +#endif + +#ifndef AL_EXT_float32 +#define AL_EXT_float32 1 +#define AL_FORMAT_MONO_FLOAT32 0x10010 +#define AL_FORMAT_STEREO_FLOAT32 0x10011 +#endif + +#ifndef AL_EXT_MCFORMATS +#define AL_EXT_MCFORMATS 1 +#define AL_FORMAT_QUAD8 0x1204 +#define AL_FORMAT_QUAD16 0x1205 +#define AL_FORMAT_QUAD32 0x1206 +#define AL_FORMAT_REAR8 0x1207 +#define AL_FORMAT_REAR16 0x1208 +#define AL_FORMAT_REAR32 0x1209 +#define AL_FORMAT_51CHN8 0x120A +#define AL_FORMAT_51CHN16 0x120B +#define AL_FORMAT_51CHN32 0x120C +#define AL_FORMAT_61CHN8 0x120D +#define AL_FORMAT_61CHN16 0x120E +#define AL_FORMAT_61CHN32 0x120F +#define AL_FORMAT_71CHN8 0x1210 +#define AL_FORMAT_71CHN16 0x1211 +#define AL_FORMAT_71CHN32 0x1212 +#endif + +#include "efx.h" + + +class OpenALSoundStream; + +class OpenALSoundRenderer : public SoundRenderer +{ +public: + OpenALSoundRenderer(); + virtual ~OpenALSoundRenderer(); + + virtual void SetSfxVolume(float volume); + virtual void SetMusicVolume(float volume); + virtual SoundHandle LoadSound(BYTE *sfxdata, int length); + virtual SoundHandle LoadSoundRaw(BYTE *sfxdata, int length, int frequency, int channels, int bits, int loopstart, int loopend = -1); + virtual void UnloadSound(SoundHandle sfx); + virtual unsigned int GetMSLength(SoundHandle sfx); + virtual unsigned int GetSampleLength(SoundHandle sfx); + virtual float GetOutputRate(); + + // Streaming sounds. + virtual SoundStream *CreateStream(SoundStreamCallback callback, int buffbytes, int flags, int samplerate, void *userdata); + virtual SoundStream *OpenStream(FileReader *reader, int flags); + + // Starts a sound. + virtual FISoundChannel *StartSound(SoundHandle sfx, float vol, int pitch, int chanflags, FISoundChannel *reuse_chan); + virtual FISoundChannel *StartSound3D(SoundHandle sfx, SoundListener *listener, float vol, FRolloffInfo *rolloff, float distscale, int pitch, int priority, const FVector3 &pos, const FVector3 &vel, int channum, int chanflags, FISoundChannel *reuse_chan); + + // Changes a channel's volume. + virtual void ChannelVolume(FISoundChannel *chan, float volume); + + // Stops a sound channel. + virtual void StopChannel(FISoundChannel *chan); + + // Returns position of sound on this channel, in samples. + virtual unsigned int GetPosition(FISoundChannel *chan); + + // Synchronizes following sound startups. + virtual void Sync(bool sync); + + // Pauses or resumes all sound effect channels. + virtual void SetSfxPaused(bool paused, int slot); + + // Pauses or resumes *every* channel, including environmental reverb. + virtual void SetInactive(SoundRenderer::EInactiveState inactive); + + // Updates the volume, separation, and pitch of a sound channel. + virtual void UpdateSoundParams3D(SoundListener *listener, FISoundChannel *chan, bool areasound, const FVector3 &pos, const FVector3 &vel); + + virtual void UpdateListener(SoundListener *); + virtual void UpdateSounds(); + virtual void UpdateMusic(); + + virtual void MarkStartTime(FISoundChannel*); + virtual float GetAudibility(FISoundChannel*); + + + virtual bool IsValid(); + virtual void PrintStatus(); + virtual void PrintDriversList(); + virtual FString GatherStats(); + +private: + struct { + bool EXT_EFX; + bool EXT_disconnect; + } ALC; + struct { + bool EXT_source_distance_model; + bool SOFT_deferred_updates; + bool SOFT_loop_points; + } AL; + + // EFX Extension function pointer variables. Loaded after context creation + // if EFX is supported. These pointers may be context- or device-dependant, + // thus can't be static + // Effect objects + LPALGENEFFECTS alGenEffects; + LPALDELETEEFFECTS alDeleteEffects; + LPALISEFFECT alIsEffect; + LPALEFFECTI alEffecti; + LPALEFFECTIV alEffectiv; + LPALEFFECTF alEffectf; + LPALEFFECTFV alEffectfv; + LPALGETEFFECTI alGetEffecti; + LPALGETEFFECTIV alGetEffectiv; + LPALGETEFFECTF alGetEffectf; + LPALGETEFFECTFV alGetEffectfv; + // Filter objects + LPALGENFILTERS alGenFilters; + LPALDELETEFILTERS alDeleteFilters; + LPALISFILTER alIsFilter; + LPALFILTERI alFilteri; + LPALFILTERIV alFilteriv; + LPALFILTERF alFilterf; + LPALFILTERFV alFilterfv; + LPALGETFILTERI alGetFilteri; + LPALGETFILTERIV alGetFilteriv; + LPALGETFILTERF alGetFilterf; + LPALGETFILTERFV alGetFilterfv; + // Auxiliary slot objects + LPALGENAUXILIARYEFFECTSLOTS alGenAuxiliaryEffectSlots; + LPALDELETEAUXILIARYEFFECTSLOTS alDeleteAuxiliaryEffectSlots; + LPALISAUXILIARYEFFECTSLOT alIsAuxiliaryEffectSlot; + LPALAUXILIARYEFFECTSLOTI alAuxiliaryEffectSloti; + LPALAUXILIARYEFFECTSLOTIV alAuxiliaryEffectSlotiv; + LPALAUXILIARYEFFECTSLOTF alAuxiliaryEffectSlotf; + LPALAUXILIARYEFFECTSLOTFV alAuxiliaryEffectSlotfv; + LPALGETAUXILIARYEFFECTSLOTI alGetAuxiliaryEffectSloti; + LPALGETAUXILIARYEFFECTSLOTIV alGetAuxiliaryEffectSlotiv; + LPALGETAUXILIARYEFFECTSLOTF alGetAuxiliaryEffectSlotf; + LPALGETAUXILIARYEFFECTSLOTFV alGetAuxiliaryEffectSlotfv; + + ALvoid (AL_APIENTRY*alDeferUpdatesSOFT)(void); + ALvoid (AL_APIENTRY*alProcessUpdatesSOFT)(void); + + void LoadReverb(const ReverbContainer *env); + void PurgeStoppedSources(); + static FSoundChan *FindLowestChannel(); + + ALCdevice *Device; + ALCcontext *Context; + + TArray Sources; + + ALfloat SfxVolume; + ALfloat MusicVolume; + + int SFXPaused; + TArray FreeSfx; + TArray PausableSfx; + TArray ReverbSfx; + TArray SfxGroup; + + const ReverbContainer *PrevEnvironment; + + typedef TMap EffectMap; + typedef TMapIterator EffectMapIter; + ALuint EnvSlot; + ALuint EnvFilters[2]; + EffectMap EnvEffects; + + bool WasInWater; + + TArray Streams; + friend class OpenALSoundStream; + + ALCdevice *InitDevice(); +}; + +#endif // NO_OPENAL + +#endif diff --git a/src/sound/sndfile_decoder.cpp b/src/sound/sndfile_decoder.cpp new file mode 100644 index 0000000000..a5b10faf32 --- /dev/null +++ b/src/sound/sndfile_decoder.cpp @@ -0,0 +1,152 @@ +#ifdef _WIN32 +#define WIN32_LEAN_AND_MEAN +#include +#define USE_WINDOWS_DWORD +#endif +#include "except.h" + +#include "sndfile_decoder.h" +#include "templates.h" +#include "files.h" +#include "xs_Float.h" + +#ifdef HAVE_SNDFILE + +sf_count_t SndFileDecoder::file_get_filelen(void *user_data) +{ + FileReader *reader = reinterpret_cast(user_data)->Reader; + return reader->GetLength(); +} + +sf_count_t SndFileDecoder::file_seek(sf_count_t offset, int whence, void *user_data) +{ + FileReader *reader = reinterpret_cast(user_data)->Reader; + + if(reader->Seek((long)offset, whence) != 0) + return -1; + return reader->Tell(); +} + +sf_count_t SndFileDecoder::file_read(void *ptr, sf_count_t count, void *user_data) +{ + FileReader *reader = reinterpret_cast(user_data)->Reader; + return reader->Read(ptr, (long)count); +} + +sf_count_t SndFileDecoder::file_write(const void *ptr, sf_count_t count, void *user_data) +{ + return -1; +} + +sf_count_t SndFileDecoder::file_tell(void *user_data) +{ + FileReader *reader = reinterpret_cast(user_data)->Reader; + return reader->Tell(); +} + + +SndFileDecoder::~SndFileDecoder() +{ + if(SndFile) + sf_close(SndFile); + SndFile = 0; +} + +bool SndFileDecoder::open(FileReader *reader) +{ + __try + { + SF_VIRTUAL_IO sfio = { file_get_filelen, file_seek, file_read, file_write, file_tell }; + + Reader = reader; + SndFile = sf_open_virtual(&sfio, SFM_READ, &SndInfo, this); + if (SndFile) + { + if (SndInfo.channels == 1 || SndInfo.channels == 2) + return true; + + sf_close(SndFile); + SndFile = 0; + } + } + __except (CheckException(GetExceptionCode())) + { + // this means that the delay loaded decoder DLL was not found. + } + return false; +} + +void SndFileDecoder::getInfo(int *samplerate, ChannelConfig *chans, SampleType *type) +{ + *samplerate = SndInfo.samplerate; + + if(SndInfo.channels == 2) + *chans = ChannelConfig_Stereo; + else + *chans = ChannelConfig_Mono; + + *type = SampleType_Int16; +} + +size_t SndFileDecoder::read(char *buffer, size_t bytes) +{ + short *out = (short*)buffer; + size_t frames = bytes / SndInfo.channels / 2; + size_t total = 0; + + // It seems libsndfile has a bug with converting float samples from Vorbis + // to the 16-bit shorts we use, which causes some PCM samples to overflow + // and wrap, creating static. So instead, read the samples as floats and + // convert to short ourselves. + // Use a loop to convert a handful of samples at a time, avoiding a heap + // allocation for temporary storage. 64 at a time works, though maybe it + // could be more. + while(total < frames) + { + size_t todo = MIN(frames-total, 64/SndInfo.channels); + float tmp[64]; + + size_t got = (size_t)sf_readf_float(SndFile, tmp, todo); + if(got < todo) frames = total + got; + + for(size_t i = 0;i < got*SndInfo.channels;i++) + *out++ = (short)xs_CRoundToInt(clamp(tmp[i] * 32767.f, -32768.f, 32767.f)); + total += got; + } + return total * SndInfo.channels * 2; +} + +TArray SndFileDecoder::readAll() +{ + if(SndInfo.frames <= 0) + return SoundDecoder::readAll(); + + int framesize = 2 * SndInfo.channels; + TArray output; + + output.Resize((unsigned)(SndInfo.frames * framesize)); + size_t got = read(&output[0], output.Size()); + output.Resize(got); + + return output; +} + +bool SndFileDecoder::seek(size_t ms_offset) +{ + size_t smp_offset = (size_t)((double)ms_offset / 1000. * SndInfo.samplerate); + if(sf_seek(SndFile, smp_offset, SEEK_SET) < 0) + return false; + return true; +} + +size_t SndFileDecoder::getSampleOffset() +{ + return (size_t)sf_seek(SndFile, 0, SEEK_CUR); +} + +size_t SndFileDecoder::getSampleLength() +{ + return (size_t)((SndInfo.frames > 0) ? SndInfo.frames : 0); +} + +#endif diff --git a/src/sound/sndfile_decoder.h b/src/sound/sndfile_decoder.h new file mode 100644 index 0000000000..f53f7e52a3 --- /dev/null +++ b/src/sound/sndfile_decoder.h @@ -0,0 +1,44 @@ +#ifndef SNDFILE_DECODER_H +#define SNDFILE_DECODER_H + +#include "i_soundinternal.h" + +#ifdef HAVE_SNDFILE + +#include "sndfile.h" + +struct SndFileDecoder : public SoundDecoder +{ + virtual void getInfo(int *samplerate, ChannelConfig *chans, SampleType *type); + + virtual size_t read(char *buffer, size_t bytes); + virtual TArray readAll(); + virtual bool seek(size_t ms_offset); + virtual size_t getSampleOffset(); + virtual size_t getSampleLength(); + + SndFileDecoder() : SndFile(0) { } + virtual ~SndFileDecoder(); + +protected: + virtual bool open(FileReader *reader); + +private: + SNDFILE *SndFile; + SF_INFO SndInfo; + + FileReader *Reader; + static sf_count_t file_get_filelen(void *user_data); + static sf_count_t file_seek(sf_count_t offset, int whence, void *user_data); + static sf_count_t file_read(void *ptr, sf_count_t count, void *user_data); + static sf_count_t file_write(const void *ptr, sf_count_t count, void *user_data); + static sf_count_t file_tell(void *user_data); + + // Make non-copyable + SndFileDecoder(const SndFileDecoder &rhs); + SndFileDecoder& operator=(const SndFileDecoder &rhs); +}; + +#endif + +#endif /* SNDFILE_DECODER_H */ diff --git a/src/tarray.h b/src/tarray.h index d32a688dfa..dac824800f 100644 --- a/src/tarray.h +++ b/src/tarray.h @@ -136,6 +136,17 @@ public: return Array[Count-1]; } + unsigned int Find(const T& item) const + { + unsigned int i; + for(i = 0;i < Count;++i) + { + if(Array[i] == item) + break; + } + return i; + } + unsigned int Push (const T &item) { Grow (1); diff --git a/src/w_wad.cpp b/src/w_wad.cpp index 2b3413460d..efeb38571c 100644 --- a/src/w_wad.cpp +++ b/src/w_wad.cpp @@ -1228,6 +1228,17 @@ FWadLump *FWadCollection::ReopenLumpNum (int lump) return new FWadLump(LumpInfo[lump].lump, true); } +FWadLump *FWadCollection::ReopenLumpNumNewFile (int lump) +{ + if ((unsigned)lump >= (unsigned)LumpInfo.Size()) + { + return NULL; + } + + return new FWadLump(lump, LumpInfo[lump].lump); +} + + //========================================================================== // // GetFileReader @@ -1417,6 +1428,34 @@ FWadLump::FWadLump(FResourceLump *lump, bool alwayscache) } } +FWadLump::FWadLump(int lumpnum, FResourceLump *lump) +: FileReader() +{ + FileReader *f = lump->GetReader(); + + if (f != NULL && f->GetFile() != NULL) + { + // Uncompressed lump in a file. For this we will have to open a new FILE, since we need it for streaming + int fileno = Wads.GetLumpFile(lumpnum); + const char *filename = Wads.GetWadFullName(fileno); + File = fopen(filename, "rb"); + if (File != NULL) + { + Length = lump->LumpSize; + StartPos = FilePos = lump->GetFileOffset(); + Lump = NULL; + CloseOnDestruct = true; + Seek(0, SEEK_SET); + return; + } + } + File = NULL; + Length = lump->LumpSize; + StartPos = FilePos = 0; + Lump = lump; + Lump->CacheLump(); +} + FWadLump::~FWadLump() { if (Lump != NULL) diff --git a/src/w_wad.h b/src/w_wad.h index 63912373e4..323f12df24 100644 --- a/src/w_wad.h +++ b/src/w_wad.h @@ -112,6 +112,7 @@ public: private: FWadLump (FResourceLump *Lump, bool alwayscache = false); + FWadLump(int lumpnum, FResourceLump *lump); FResourceLump *Lump; @@ -185,6 +186,7 @@ public: FWadLump OpenLumpNum (int lump); FWadLump OpenLumpName (const char *name) { return OpenLumpNum (GetNumForName (name)); } FWadLump *ReopenLumpNum (int lump); // Opens a new, independent FILE + FWadLump *ReopenLumpNumNewFile (int lump); // Opens a new, independent FILE FileReader * GetFileReader(int wadnum); // Gets a FileReader object to the entire WAD diff --git a/src/win32/i_system.cpp b/src/win32/i_system.cpp index d25229b672..1d2bae8e1e 100644 --- a/src/win32/i_system.cpp +++ b/src/win32/i_system.cpp @@ -1601,19 +1601,19 @@ unsigned int I_MakeRNGSeed() FString I_GetLongPathName(FString shortpath) { static TOptWin32Proc - GetLongPathName("kernel32.dll", "GetLongPathNameA", NULL); + GetLongPathNameA("kernel32.dll", "GetLongPathNameA"); // Doesn't exist on NT4 if (GetLongPathName == NULL) return shortpath; - DWORD buffsize = GetLongPathName.Call(shortpath.GetChars(), NULL, 0); + DWORD buffsize = GetLongPathNameA.Call(shortpath.GetChars(), NULL, 0); if (buffsize == 0) { // nothing to change (it doesn't exist, maybe?) return shortpath; } TCHAR *buff = new TCHAR[buffsize]; - DWORD buffsize2 = GetLongPathName.Call(shortpath.GetChars(), buff, buffsize); + DWORD buffsize2 = GetLongPathNameA.Call(shortpath.GetChars(), buff, buffsize); if (buffsize2 >= buffsize) { // Failure! Just return the short path delete[] buff; diff --git a/src/win32/i_system.h b/src/win32/i_system.h index 6872ca232c..566ca19778 100644 --- a/src/win32/i_system.h +++ b/src/win32/i_system.h @@ -56,25 +56,20 @@ extern os_t OSPlatform; template class TOptWin32Proc { - static Proto GetOptionalWin32Proc(const char* module, const char* function, const char* alt) + static Proto GetOptionalWin32Proc(const char* module, const char* function) { HMODULE hmodule = GetModuleHandle(module); if (hmodule == NULL) return NULL; - Proto ret = (Proto)GetProcAddress(hmodule, function); - if(ret != NULL || alt == NULL) - return ret; - - // Lookup alternate function name (ex. ProcW -> ProcA) - return (Proto)GetProcAddress(hmodule, alt); + return (Proto)GetProcAddress(hmodule, function); } public: const Proto Call; - TOptWin32Proc(const char* module, const char* function, const char* alt=NULL) - : Call(GetOptionalWin32Proc(module, function, alt)) {} + TOptWin32Proc(const char* module, const char* function) + : Call(GetOptionalWin32Proc(module, function)) {} // Wrapper object can be tested against NULL, but not directly called. operator const void*() const { return Call; } diff --git a/wadsrc/static/menudef.txt b/wadsrc/static/menudef.txt index ccd5df5662..d189478780 100644 --- a/wadsrc/static/menudef.txt +++ b/wadsrc/static/menudef.txt @@ -1423,6 +1423,11 @@ OptionString SoundOutputsMac "No sound", "No sound" } +OptionString ALDevices +{ + // filled in by the sound code +} + OptionString OutputFormats { "PCM-8", "8-bit" @@ -1454,6 +1459,60 @@ OptionString Resamplers "Spline", "Spline" } + +OptionString SoundBackends +{ + "fmod", "FMOD Ex" + "openal", "OpenAL" + "null", "No Sound" +} + +OptionString SoundBackendsFModOnly +{ + "fmod", "FMOD Ex" + "null", "No Sound" +} + +OptionString SoundBackendsOpenALOnly +{ + "openal", "OpenAL" + "null", "No Sound" +} + +OptionMenu FMODSoundItems +{ + Title "FMOD OPTIONS" + Slider "Underwater cutoff", "snd_waterlp", 0.0, 2000.0, 50.0, 0 + IfOption(Windows) + { + Option "Output system", "snd_output", "SoundOutputsWindows" + } + IfOption(Unix) + { + Option "Output system", "snd_output", "SoundOutputsUnix" + } + IfOption(Mac) + { + Option "Output system", "snd_output", "SoundOutputsMac" + } + Option "Output format", "snd_output_format", "OutputFormats" + Option "Speaker mode", "snd_speakermode", "SpeakerModes" + Option "Resampler", "snd_resampler", "Resamplers" + Option "HRTF filter", "snd_hrtf", "OnOff" + StaticText " " + Option "Buffer size", "snd_buffersize", "BufferSizes" + Option "Buffer count", "snd_buffercount", "BufferCounts" +} + + +OptionMenu OpenALSoundItems +{ + Title "OPENAL OPTIONS" + Option "Playback device", "snd_aldevice", "ALDevices" + Option "Enable EFX", "snd_efx", "OnOff" +} + + OptionValue MidiDevices { // filled in by the sound code @@ -1468,28 +1527,39 @@ OptionMenu SoundOptions Option "MIDI device", "snd_mididevice", "MidiDevices" StaticText " " Option "Underwater reverb", "snd_waterreverb", "OnOff" - Slider "Underwater cutoff", "snd_waterlp", 0, 2000, 50, 0 Option "Randomize pitches", "snd_pitched", "OnOff" Slider "Sound channels", "snd_channels", 8, 256, 8, 0 StaticText " " - Command "Restart sound", "snd_reset" + + ifoption(fmodex) + { + ifoption(openal) + { + Option "Sound backend", "snd_backend", "SoundBackends" + } + else + { + Option "Sound backend", "snd_backend", "SoundBackendsFModOnly" + } + } + else + { + ifoption(openal) + { + Option "Sound backend", "snd_backend", "SoundBackendsOpenALOnly" + } + } + + ifoption(fmodex) + { + Submenu "FMOD options", "FMODSoundItems" + } + ifoption(openal) + { + Submenu "OpenAL options", "OpenALSoundItems" + } StaticText " " - IfOption(Windows) - { - Option "Output system", "snd_output", "SoundOutputsWindows" - } - IfOption(Unix) - { - Option "Output system", "snd_output", "SoundOutputsUnix" - } - IfOption(Mac) - { - Option "Output system", "snd_output", "SoundOutputsMac" - } - Option "Output format", "snd_output_format", "OutputFormats" - Option "Speaker mode", "snd_speakermode", "SpeakerModes" - Option "Resampler", "snd_resampler", "Resamplers" - Option "HRTF filter", "snd_hrtf", "OnOff" + Command "Restart sound", "snd_reset" StaticText " " Submenu "Advanced options", "AdvSoundOptions" @@ -1523,8 +1593,6 @@ OptionMenu AdvSoundOptions { Title "ADVANCED SOUND OPTIONS" Option "Sample rate", "snd_samplerate", "SampleRates" - Option "Buffer size", "snd_buffersize", "BufferSizes" - Option "Buffer count", "snd_buffercount", "BufferCounts" StaticText " " StaticText "OPL Synthesis", 1 Slider "Number of emulated OPL chips", "opl_numchips", 1, 8, 1, 0 @@ -1546,7 +1614,7 @@ OptionMenu AdvSoundOptions OptionValue ModReplayers { - 0.0, "FMOD" + 0.0, "Sound System" 1.0, "foo_dumb" } diff --git a/wadsrc/static/sprites/pista0.png b/wadsrc/static/sprites/pista0.png index 9e3a4a0524..74ff02360e 100644 Binary files a/wadsrc/static/sprites/pista0.png and b/wadsrc/static/sprites/pista0.png differ diff --git a/zdoom.vcproj b/zdoom.vcproj index d171b18eb5..d802fc61c3 100644 --- a/zdoom.vcproj +++ b/zdoom.vcproj @@ -57,7 +57,7 @@ OmitFramePointers="true" WholeProgramOptimization="false" AdditionalIncludeDirectories="src\win32;src\sound;src;zlib;src\g_shared;src\g_doom;src\g_raven;src\g_heretic;src\g_hexen;src\g_strife;"jpeg-6b";"game-music-emu";gdtoa;bzip2;lzma\C" - PreprocessorDefinitions="NDEBUG,WIN32,_WIN32,_WINDOWS,HAVE_STRUPR,HAVE_FILELENGTH;NO_VA_COPY,BACKPATCH,HAVE_FLUIDSYNTH,DYN_FLUIDSYNTH" + PreprocessorDefinitions="NDEBUG,WIN32,_WIN32,_WINDOWS,HAVE_STRUPR,HAVE_FILELENGTH;NO_VA_COPY,BACKPATCH,HAVE_FLUIDSYNTH,DYN_FLUIDSYNTH,NO_OPENAL" StringPooling="true" ExceptionHandling="1" RuntimeLibrary="0" @@ -180,7 +180,7 @@ FavorSizeOrSpeed="1" OmitFramePointers="true" AdditionalIncludeDirectories="src\win32;src\sound;src;zlib;src\g_shared;src\g_doom;src\g_raven;src\g_heretic;src\g_hexen;src\g_strife;"jpeg-6b";game-music-emu;gdtoa;bzip2;lzma\C" - PreprocessorDefinitions="NDEBUG,WIN32,_WIN32,_WINDOWS,HAVE_STRUPR,HAVE_FILELENGTH;NO_VA_COPY" + PreprocessorDefinitions="NDEBUG,WIN32,_WIN32,_WINDOWS,HAVE_STRUPR,HAVE_FILELENGTH;NO_VA_COPY,NO_OPENAL" StringPooling="true" RuntimeLibrary="0" EnableFunctionLevelLinking="false" @@ -290,7 +290,7 @@ Name="VCCLCompilerTool" Optimization="0" AdditionalIncludeDirectories="src\win32;src\sound;src;zlib;src\g_shared;src\g_doom;src\g_raven;src\g_heretic;src\g_hexen;src\g_strife;"jpeg-6b";"game-music-emu";gdtoa;bzip2;lzma\C" - PreprocessorDefinitions="WIN32,_DEBUG,_WIN32,_WINDOWS,_CRTDBG_MAP_ALLOC,HAVE_STRUPR,HAVE_FILELENGTH;NO_VA_COPY,BACKPATCH,HAVE_FLUIDSYNTH,DYN_FLUIDSYNTH" + PreprocessorDefinitions="WIN32,_DEBUG,_WIN32,_WINDOWS,_CRTDBG_MAP_ALLOC,HAVE_STRUPR,HAVE_FILELENGTH;NO_VA_COPY,BACKPATCH,HAVE_FLUIDSYNTH,DYN_FLUIDSYNTH,NO_OPENAL" MinimalRebuild="true" RuntimeLibrary="1" EnableFunctionLevelLinking="true" @@ -398,7 +398,7 @@ Name="VCCLCompilerTool" Optimization="0" AdditionalIncludeDirectories="src\win32;src\sound;src;zlib;src\g_shared;src\g_doom;src\g_raven;src\g_heretic;src\g_hexen;src\g_strife;"jpeg-6b";game-music-emu;gdtoa;bzip2;lzma\C" - PreprocessorDefinitions="WIN32,_DEBUG,_WIN32,_WINDOWS,_CRTDBG_MAP_ALLOC,HAVE_STRUPR,HAVE_FILELENGTH;NO_VA_COPY" + PreprocessorDefinitions="WIN32,_DEBUG,_WIN32,_WINDOWS,_CRTDBG_MAP_ALLOC,HAVE_STRUPR,HAVE_FILELENGTH;NO_VA_COPY,NO_OPENAL" MinimalRebuild="true" RuntimeLibrary="1" EnableFunctionLevelLinking="true" @@ -2481,6 +2481,10 @@ + + @@ -2593,6 +2597,14 @@ RelativePath=".\src\sound\music_xmi_midiout.cpp" > + + + +