diff --git a/CMakeLists.txt b/CMakeLists.txt index e3ee74d55b..dd2c7730fc 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/specs/udmf_zdoom.txt b/specs/udmf_zdoom.txt index cbb5b902ce..6541e9a02d 100644 --- a/specs/udmf_zdoom.txt +++ b/specs/udmf_zdoom.txt @@ -119,6 +119,7 @@ Note: All fields default to false unless mentioned otherwise. blockhitscan = ; // Line blocks hitscan attacks locknumber = ; // Line special is locked arg0str = ; // Alternate string-based version of arg0 + moreids = ; // Additional line IDs, specified as a space separated list of numbers (e.g. "2 666 1003 4505") transparent = ; // true = line is a Strife transparent line (alpha 0.25) @@ -201,6 +202,7 @@ Note: All fields default to false unless mentioned otherwise. // sound sequence thing in the sector will override this property. hidden = ; // if true this sector will not be drawn on the textured automap. waterzone = ; // Sector is under water and swimmable + moreids = ; // Additional sector IDs/tags, specified as a space separated list of numbers (e.g. "2 666 1003 4505") * Note about dropactors @@ -370,6 +372,9 @@ Added transparent line property (to be folded back to core UDMF standard), and h Added plane equations for sector slopes. (Please read carefully to ensure proper use!) Changed language describing the DIALOGUE lump to mention USDF as an option. +1.25 19.04.2015 +Added 'moreids' for linedefs and sectors. + =============================================================================== EOF =============================================================================== diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index ad65d572cb..4e6b09a823 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -212,59 +212,88 @@ else( WIN32 ) endif( FPU_CONTROL_DIR ) endif( WIN32 ) -# 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 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 FMOD_LIB_NAME AND BORLAND ) - set( FMOD_LIB_NAME fmodex${X64}_bc ) -endif( NOT FMOD_LIB_NAME AND BORLAND ) +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 ) -if( NOT FMOD_LIB_NAME ) - set( FMOD_LIB_NAME fmodex${X64} ) -endif( NOT FMOD_LIB_NAME ) + 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 ) -# Search for FMOD include files +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 WIN32 ) - find_path( FMOD_INCLUDE_DIR fmod.hpp - PATHS ${FMOD_LOCAL_INC_DIRS} ) -endif( NOT WIN32 ) + if( NOT FMOD_LIB_NAME AND BORLAND ) + set( FMOD_LIB_NAME fmodex${X64}_bc ) + endif( NOT FMOD_LIB_NAME AND BORLAND ) -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( NOT FMOD_LIB_NAME ) + set( FMOD_LIB_NAME fmodex${X64} ) + endif( NOT FMOD_LIB_NAME ) -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}" ) + 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 library +if( NO_FMOD ) + add_definitions( -DNO_FMOD=1 ) +endif( NO_FMOD ) +if( NO_OPENAL ) + add_definitions( -DNO_OPENAL=1 ) +endif( NO_OPENAL ) -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 ) +# Search for libSndFile -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 ) +find_package( SndFile ) +# Search for libmpg123 + +find_package( MPG123 ) # Search for FluidSynth @@ -512,9 +541,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}" ) @@ -655,6 +692,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 ) @@ -905,6 +948,7 @@ add_executable( zdoom WIN32 MACOSX_BUNDLE p_spec.cpp p_states.cpp p_switch.cpp + p_tags.cpp p_teleport.cpp p_terrain.cpp p_things.cpp @@ -1035,6 +1079,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 @@ -1051,6 +1096,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 @@ -1171,9 +1218,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/compatibility.cpp b/src/compatibility.cpp index 096811def6..d60677f64f 100644 --- a/src/compatibility.cpp +++ b/src/compatibility.cpp @@ -49,6 +49,7 @@ #include "gi.h" #include "g_level.h" #include "p_lnspec.h" +#include "p_tags.h" #include "r_state.h" #include "w_wad.h" @@ -551,8 +552,8 @@ void SetCompatibilityParams() { if ((unsigned)CompatParams[i + 1] < (unsigned)numsectors) { - sectors[CompatParams[i + 1]].ClearTags(); - sectors[CompatParams[i + 1]].SetMainTag(CompatParams[i + 2]); + // this assumes that the sector does not have any tags yet! + tagManager.AddSectorTag(CompatParams[i + 1], CompatParams[i + 2]); } i += 3; break; diff --git a/src/d_main.cpp b/src/d_main.cpp index de5a7234ad..6ea7e8ff56 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) { @@ -1999,6 +2001,9 @@ static void D_DoomInit() } FRandom::StaticClearRandom (); + + Printf ("M_LoadDefaults: Load system defaults.\n"); + M_LoadDefaults (); // load before initing other systems } //========================================================================== @@ -2007,7 +2012,7 @@ static void D_DoomInit() // //========================================================================== -static void AddAutoloadFiles(const char *autoname) +static void AddAutoloadFiles(const char *autoname) { LumpFilterIWAD.Format("%s.", autoname); // The '.' is appened to simplify parsing the string @@ -2249,8 +2254,8 @@ void D_DoomMain (void) iwad_man = new FIWadManager; iwad_man->ParseIWadInfos(basewad); - Printf ("M_LoadDefaults: Load system defaults.\n"); - M_LoadDefaults (iwad_man); // load before initing other systems + // Now that we have the IWADINFO, initialize the autoload ini sections. + GameConfig->DoAutoloadSetup(iwad_man); // reinit from here @@ -2291,7 +2296,7 @@ void D_DoomMain (void) FBaseCVar::DisableCallbacks(); GameConfig->DoGameSetup (gameinfo.ConfigName); - AddAutoloadFiles(iwad_info->Autoname); + AddAutoloadFiles(iwad_info->Autoname); // Process automatically executed files FExecList *exec; 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/fragglescript/t_cmd.cpp b/src/fragglescript/t_cmd.cpp index 190c6b984d..74f3663579 100644 --- a/src/fragglescript/t_cmd.cpp +++ b/src/fragglescript/t_cmd.cpp @@ -169,6 +169,7 @@ void FS_EmulateCmd(char * string) { // No, this is not correct. But this is the way Legacy WADs expect it to be handled! if (players[i].mo != NULL) players[i].mo->ViewHeight = playerviewheight; + players[i].viewheight = playerviewheight; players[i].Uncrouch(); } while (sc.GetString()) diff --git a/src/fragglescript/t_func.cpp b/src/fragglescript/t_func.cpp index eb93cdbd67..56cb0e5ce9 100644 --- a/src/fragglescript/t_func.cpp +++ b/src/fragglescript/t_func.cpp @@ -1165,7 +1165,7 @@ void FParser::SF_ObjSector(void) } t_return.type = svt_int; - t_return.value.i = mo ? mo->Sector->GetMainTag() : 0; // nullptr check + t_return.value.i = mo ? tagManager.GetFirstSectorTag(mo->Sector) : 0; // nullptr check } //========================================================================== @@ -2235,7 +2235,7 @@ void FParser::SF_LineTrigger() maplinedef_t mld; mld.special=intvalue(t_argv[0]); mld.tag=t_argc > 1 ? intvalue(t_argv[1]) : 0; - P_TranslateLineDef(&line, &mld, false); + P_TranslateLineDef(&line, &mld); P_ExecuteSpecial(line.special, NULL, Script->trigger, false, line.args[0],line.args[1],line.args[2],line.args[3],line.args[4]); } @@ -4312,7 +4312,7 @@ void FParser::SF_KillInSector() while ((mo=it.Next())) { - if (mo->flags3&MF3_ISMONSTER && mo->Sector->HasTag(tag)) P_DamageMobj(mo, NULL, NULL, 1000000, NAME_Massacre); + if (mo->flags3&MF3_ISMONSTER && tagManager.SectorHasTag(mo->Sector, tag)) P_DamageMobj(mo, NULL, NULL, 1000000, NAME_Massacre); } } } @@ -4385,7 +4385,7 @@ void FParser::SF_SetLineTrigger() mld.tag = tag; mld.flags = 0; int f = lines[i].flags; - P_TranslateLineDef(&lines[i], &mld, false); + P_TranslateLineDef(&lines[i], &mld); lines[i].flags = (lines[i].flags & (ML_MONSTERSCANACTIVATE | ML_REPEAT_SPECIAL | ML_SPAC_MASK | ML_FIRSTSIDEONLY)) | (f & ~(ML_MONSTERSCANACTIVATE | ML_REPEAT_SPECIAL | ML_SPAC_MASK | ML_FIRSTSIDEONLY)); diff --git a/src/g_doomedmap.cpp b/src/g_doomedmap.cpp index bb2f18dc0e..338267deb1 100644 --- a/src/g_doomedmap.cpp +++ b/src/g_doomedmap.cpp @@ -213,13 +213,12 @@ void FMapInfoParser::ParseDoomEdNums() DoomEdFromMapinfo.Insert(ednum, editem); continue; } - sc.MustGetStringName(","); sc.MustGetNumber(); } int i = 0; while (i < 5) { - editem.args[i++] = sc.Number; + editem.args[i] = sc.Number; i++; if (!sc.CheckString(",")) break; sc.MustGetNumber(); diff --git a/src/g_level.cpp b/src/g_level.cpp index fd6d8049a6..713205f6fb 100644 --- a/src/g_level.cpp +++ b/src/g_level.cpp @@ -1229,6 +1229,7 @@ void G_FinishTravel () pawn->lastenemy = NULL; pawn->player->mo = pawn; pawn->player->camera = pawn; + pawn->player->viewheight = pawn->ViewHeight; pawn->flags2 &= ~MF2_BLASTED; DObject::StaticPointerSubstitution (oldpawn, pawn); oldpawn->Destroy(); diff --git a/src/g_shared/a_pickups.cpp b/src/g_shared/a_pickups.cpp index 24898baabc..892ea01e03 100644 --- a/src/g_shared/a_pickups.cpp +++ b/src/g_shared/a_pickups.cpp @@ -639,7 +639,7 @@ bool AInventory::HandlePickup (AInventory *item) { if (item->GetClass() == GetClass()) { - if (Amount < MaxAmount || sv_unlimited_pickup) + if (Amount < MaxAmount || (sv_unlimited_pickup && !item->ShouldStay())) { if (Amount > 0 && Amount + item->Amount < 0) { diff --git a/src/g_strife/a_strifestuff.cpp b/src/g_strife/a_strifestuff.cpp index 466832c488..78cda3323d 100644 --- a/src/g_strife/a_strifestuff.cpp +++ b/src/g_strife/a_strifestuff.cpp @@ -665,7 +665,7 @@ DEFINE_ACTION_FUNCTION(AActor, A_CheckTerrain) } else if ((sec->special & 0xFF) == Scroll_StrifeCurrent) { - int anglespeed = sec->GetMainTag() - 100; + int anglespeed = tagManager.GetFirstSectorTag(sec) - 100; fixed_t speed = (anglespeed % 10) << (FRACBITS - 4); angle_t finean = (anglespeed / 10) << (32-3); finean >>= ANGLETOFINESHIFT; diff --git a/src/gameconfigfile.cpp b/src/gameconfigfile.cpp index 30dc2276db..3d396d98b5 100644 --- a/src/gameconfigfile.cpp +++ b/src/gameconfigfile.cpp @@ -76,7 +76,7 @@ EXTERN_CVAR (Color, am_cdwallcolor) EXTERN_CVAR (Float, spc_amp) EXTERN_CVAR (Bool, wi_percents) -FGameConfigFile::FGameConfigFile (FIWadManager *iwad_man) +FGameConfigFile::FGameConfigFile () { #ifdef __APPLE__ FString user_docs, user_app_support, local_app_support; @@ -161,6 +161,27 @@ FGameConfigFile::FGameConfigFile (FIWadManager *iwad_man) SetValueForKey ("Path", "$DOOMWADDIR", true); } + // Add some self-documentation. + SetSectionNote("IWADSearch.Directories", + "# These are the directories to automatically search for IWADs.\n" + "# Each directory should be on a separate line, preceded by Path=\n"); + SetSectionNote("FileSearch.Directories", + "# These are the directories to search for wads added with the -file\n" + "# command line parameter, if they cannot be found with the path\n" + "# as-is. Layout is the same as for IWADSearch.Directories\n"); +} + +FGameConfigFile::~FGameConfigFile () +{ +} + +void FGameConfigFile::WriteCommentHeader (FILE *file) const +{ + fprintf (file, "# This file was generated by " GAMENAME " %s on %s\n", GetVersionString(), myasctime()); +} + +void FGameConfigFile::DoAutoloadSetup (FIWadManager *iwad_man) +{ // Create auto-load sections, so users know what's available. // Note that this totem pole is the reverse of the order that // they will appear in the file. @@ -220,14 +241,6 @@ FGameConfigFile::FGameConfigFile (FIWadManager *iwad_man) MoveSectionToStart("FileSearch.Directories"); MoveSectionToStart("IWADSearch.Directories"); - // Add some self-documentation. - SetSectionNote("IWADSearch.Directories", - "# These are the directories to automatically search for IWADs.\n" - "# Each directory should be on a separate line, preceded by Path=\n"); - SetSectionNote("FileSearch.Directories", - "# These are the directories to search for wads added with the -file\n" - "# command line parameter, if they cannot be found with the path\n" - "# as-is. Layout is the same as for IWADSearch.Directories\n"); SetSectionNote("Doom.AutoExec", "# Files to automatically execute when running the corresponding game.\n" "# Each file should be on its own line, preceded by Path=\n\n"); @@ -245,15 +258,6 @@ FGameConfigFile::FGameConfigFile (FIWadManager *iwad_man) "# 'doom.doom2.commercial.Autoload' only when playing doom2.wad.\n\n"); } -FGameConfigFile::~FGameConfigFile () -{ -} - -void FGameConfigFile::WriteCommentHeader (FILE *file) const -{ - fprintf (file, "# This file was generated by " GAMENAME " %s on %s\n", GetVersionString(), myasctime()); -} - void FGameConfigFile::DoGlobalSetup () { if (SetSection ("GlobalSettings.Unknown")) diff --git a/src/gameconfigfile.h b/src/gameconfigfile.h index 5862bb79ec..68630f5107 100644 --- a/src/gameconfigfile.h +++ b/src/gameconfigfile.h @@ -43,9 +43,10 @@ class FIWadManager; class FGameConfigFile : public FConfigFile { public: - FGameConfigFile (FIWadManager *iwad_man); + FGameConfigFile (); ~FGameConfigFile (); + void DoAutoloadSetup (FIWadManager *iwad_man); void DoGlobalSetup (); void DoGameSetup (const char *gamename); void DoKeySetup (const char *gamename); diff --git a/src/i_net.cpp b/src/i_net.cpp index d3958e1e12..5957846d2a 100644 --- a/src/i_net.cpp +++ b/src/i_net.cpp @@ -319,7 +319,11 @@ void PacketGet (void) } else if (c > 0) { //The packet is not from any in-game node, so we might as well discard it. - Printf("Dropped packet: Unknown host (%s:%d)\n", inet_ntoa(fromaddress.sin_addr), fromaddress.sin_port); + // Don't show the message for disconnect notifications. + if (c != 2 || TransmitBuffer[0] != PRE_FAKE || TransmitBuffer[1] != PRE_DISCONNECT) + { + DPrintf("Dropped packet: Unknown host (%s:%d)\n", inet_ntoa(fromaddress.sin_addr), fromaddress.sin_port); + } doomcom.remotenode = -1; return; } diff --git a/src/info.cpp b/src/info.cpp index 0e6abb8ea0..07ae29eb0f 100644 --- a/src/info.cpp +++ b/src/info.cpp @@ -358,7 +358,7 @@ void PClassActor::RegisterIDs() if (ConversationID > 0) { StrifeTypes[ConversationID] = cls; - if (cls != Class) + if (cls != this) { Printf(TEXTCOLOR_RED"Conversation ID %d refers to hidden class type '%s'\n", SpawnID, cls->TypeName.GetChars()); } diff --git a/src/m_misc.cpp b/src/m_misc.cpp index 1369bdb520..7f4fa482d9 100644 --- a/src/m_misc.cpp +++ b/src/m_misc.cpp @@ -410,9 +410,9 @@ CCMD (writeini) // M_LoadDefaults // -void M_LoadDefaults (FIWadManager *iwad_man) +void M_LoadDefaults () { - GameConfig = new FGameConfigFile(iwad_man); + GameConfig = new FGameConfigFile; GameConfig->DoGlobalSetup (); atterm (M_SaveDefaultsFinal); } diff --git a/src/m_misc.h b/src/m_misc.h index ea146c6905..8b0da1050c 100644 --- a/src/m_misc.h +++ b/src/m_misc.h @@ -41,7 +41,7 @@ void M_FindResponseFile (void); // Pass a NULL to get the original behavior. void M_ScreenShot (const char *filename); -void M_LoadDefaults (FIWadManager *iwad_man); +void M_LoadDefaults (); bool M_SaveDefaults (const char *filename); void M_SaveCustomKeys (FConfigFile *config, char *section, char *subsection, size_t sublen); diff --git a/src/m_specialpaths.cpp b/src/m_specialpaths.cpp index a45c23dc79..086f170f8f 100644 --- a/src/m_specialpaths.cpp +++ b/src/m_specialpaths.cpp @@ -22,6 +22,8 @@ #if defined(_WIN32) +#include "i_system.h" + typedef HRESULT (WINAPI *GKFP)(REFKNOWNFOLDERID, DWORD, HANDLE, PWSTR *); //=========================================================================== @@ -73,18 +75,7 @@ bool UseKnownFolders() bool GetKnownFolder(int shell_folder, REFKNOWNFOLDERID known_folder, bool create, FString &path) { - static GKFP SHGetKnownFolderPath = NULL; - static bool tested = false; - - if (!tested) - { - tested = true; - HMODULE shell32 = GetModuleHandle("shell32.dll"); - if (shell32 != NULL) - { - SHGetKnownFolderPath = (GKFP)GetProcAddress(shell32, "SHGetKnownFolderPath"); - } - } + static TOptWin32Proc SHGetKnownFolderPath("shell32.dll", "SHGetKnownFolderPath"); char pathstr[MAX_PATH]; @@ -92,6 +83,13 @@ bool GetKnownFolder(int shell_folder, REFKNOWNFOLDERID known_folder, bool create // new to Vista, hence the reason we support both. if (SHGetKnownFolderPath == NULL) { + static TOptWin32Proc + SHGetFolderPathA("shell32.dll", "SHGetFolderPathA"); + + // NT4 doesn't even have this function. + if (SHGetFolderPathA == NULL) + return false; + if (shell_folder < 0) { // Not supported by SHGetFolderPath return false; @@ -100,7 +98,7 @@ bool GetKnownFolder(int shell_folder, REFKNOWNFOLDERID known_folder, bool create { shell_folder |= CSIDL_FLAG_CREATE; } - if (FAILED(SHGetFolderPathA(NULL, shell_folder, NULL, 0, pathstr))) + if (FAILED(SHGetFolderPathA.Call(NULL, shell_folder, NULL, 0, pathstr))) { return false; } @@ -110,7 +108,7 @@ bool GetKnownFolder(int shell_folder, REFKNOWNFOLDERID known_folder, bool create else { PWSTR wpath; - if (FAILED(SHGetKnownFolderPath(known_folder, create ? KF_FLAG_CREATE : 0, NULL, &wpath))) + if (FAILED(SHGetKnownFolderPath.Call(known_folder, create ? KF_FLAG_CREATE : 0, NULL, &wpath))) { return false; } 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/menu/optionmenu.cpp b/src/menu/optionmenu.cpp index 9a77152659..57e69cfaba 100644 --- a/src/menu/optionmenu.cpp +++ b/src/menu/optionmenu.cpp @@ -489,7 +489,13 @@ bool FOptionMenuItem::MouseEvent(int type, int x, int y) int FOptionMenuItem::GetIndent() { - return mCentered? 0 : SmallFont->StringWidth(mLabel); + if (mCentered) + { + return 0; + } + const char *label = mLabel; + if (*label == '$') label = GStrings(label+1); + return SmallFont->StringWidth(label); } void FOptionMenuItem::drawLabel(int indent, int y, EColorRange color, bool grayed) diff --git a/src/namedef.h b/src/namedef.h index d738d98c81..7055ee3c9a 100644 --- a/src/namedef.h +++ b/src/namedef.h @@ -338,6 +338,7 @@ xx(Arg4) xx(Arg0Str) xx(Arg1Str) xx(Id) +xx(MoreIds) xx(V1) xx(V2) @@ -565,6 +566,7 @@ xx(Controlmessage) xx(Crosshairs) xx(Colorpickermenu) xx(Mididevices) +xx(Aldevices) xx(CustomizeControls) xx(MessageOptions) xx(AutomapOptions) diff --git a/src/oplsynth/music_opldumper_mididevice.cpp b/src/oplsynth/music_opldumper_mididevice.cpp index 33e026c07d..46dcdfef21 100644 --- a/src/oplsynth/music_opldumper_mididevice.cpp +++ b/src/oplsynth/music_opldumper_mididevice.cpp @@ -113,7 +113,7 @@ public: BYTE chipnum = reg >> 8; if (chipnum != CurChip) { - BYTE switcher[2] = { chipnum + 1, 2 }; + BYTE switcher[2] = { (BYTE)(chipnum + 1), 2 }; fwrite(switcher, 1, 2, File); } reg &= 255; @@ -192,7 +192,7 @@ public: "\0\0\0\0" // Total milliseconds "\0\0\0", // Total data 1, 20, File); - char type[4] = { Dual * 2, 0, 0, 0 }; // Single or dual OPL-2 + char type[4] = { (char)(Dual * 2), 0, 0, 0 }; // Single or dual OPL-2 fwrite(type, 1, 4, File); } virtual ~OPL_DOSBOXdump() 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_3dfloors.cpp b/src/p_3dfloors.cpp index 42d79dcc0c..cc76beaa31 100644 --- a/src/p_3dfloors.cpp +++ b/src/p_3dfloors.cpp @@ -844,7 +844,7 @@ void P_Spawn3DFloors (void) { if (line->args[1]&8) { - line->SetMainId(line->args[4]); + tagManager.AddLineID(i, line->args[4]); } else { diff --git a/src/p_3dmidtex.cpp b/src/p_3dmidtex.cpp index ff0d3a1814..9a9492e08e 100644 --- a/src/p_3dmidtex.cpp +++ b/src/p_3dmidtex.cpp @@ -167,7 +167,7 @@ void P_Attach3dMidtexLinesToSector(sector_t *sector, int lineid, int tag, bool c { line_t *ln = sectors[sec].lines[line]; - if (lineid != 0 && !ln->HasId(lineid)) continue; + if (lineid != 0 && !tagManager.LineHasID(ln, lineid)) continue; if (ln->frontsector == NULL || ln->backsector == NULL || !(ln->flags & ML_3DMIDTEX)) { diff --git a/src/p_acs.cpp b/src/p_acs.cpp index eafbd172e2..52bcda4d62 100644 --- a/src/p_acs.cpp +++ b/src/p_acs.cpp @@ -1562,7 +1562,7 @@ void FBehavior::StaticSerializeModuleStates (FArchive &arc) if (modnum != StaticModules.Size()) { - I_Error ("Level was saved with a different number of ACS modules."); + I_Error("Level was saved with a different number of ACS modules. (Have %d, save has %d)", StaticModules.Size(), modnum); } for (modnum = 0; modnum < StaticModules.Size(); ++modnum) @@ -1583,7 +1583,7 @@ void FBehavior::StaticSerializeModuleStates (FArchive &arc) if (stricmp (modname, module->ModuleName) != 0) { delete[] modname; - I_Error ("Level was saved with a different set of ACS modules."); + I_Error("Level was saved with a different set or order of ACS modules. (Have %s, save has %s)", module->ModuleName, modname); } else if (ModSize != module->GetDataSize()) { @@ -3210,7 +3210,7 @@ do_count: if (actor->health > 0 && (kind == NULL || actor->IsA (kind))) { - if (actor->Sector->HasTag(tag) || tag == -1) + if (tag == -1 || tagManager.SectorHasTag(actor->Sector, tag)) { // Don't count items in somebody's inventory if (!actor->IsKindOf (RUNTIME_CLASS(AInventory)) || @@ -3230,7 +3230,7 @@ do_count: if (actor->health > 0 && (kind == NULL || actor->IsA (kind))) { - if (actor->Sector->HasTag(tag) || tag == -1) + if (tag == -1 || tagManager.SectorHasTag(actor->Sector, tag)) { // Don't count items in somebody's inventory if (!actor->IsKindOf (RUNTIME_CLASS(AInventory)) || @@ -3898,7 +3898,13 @@ void DLevelScript::DoSetActorProperty (AActor *actor, int property, int value) case APROP_ViewHeight: if (actor->IsKindOf (RUNTIME_CLASS (APlayerPawn))) + { static_cast(actor)->ViewHeight = value; + if (actor->player != NULL) + { + actor->player->viewheight = value; + } + } break; case APROP_AttackZOffset: diff --git a/src/p_effect.cpp b/src/p_effect.cpp index fee207de95..8fe8a5bfa6 100644 --- a/src/p_effect.cpp +++ b/src/p_effect.cpp @@ -586,7 +586,7 @@ void P_DrawSplash2 (int count, fixed_t x, fixed_t y, fixed_t z, angle_t angle, i } } -void P_DrawRailTrail (AActor *source, const FVector3 &start, const FVector3 &end, int color1, int color2, double maxdiff_d, int flags, PClassActor *spawnclass, angle_t angle, int duration, double sparsity, double drift) +void P_DrawRailTrail (AActor *source, const FVector3 &start, const FVector3 &end, int color1, int color2, double maxdiff_d, int flags, PClassActor *spawnclass, angle_t angle, int duration, double sparsity, double drift, int SpiralOffset) { double length, lengthsquared; int steps, i; @@ -679,7 +679,7 @@ void P_DrawRailTrail (AActor *source, const FVector3 &start, const FVector3 &end color1 = color1 == 0 ? -1 : ParticleColor(color1); pos = start; - deg = FAngle(270); + deg = FAngle(SpiralOffset); for (i = spiral_steps; i; i--) { particle_t *p = NewParticle (); diff --git a/src/p_effect.h b/src/p_effect.h index 4348446cd6..ec06b9820a 100644 --- a/src/p_effect.h +++ b/src/p_effect.h @@ -88,7 +88,7 @@ void P_RunEffects (void); void P_RunEffect (AActor *actor, int effects); -void P_DrawRailTrail (AActor *source, const FVector3 &start, const FVector3 &end, int color1, int color2, double maxdiff = 0, int flags = 0, PClassActor *spawnclass = NULL, angle_t angle = 0, int duration = 35, double sparsity = 1.0, double drift = 1.0); +void P_DrawRailTrail (AActor *source, const FVector3 &start, const FVector3 &end, int color1, int color2, double maxdiff = 0, int flags = 0, PClassActor *spawnclass = NULL, angle_t angle = 0, int duration = 35, double sparsity = 1.0, double drift = 1.0, int SpiralOffset = 270); void P_DrawSplash (int count, fixed_t x, fixed_t y, fixed_t z, angle_t angle, int kind); void P_DrawSplash2 (int count, fixed_t x, fixed_t y, fixed_t z, angle_t angle, int updown, int kind); void P_DisconnectEffect (AActor *actor); diff --git a/src/p_lights.cpp b/src/p_lights.cpp index b4bfa1669c..49ef130e8a 100644 --- a/src/p_lights.cpp +++ b/src/p_lights.cpp @@ -838,7 +838,7 @@ void EV_StopLightEffect (int tag) while ((effect = iterator.Next()) != NULL) { - if (effect->GetSector()->HasTag(tag)) + if (tagManager.SectorHasTag(effect->GetSector(), tag)) { effect->Destroy(); } diff --git a/src/p_linkedsectors.cpp b/src/p_linkedsectors.cpp index 8f78aaddad..b9ef8b1007 100644 --- a/src/p_linkedsectors.cpp +++ b/src/p_linkedsectors.cpp @@ -278,7 +278,7 @@ static void RemoveTaggedSectors(extsector_t::linked::plane &scrollplane, int tag { for(int i = scrollplane.Sectors.Size()-1; i>=0; i--) { - if (scrollplane.Sectors[i].Sector->HasTag(tag)) + if (tagManager.SectorHasTag(scrollplane.Sectors[i].Sector, tag)) { scrollplane.Sectors.Delete(i); } diff --git a/src/p_lnspec.cpp b/src/p_lnspec.cpp index ec968d0208..0282e5ea1c 100644 --- a/src/p_lnspec.cpp +++ b/src/p_lnspec.cpp @@ -1266,7 +1266,7 @@ FUNC(LS_Thing_Destroy) while (actor) { AActor *temp = iterator.Next (); - if (actor->flags & MF_SHOOTABLE && actor->Sector->HasTag(arg2)) + if (actor->flags & MF_SHOOTABLE && tagManager.SectorHasTag(actor->Sector, arg2)) P_DamageMobj (actor, NULL, it, arg1 ? TELEFRAG_DAMAGE : actor->health, NAME_None); actor = temp; } @@ -1279,7 +1279,7 @@ FUNC(LS_Thing_Destroy) while (actor) { AActor *temp = iterator.Next (); - if (actor->flags & MF_SHOOTABLE && (arg2 == 0 || actor->Sector->HasTag(arg2))) + if (actor->flags & MF_SHOOTABLE && (arg2 == 0 || tagManager.SectorHasTag(actor->Sector, arg2))) P_DamageMobj (actor, NULL, it, arg1 ? TELEFRAG_DAMAGE : actor->health, NAME_None); actor = temp; } @@ -2063,7 +2063,7 @@ static void SetWallScroller (int id, int sidechoice, fixed_t dx, fixed_t dy, int { int wallnum = scroller->GetWallNum (); - if (wallnum >= 0 && sides[wallnum].linedef->HasId(id) && + if (wallnum >= 0 && tagManager.LineHasID(sides[wallnum].linedef, id) && int(sides[wallnum].linedef->sidedef[sidechoice] - sides) == wallnum && Where == scroller->GetScrollParts()) { @@ -2082,7 +2082,7 @@ static void SetWallScroller (int id, int sidechoice, fixed_t dx, fixed_t dy, int while ( (collect.Obj = iterator.Next ()) ) { if ((collect.RefNum = ((DScroller *)collect.Obj)->GetWallNum ()) != -1 && - sides[collect.RefNum].linedef->HasId(id) && + tagManager.LineHasID(sides[collect.RefNum].linedef, id) && int(sides[collect.RefNum].linedef->sidedef[sidechoice] - sides) == collect.RefNum && Where == ((DScroller *)collect.Obj)->GetScrollParts()) { @@ -2169,7 +2169,7 @@ static void SetScroller (int tag, DScroller::EScrollType type, fixed_t dx, fixed { if (scroller->IsType (type)) { - if (sectors[scroller->GetAffectee ()].HasTag(tag)) + if (tagManager.SectorHasTag(scroller->GetAffectee (), tag)) { i++; scroller->SetRate (dx, dy); diff --git a/src/p_local.h b/src/p_local.h index 2a8c080efc..a029bf02f2 100644 --- a/src/p_local.h +++ b/src/p_local.h @@ -478,7 +478,7 @@ void P_TraceBleed (int damage, AActor *target); // random direction version bool P_HitFloor (AActor *thing); bool P_HitWater (AActor *thing, sector_t *sec, fixed_t splashx = FIXED_MIN, fixed_t splashy = FIXED_MIN, fixed_t splashz=FIXED_MIN, bool checkabove = false, bool alert = true); void P_CheckSplash(AActor *self, fixed_t distance); -void P_RailAttack (AActor *source, int damage, int offset_xy, fixed_t offset_z = 0, int color1 = 0, int color2 = 0, double maxdiff = 0, int flags = 0, PClassActor *puff = NULL, angle_t angleoffset = 0, angle_t pitchoffset = 0, fixed_t distance = 8192*FRACUNIT, int duration = 0, double sparsity = 1.0, double drift = 1.0, PClassActor *spawnclass = NULL); // [RH] Shoot a railgun +void P_RailAttack (AActor *source, int damage, int offset_xy, fixed_t offset_z = 0, int color1 = 0, int color2 = 0, double maxdiff = 0, int flags = 0, PClassActor *puff = NULL, angle_t angleoffset = 0, angle_t pitchoffset = 0, fixed_t distance = 8192*FRACUNIT, int duration = 0, double sparsity = 1.0, double drift = 1.0, PClassActor *spawnclass = NULL, int SpiralOffset = 270); // [RH] Shoot a railgun enum // P_RailAttack / A_RailAttack / A_CustomRailgun / P_DrawRailTrail flags { diff --git a/src/p_map.cpp b/src/p_map.cpp index ab0cc48014..0fb2bbff83 100644 --- a/src/p_map.cpp +++ b/src/p_map.cpp @@ -1034,7 +1034,7 @@ bool PIT_CheckThing(AActor *thing, FCheckPosition &tm) // Both things overlap in x or y direction bool unblocking = false; - if (tm.FromPMove || tm.thing->player != NULL) + if ((tm.FromPMove || tm.thing->player != NULL) && thing->flags&MF_SOLID) { // Both actors already overlap. To prevent them from remaining stuck allow the move if it // takes them further apart or the move does not change the position (when called from P_ChangeSector.) @@ -1042,7 +1042,9 @@ bool PIT_CheckThing(AActor *thing, FCheckPosition &tm) { unblocking = true; } - else + else if (abs(thing->x - tm.thing->x) < (thing->radius+tm.thing->radius)/2 && + abs(thing->y - tm.thing->y) < (thing->radius+tm.thing->radius)/2) + { fixed_t newdist = P_AproxDistance(thing->x - tm.x, thing->y - tm.y); fixed_t olddist = P_AproxDistance(thing->x - tm.thing->x, thing->y - tm.thing->y); @@ -4140,7 +4142,7 @@ static ETraceStatus ProcessRailHit(FTraceResults &res, void *userdata) // // //========================================================================== -void P_RailAttack(AActor *source, int damage, int offset_xy, fixed_t offset_z, int color1, int color2, double maxdiff, int railflags, PClassActor *puffclass, angle_t angleoffset, angle_t pitchoffset, fixed_t distance, int duration, double sparsity, double drift, PClassActor *spawnclass) +void P_RailAttack(AActor *source, int damage, int offset_xy, fixed_t offset_z, int color1, int color2, double maxdiff, int railflags, PClassActor *puffclass, angle_t angleoffset, angle_t pitchoffset, fixed_t distance, int duration, double sparsity, double drift, PClassActor *spawnclass, int SpiralOffset) { fixed_t vx, vy, vz; angle_t angle, pitch; @@ -4295,7 +4297,7 @@ void P_RailAttack(AActor *source, int damage, int offset_xy, fixed_t offset_z, i end.X = FIXED2FLOAT(trace.X); end.Y = FIXED2FLOAT(trace.Y); end.Z = FIXED2FLOAT(trace.Z); - P_DrawRailTrail(source, start, end, color1, color2, maxdiff, railflags, spawnclass, source->angle + angleoffset, duration, sparsity, drift); + P_DrawRailTrail(source, start, end, color1, color2, maxdiff, railflags, spawnclass, source->angle + angleoffset, duration, sparsity, drift, SpiralOffset); } //========================================================================== diff --git a/src/p_mobj.cpp b/src/p_mobj.cpp index d9b904e69b..2aff70798c 100644 --- a/src/p_mobj.cpp +++ b/src/p_mobj.cpp @@ -3545,7 +3545,7 @@ void AActor::Tick () } else if (scrolltype == Scroll_StrifeCurrent) { // Strife scroll special - int anglespeed = sec->GetMainTag() - 100; + int anglespeed = tagManager.GetFirstSectorTag(sec) - 100; fixed_t carryspeed = DivScale32 (anglespeed % 10, 16*CARRYFACTOR); angle_t fineangle = (anglespeed / 10) << (32-3); fineangle >>= ANGLETOFINESHIFT; @@ -4857,7 +4857,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/p_saveg.cpp b/src/p_saveg.cpp index 119d92a9c1..4804943203 100644 --- a/src/p_saveg.cpp +++ b/src/p_saveg.cpp @@ -348,9 +348,13 @@ void P_SerializeWorld (FArchive &arc) { arc << sec->lightlevel; } - arc << sec->special - << sec->tag - << sec->soundtraversed + arc << sec->special; + if (SaveVersion < 4523) + { + short tag; + arc << tag; + } + arc << sec->soundtraversed << sec->seqType << sec->friction << sec->movefactor @@ -405,8 +409,13 @@ void P_SerializeWorld (FArchive &arc) arc << li->flags << li->activation << li->special - << li->Alpha - << li->id; + << li->Alpha; + + if (SaveVersion < 4523) + { + int id; + arc << id; + } if (P_IsACSSpecial(li->special)) { P_SerializeACSScriptNumber(arc, li->args[0], false); diff --git a/src/p_sectors.cpp b/src/p_sectors.cpp index 49d8db9f17..904c76c1b3 100644 --- a/src/p_sectors.cpp +++ b/src/p_sectors.cpp @@ -825,77 +825,6 @@ sector_t *sector_t::GetHeightSec() const } -bool sector_t::HasTag(int checktag) const -{ - return tag == checktag; -} - -bool sector_t::HasTags() const -{ - return tag != 0; -} - -void sector_t::SetMainTag(int tagnum) -{ - tag = tagnum; -} - -int sector_t::GetMainTag() const -{ - return tag; -} - -void sector_t::ClearTags() -{ - tag = 0; -} - -void sector_t::HashTags() -{ - int i; - - for (i=numsectors; --i>=0; ) // Initially make all slots empty. - sectors[i].firsttag = -1; - for (i=numsectors; --i>=0; ) // Proceed from last to first sector - { // so that lower sectors appear first - int j = (unsigned) sectors[i].tag % (unsigned) numsectors; // Hash func - sectors[i].nexttag = sectors[j].firsttag; // Prepend sector to chain - sectors[j].firsttag = i; - } -} - -void line_t::SetMainId(int newid) -{ - id = newid; -} - -void line_t::ClearIds() -{ - id = -1; -} - -bool line_t::HasId(int checkid) const -{ - return id == checkid; -} - - -void line_t::HashIds() -{ - // killough 4/17/98: same thing, only for linedefs - int i; - - for (i=numlines; --i>=0; ) // Initially make all slots empty. - lines[i].firstid = -1; - for (i=numlines; --i>=0; ) // Proceed from last to first linedef - { // so that lower linedefs appear first - int j = (unsigned) lines[i].id % (unsigned) numlines; // Hash func - lines[i].nextid = lines[j].firstid; // Prepend linedef to chain - lines[j].firstid = i; - } -} - - bool secplane_t::CopyPlaneIfValid (secplane_t *dest, const secplane_t *opp) const { bool copy = false; diff --git a/src/p_setup.cpp b/src/p_setup.cpp index 3433c243bd..4bff741727 100644 --- a/src/p_setup.cpp +++ b/src/p_setup.cpp @@ -1513,7 +1513,7 @@ void P_LoadSectors (MapData *map, FMissingTextureTracker &missingtex) else // [RH] Translate to new sector special ss->special = P_TranslateSectorSpecial (LittleShort(ms->special)); ss->secretsector = !!(ss->special&SECRET_MASK); - ss->SetMainTag(LittleShort(ms->tag)); + tagManager.AddSectorTag(i, LittleShort(ms->tag)); ss->thinglist = NULL; ss->touching_thinglist = NULL; // phares 3/14/98 ss->seqType = defSeqType; @@ -1904,54 +1904,59 @@ void P_AdjustLine (line_t *ld) } } -void P_SetLineID (line_t *ld) +void P_SetLineID (int i, line_t *ld) { // [RH] Set line id (as appropriate) here // for Doom format maps this must be done in P_TranslateLineDef because // the tag doesn't always go into the first arg. if (level.maptype == MAPTYPE_HEXEN) { + int setid = -1; switch (ld->special) { case Line_SetIdentification: if (!(level.flags2 & LEVEL2_HEXENHACK)) { - ld->SetMainId(ld->args[0] + 256 * ld->args[4]); + setid = ld->args[0] + 256 * ld->args[4]; ld->flags |= ld->args[1]<<16; } else { - ld->SetMainId(ld->args[0]); + setid = ld->args[0]; } ld->special = 0; break; case TranslucentLine: - ld->SetMainId(ld->args[0]); + setid = ld->args[0]; ld->flags |= ld->args[3]<<16; break; case Teleport_Line: case Scroll_Texture_Model: - ld->SetMainId(ld->args[0]); + setid = ld->args[0]; break; case Polyobj_StartLine: - ld->SetMainId(ld->args[3]); + setid = ld->args[3]; break; case Polyobj_ExplicitLine: - ld->SetMainId(ld->args[4]); + setid = ld->args[4]; break; case Plane_Align: - ld->SetMainId(ld->args[2]); + setid = ld->args[2]; break; case Static_Init: - if (ld->args[1] == Init_SectorLink) ld->SetMainId(ld->args[0]); + if (ld->args[1] == Init_SectorLink) setid = ld->args[0]; break; } + if (setid != -1) + { + tagManager.AddLineID(i, setid); + } } } @@ -2034,7 +2039,7 @@ void P_FinishLoadingLineDef(line_t *ld, int alpha) { for (j = 0; j < numlines; j++) { - if (lines[j].HasId(ld->args[0])) + if (tagManager.LineHasID(j, ld->args[0])) { lines[j].Alpha = alpha; if (additive) @@ -2136,13 +2141,13 @@ void P_LoadLineDefs (MapData * map) mld = (maplinedef_t *)mldf; ld = lines; - for (i = numlines; i > 0; i--, mld++, ld++) + for (i = 0; i < numlines; i++, mld++, ld++) { ld->Alpha = FRACUNIT; // [RH] Opaque by default // [RH] Translate old linedef special and flags to be // compatible with the new format. - P_TranslateLineDef (ld, mld, true); + P_TranslateLineDef (ld, mld, i); ld->v1 = &vertexes[LittleShort(mld->v1)]; ld->v2 = &vertexes[LittleShort(mld->v2)]; @@ -2215,7 +2220,7 @@ void P_LoadLineDefs2 (MapData * map) mld = (maplinedef2_t *)mldf; ld = lines; - for (i = numlines; i > 0; i--, mld++, ld++) + for (i = 0; i < numlines; i++, mld++, ld++) { int j; @@ -2228,13 +2233,12 @@ void P_LoadLineDefs2 (MapData * map) ld->v1 = &vertexes[LittleShort(mld->v1)]; ld->v2 = &vertexes[LittleShort(mld->v2)]; ld->Alpha = FRACUNIT; // [RH] Opaque by default - ld->ClearIds(); P_SetSideNum (&ld->sidedef[0], LittleShort(mld->sidenum[0])); P_SetSideNum (&ld->sidedef[1], LittleShort(mld->sidenum[1])); P_AdjustLine (ld); - P_SetLineID(ld); + P_SetLineID(i, ld); P_SaveLineSpecial (ld); if (level.flags2 & LEVEL2_CLIPMIDTEX) ld->flags |= ML_CLIP_MIDTEX; if (level.flags2 & LEVEL2_WRAPMIDTEX) ld->flags |= ML_WRAP_MIDTEX; @@ -2490,7 +2494,7 @@ void P_ProcessSideTextures(bool checktranmap, side_t *sd, sector_t *sec, intmaps for (s = 0; s < numsectors; s++) { - if (sectors[s].HasTag(tag)) + if (tagManager.SectorHasTag(s, tag)) { if (!colorgood) color = sectors[s].ColorMap->Color; if (!foggood) fog = sectors[s].ColorMap->Fade; @@ -3126,9 +3130,9 @@ static void P_GroupLines (bool buildmap) { if (sector->linecount == 0) { - Printf ("Sector %i (tag %i) has no lines\n", i, sector->GetMainTag()); + Printf ("Sector %i (tag %i) has no lines\n", i, tagManager.GetFirstSectorTag(sector)); // 0 the sector's tag so that no specials can use it - sector->ClearTags(); + tagManager.RemoveSectorTags(i); } else { @@ -3205,8 +3209,7 @@ static void P_GroupLines (bool buildmap) // [RH] Moved this here times[4].Clock(); // killough 1/30/98: Create xref tables for tags - sector_t::HashTags(); - line_t::HashIds(); + tagManager.HashTags(); times[4].Unclock(); times[5].Clock(); @@ -3337,6 +3340,7 @@ void P_FreeLevelData () FPolyObj::ClearAllSubsectorLinks(); // can't be done as part of the polyobj deletion process. SN_StopAllSequences (); DThinker::DestroyAllThinkers (); + tagManager.Clear(); level.total_monsters = level.total_items = level.total_secrets = level.killed_monsters = level.found_items = level.found_secrets = wminfo.maxfrags = 0; diff --git a/src/p_setup.h b/src/p_setup.h index 20caa5d76c..cbd423a101 100644 --- a/src/p_setup.h +++ b/src/p_setup.h @@ -115,7 +115,7 @@ struct line_t; struct maplinedef_t; void P_LoadTranslator(const char *lumpname); -void P_TranslateLineDef (line_t *ld, maplinedef_t *mld, bool setlineid); +void P_TranslateLineDef (line_t *ld, maplinedef_t *mld, int lineindexforid = -1); int P_TranslateSectorSpecial (int); int GetUDMFInt(int type, int index, const char *key); diff --git a/src/p_spec.cpp b/src/p_spec.cpp index 74d4b6f178..457a361635 100644 --- a/src/p_spec.cpp +++ b/src/p_spec.cpp @@ -186,57 +186,6 @@ bool CheckIfExitIsGood (AActor *self, level_info_t *info) // UTILITIES // - - -// -// RETURN NEXT SECTOR # THAT LINE TAG REFERS TO -// - -// Find the next sector with a specified tag. -// Rewritten by Lee Killough to use chained hashing to improve speed - -int FSectorTagIterator::Next() -{ - int ret; - if (searchtag == INT_MIN) - { - ret = start; - start = -1; - } - else - { - while (start != -1 && sectors[start].tag != searchtag) start = sectors[start].nexttag; - if (start == -1) return -1; - ret = start; - start = sectors[start].nexttag; - } - return ret; -} - -int FSectorTagIterator::NextCompat(bool compat, int start) -{ - if (!compat) return Next(); - - for (int i = start + 1; i < numsectors; i++) - { - if (sectors[i].HasTag(searchtag)) return i; - } - return -1; -} - - -// killough 4/16/98: Same thing, only for linedefs - -int FLineIdIterator::Next() -{ - while (start != -1 && lines[start].id != searchtag) start = lines[start].nextid; - if (start == -1) return -1; - int ret = start; - start = lines[start].nextid; - return ret; -} - - //============================================================================ // // P_ActivateLine @@ -283,7 +232,7 @@ bool P_ActivateLine (line_t *line, AActor *mo, int side, int activationType) !repeat && // only non-repeatable triggers (specialGeneric_Crusher) && // not for Boom's generalized linedefs special && // not for lines without a special - line->HasId(line->args[0]) && // Safety check: exclude edited UDMF linedefs or ones that don't map the tag to args[0] + tagManager.LineHasID(line, line->args[0]) && // Safety check: exclude edited UDMF linedefs or ones that don't map the tag to args[0] line->args[0] && // only if there's a tag (which is stored in the first arg) P_FindFirstSectorFromTag (line->args[0]) == -1) // only if no sector is tagged to this linedef { @@ -1792,7 +1741,7 @@ static void P_SpawnScrollers(void) if (lines[i].special == Sector_CopyScroller) { // don't allow copying the scroller if the sector has the same tag as it would just duplicate it. - if (lines[i].frontsector->HasTag(lines[i].args[0])) + if (tagManager.SectorHasTag(lines[i].frontsector, lines[i].args[0])) { copyscrollers.Push(i); } @@ -2200,7 +2149,7 @@ DPusher::DPusher (DPusher::EPusher type, line_t *l, int magnitude, int angle, int DPusher::CheckForSectorMatch (EPusher type, int tag) { - if (m_Type == type && sectors[m_Affectee].HasTag(tag)) + if (m_Type == type && tagManager.SectorHasTag(m_Affectee, tag)) return m_Affectee; else return -1; diff --git a/src/p_spec.h b/src/p_spec.h index 64d73e8c49..37537b83a0 100644 --- a/src/p_spec.h +++ b/src/p_spec.h @@ -242,66 +242,8 @@ inline sector_t *getNextSector (line_t *line, const sector_t *sec) line->frontsector; } -class FSectorTagIterator -{ -protected: - int searchtag; - int start; -public: - FSectorTagIterator(int tag) - { - searchtag = tag; - start = sectors[(unsigned)tag % (unsigned)numsectors].firsttag; - } - - // Special constructor for actions that treat tag 0 as 'back of activation line' - FSectorTagIterator(int tag, line_t *line) - { - if (tag == 0) - { - searchtag = INT_MIN; - start = (line == NULL || line->backsector == NULL)? -1 : (int)(line->backsector - sectors); - } - else - { - searchtag = tag; - start = sectors[(unsigned)tag % (unsigned)numsectors].firsttag; - } - } - - int Next(); - int NextCompat(bool compat, int secnum); -}; - -class FLineIdIterator -{ -protected: - int searchtag; - int start; - -public: - FLineIdIterator(int id) - { - searchtag = id; - start = lines[(unsigned) id % (unsigned) numlines].firstid; - } - - int Next(); -}; - - -inline int P_FindFirstSectorFromTag(int tag) -{ - FSectorTagIterator it(tag); - return it.Next(); -} - -inline int P_FindFirstLineFromID(int tag) -{ - FLineIdIterator it(tag); - return it.Next(); -} +#include "p_tags.h" // // P_LIGHTS diff --git a/src/p_tags.cpp b/src/p_tags.cpp new file mode 100644 index 0000000000..d5c03ec9dc --- /dev/null +++ b/src/p_tags.cpp @@ -0,0 +1,353 @@ +/* +** p_tags.cpp +** everything to do with tags and their management +** +**--------------------------------------------------------------------------- +** Copyright 2015 Christoph Oelckers +** 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. +**--------------------------------------------------------------------------- +** +** +*/ + + +#include "p_tags.h" +#include "c_dispatch.h" + +FTagManager tagManager; + +//----------------------------------------------------------------------------- +// +// +// +//----------------------------------------------------------------------------- + +static inline int sectindex(const sector_t *sector) +{ + return (int)(intptr_t)(sector - sectors); +} + +static inline int lineindex(const line_t *line) +{ + return (int)(intptr_t)(line - lines); +} + +//----------------------------------------------------------------------------- +// +// +// +//----------------------------------------------------------------------------- + +void FTagManager::AddSectorTag(int sector, int tag) +{ + if (tag == 0) return; + + // This function assumes that all tags for a single sector get added sequentially. + // Should there ever be some need for compatibility.txt to add tags to sectors which already have a tag this function needs to be changed to adjust the startForSector indices. + while (startForSector.Size() <= (unsigned int)sector) + { + startForSector.Push(-1); + } + if (startForSector[sector] == -1) + { + startForSector[sector] = allTags.Size(); + } + else + { + // check if the key was already defined + for (unsigned i = startForSector[sector]; i < allTags.Size(); i++) + { + if (allTags[i].tag == tag) + { + return; + } + } + } + FTagItem it = { sector, tag, -1 }; + allTags.Push(it); +} + +//----------------------------------------------------------------------------- +// +// +// +//----------------------------------------------------------------------------- + +void FTagManager::RemoveSectorTags(int sect) +{ + int start = startForSector[sect]; + if (start >= 0) + { + while (allTags[start].target == sect) + { + allTags[start].tag = allTags[start].target = -1; + start++; + } + } +} + +//----------------------------------------------------------------------------- +// +// +// +//----------------------------------------------------------------------------- + +void FTagManager::AddLineID(int line, int tag) +{ + if (tag == -1) return; // For line IDs -1 means 'not set', unlike sectors. + + // This function assumes that all ids for a single line get added sequentially. + while (startForLine.Size() <= (unsigned int)line) + { + startForLine.Push(-1); + } + if (startForLine[line] == -1) + { + startForLine[line] = allIDs.Size(); + } + else + { + // check if the key was already defined + for (unsigned i = startForLine[line]; i < allIDs.Size(); i++) + { + if (allIDs[i].tag == tag) + { + return; + } + } + } + FTagItem it = { line, tag, -1 }; + allIDs.Push(it); +} + +//----------------------------------------------------------------------------- +// +// +// +//----------------------------------------------------------------------------- + +void FTagManager::HashTags() +{ + // add an end marker so we do not need to check for the array's size in the other functions. + static FTagItem it = { -1, -1, -1 }; + allTags.Push(it); + allIDs.Push(it); + + // Initially make all slots empty. + memset(TagHashFirst, -1, sizeof(TagHashFirst)); + memset(IDHashFirst, -1, sizeof(IDHashFirst)); + + // Proceed from last to first so that lower targets appear first + for (int i = allTags.Size() - 1; i >= 0; i--) + { + if (allTags[i].target >= 0) // only link valid entries + { + int hash = ((unsigned int)allTags[i].tag) % FTagManager::TAG_HASH_SIZE; + allTags[i].nexttag = TagHashFirst[hash]; + TagHashFirst[hash] = i; + } + } + + for (int i = allIDs.Size() - 1; i >= 0; i--) + { + if (allIDs[i].target >= 0) // only link valid entries + { + int hash = ((unsigned int)allIDs[i].tag) % FTagManager::TAG_HASH_SIZE; + allIDs[i].nexttag = IDHashFirst[hash]; + IDHashFirst[hash] = i; + } + } + +} + +//----------------------------------------------------------------------------- +// +// +// +//----------------------------------------------------------------------------- + +bool FTagManager::SectorHasTags(const sector_t *sector) const +{ + int i = sectindex(sector); + return SectorHasTags(i); +} + +//----------------------------------------------------------------------------- +// +// +// +//----------------------------------------------------------------------------- + +int FTagManager::GetFirstSectorTag(const sector_t *sect) const +{ + int i = sectindex(sect); + return SectorHasTags(i) ? allTags[startForSector[i]].tag : 0; +} + +//----------------------------------------------------------------------------- +// +// +// +//----------------------------------------------------------------------------- + +bool FTagManager::SectorHasTag(int i, int tag) const +{ + if (SectorHasTags(i)) + { + int ndx = startForSector[i]; + while (allTags[ndx].target == i) + { + if (allTags[ndx].tag == tag) return true; + ndx++; + } + } + return false; +} + +//----------------------------------------------------------------------------- +// +// +// +//----------------------------------------------------------------------------- + +bool FTagManager::SectorHasTag(const sector_t *sector, int tag) const +{ + return SectorHasTag(sectindex(sector), tag); +} + +//----------------------------------------------------------------------------- +// +// +// +//----------------------------------------------------------------------------- + +bool FTagManager::LineHasID(int i, int tag) const +{ + if (LineHasIDs(i)) + { + int ndx = startForLine[i]; + while (allIDs[ndx].target == i) + { + if (allIDs[ndx].tag == tag) return true; + ndx++; + } + } + return false; +} + +//----------------------------------------------------------------------------- +// +// +// +//----------------------------------------------------------------------------- + +bool FTagManager::LineHasID(const line_t *line, int tag) const +{ + return LineHasID(lineindex(line), tag); +} + +//----------------------------------------------------------------------------- +// +// +// +//----------------------------------------------------------------------------- + +void FTagManager::DumpTags() +{ + for (unsigned i = 0; i < allTags.Size(); i++) + { + Printf("Sector %d, tag %d\n", allTags[i].target, allTags[i].tag); + } + for (unsigned i = 0; i < allIDs.Size(); i++) + { + Printf("Line %d, ID %d\n", allIDs[i].target, allIDs[i].tag); + } +} + +CCMD(dumptags) +{ + tagManager.DumpTags(); +} + +//----------------------------------------------------------------------------- +// +// RETURN NEXT SECTOR # THAT LINE TAG REFERS TO +// +// Find the next sector with a specified tag. +// Rewritten by Lee Killough to use chained hashing to improve speed +// +//----------------------------------------------------------------------------- + +int FSectorTagIterator::Next() +{ + int ret; + if (searchtag == INT_MIN) + { + ret = start; + start = -1; + } + else + { + while (start >= 0 && tagManager.allTags[start].tag != searchtag) start = tagManager.allTags[start].nexttag; + if (start == -1) return -1; + ret = tagManager.allTags[start].target; + start = start = tagManager.allTags[start].nexttag; + } + return ret; +} + +//----------------------------------------------------------------------------- +// +// linear search for compatible stair building +// +//----------------------------------------------------------------------------- + +int FSectorTagIterator::NextCompat(bool compat, int start) +{ + if (!compat) return Next(); + + for (int i = start + 1; i < numsectors; i++) + { + if (tagManager.SectorHasTag(i, searchtag)) return i; + } + return -1; +} + + +//----------------------------------------------------------------------------- +// +// killough 4/16/98: Same thing, only for linedefs +// +//----------------------------------------------------------------------------- + +int FLineIdIterator::Next() +{ + while (start >= 0 && tagManager.allIDs[start].tag != searchtag) start = tagManager.allIDs[start].nexttag; + if (start == -1) return -1; + int ret = tagManager.allIDs[start].target; + start = start = tagManager.allIDs[start].nexttag; + return ret; +} diff --git a/src/p_tags.h b/src/p_tags.h new file mode 100644 index 0000000000..2195821c15 --- /dev/null +++ b/src/p_tags.h @@ -0,0 +1,133 @@ +#ifndef P_TAGS_H +#define P_TAGS_H 1 + +#include "r_defs.h" +#include "r_state.h" + +struct FTagItem +{ + int target; // either sector or line + int tag; + int nexttag; // for hashing +}; + +class FSectorTagIterator; +class FLineIdIterator; + +class FTagManager +{ + enum + { + TAG_HASH_SIZE = 256 + }; + + friend class FSectorTagIterator; + friend class FLineIdIterator; + + TArray allTags; + TArray allIDs; + TArray startForSector; + TArray startForLine; + int TagHashFirst[TAG_HASH_SIZE]; + int IDHashFirst[TAG_HASH_SIZE]; + + bool SectorHasTags(int sect) const + { + return sect >= 0 && sect < (int)startForSector.Size() && startForSector[sect] >= 0; + } + + bool LineHasIDs(int sect) const + { + return sect >= 0 && sect < (int)startForLine.Size() && startForLine[sect] >= 0; + } + +public: + void Clear() + { + allTags.Clear(); + allIDs.Clear(); + startForSector.Clear(); + startForLine.Clear(); + memset(TagHashFirst, -1, sizeof(TagHashFirst)); + } + + bool SectorHasTags(const sector_t *sector) const; + int GetFirstSectorTag(const sector_t *sect) const; + bool SectorHasTag(int sector, int tag) const; + bool SectorHasTag(const sector_t *sector, int tag) const; + + bool LineHasID(int line, int id) const; + bool LineHasID(const line_t *line, int id) const; + + void HashTags(); + void AddSectorTag(int sector, int tag); + void AddLineID(int line, int tag); + void RemoveSectorTags(int sect); + + void DumpTags(); +}; + +extern FTagManager tagManager; + +class FSectorTagIterator +{ +protected: + int searchtag; + int start; + +public: + FSectorTagIterator(int tag) + { + searchtag = tag; + start = tagManager.TagHashFirst[((unsigned int)tag) % FTagManager::TAG_HASH_SIZE]; + } + + // Special constructor for actions that treat tag 0 as 'back of activation line' + FSectorTagIterator(int tag, line_t *line) + { + if (tag == 0) + { + searchtag = INT_MIN; + start = (line == NULL || line->backsector == NULL)? -1 : (int)(line->backsector - sectors); + } + else + { + searchtag = tag; + start = tagManager.TagHashFirst[((unsigned int)tag) % FTagManager::TAG_HASH_SIZE]; + } + } + + int Next(); + int NextCompat(bool compat, int secnum); +}; + +class FLineIdIterator +{ +protected: + int searchtag; + int start; + +public: + FLineIdIterator(int id) + { + searchtag = id; + start = tagManager.IDHashFirst[((unsigned int)id) % FTagManager::TAG_HASH_SIZE]; + } + + int Next(); +}; + + +inline int P_FindFirstSectorFromTag(int tag) +{ + FSectorTagIterator it(tag); + return it.Next(); +} + +inline int P_FindFirstLineFromID(int tag) +{ + FLineIdIterator it(tag); + return it.Next(); +} + +#endif diff --git a/src/p_teleport.cpp b/src/p_teleport.cpp index 8ca54d0e49..7fb49fe24d 100644 --- a/src/p_teleport.cpp +++ b/src/p_teleport.cpp @@ -249,7 +249,7 @@ static AActor *SelectTeleDest (int tid, int tag, bool norandom) int count = 0; while ( (searcher = iterator.Next ()) ) { - if (tag == 0 || searcher->Sector->HasTag(tag)) + if (tag == 0 || tagManager.SectorHasTag(searcher->Sector, tag)) { count++; } @@ -288,7 +288,7 @@ static AActor *SelectTeleDest (int tid, int tag, bool norandom) while (count > 0) { searcher = iterator.Next (); - if (tag == 0 || searcher->Sector->HasTag(tag)) + if (tag == 0 || tagManager.SectorHasTag(searcher->Sector, tag)) { count--; } diff --git a/src/p_things.cpp b/src/p_things.cpp index 7c3bb18345..0b51b9c18a 100644 --- a/src/p_things.cpp +++ b/src/p_things.cpp @@ -659,8 +659,12 @@ void InitClassMap(FClassMap &themap, SpawnMap &thedata) pair->Value.filename.GetChars(), pair->Value.linenum, pair->Value.classname.GetChars()); error++; } + themap.Insert(pair->Key, cls); + } + else + { + themap.Remove(pair->Key); } - themap.Insert(pair->Key, cls); } if (error > 0) { diff --git a/src/p_udmf.cpp b/src/p_udmf.cpp index 657281a117..0434d8bce4 100644 --- a/src/p_udmf.cpp +++ b/src/p_udmf.cpp @@ -46,6 +46,7 @@ #include "r_state.h" #include "r_data/colormaps.h" #include "w_wad.h" +#include "p_tags.h" //=========================================================================== // @@ -754,7 +755,7 @@ public: mld.flags = 0; mld.special = th->special; mld.tag = th->args[0]; - P_TranslateLineDef(&ld, &mld, true); + P_TranslateLineDef(&ld, &mld); th->special = ld.special; memcpy(th->args, ld.args, sizeof (ld.args)); } @@ -779,10 +780,10 @@ public: bool strifetrans2 = false; FString arg0str, arg1str; int lineid; // forZDoomTranslated namespace + FString tagstring; memset(ld, 0, sizeof(*ld)); ld->Alpha = FRACUNIT; - ld->ClearIds(); ld->sidedef[0] = ld->sidedef[1] = NULL; if (level.flags2 & LEVEL2_CLIPMIDTEX) ld->flags |= ML_CLIP_MIDTEX; if (level.flags2 & LEVEL2_WRAPMIDTEX) ld->flags |= ML_WRAP_MIDTEX; @@ -816,7 +817,7 @@ public: case NAME_Id: lineid = CheckInt(key); - ld->SetMainId(lineid); + tagManager.AddLineID(index, lineid); continue; case NAME_Sidefront: @@ -1039,22 +1040,17 @@ public: Flag(ld->flags, ML_3DMIDTEX_IMPASS, key); continue; + case NAME_MoreIds: + // delay parsing of the tag string until parsing of the sector is complete + // This ensures that the ID is always the first tag in the list. + tagstring = CheckString(key); + break; + + default: break; } -#if 0 // for later - if (namespace_bits & (Zd)) && !strnicmp(key.GetChars(), "Id", 2)) - { - char *endp; - int num = strtol(key.GetChars(), &endp, 10); - if (num > 0 && *endp == NULL) - { - // only allow ID## with ## as a proper number - ld->SetId((short)CheckInt(key), false); - } - } -#endif if ((namespace_bits & (Zd | Zdt)) && !strnicmp("user_", key.GetChars(), 5)) { @@ -1062,6 +1058,17 @@ public: } } + if (tagstring.IsNotEmpty()) + { + FScanner sc; + sc.OpenString("tagstring", tagstring); + // scan the string as long as valid numbers can be found + while (sc.CheckNumber()) + { + if (sc.Number != 0) tagManager.AddLineID(index, sc.Number); + } + } + if (isTranslated) { int saved = ld->flags; @@ -1070,7 +1077,7 @@ public: memset(&mld, 0, sizeof(mld)); mld.special = ld->special; mld.tag = lineid; - P_TranslateLineDef(ld, &mld, false); + P_TranslateLineDef(ld, &mld); ld->flags = saved | (ld->flags&(ML_MONSTERSCANACTIVATE|ML_REPEAT_SPECIAL|ML_FIRSTSIDEONLY)); } if (passuse && (ld->activation & SPAC_Use)) @@ -1269,6 +1276,7 @@ public: int desaturation = -1; int fplaneflags = 0, cplaneflags = 0; double fp[4] = { 0 }, cp[4] = { 0 }; + FString tagstring; memset(sec, 0, sizeof(*sec)); sec->lightlevel = 160; @@ -1332,7 +1340,7 @@ public: continue; case NAME_Id: - sec->SetMainTag((short)CheckInt(key)); + tagManager.AddSectorTag(index, CheckInt(key)); continue; default: @@ -1510,28 +1518,32 @@ public: cp[3] = CheckFloat(key); break; + case NAME_MoreIds: + // delay parsing of the tag string until parsing of the sector is complete + // This ensures that the ID is always the first tag in the list. + tagstring = CheckString(key); + break; + default: break; } -#if 0 // for later - if (namespace_bits & (Zd)) && !strnicmp(key.GetChars(), "Id", 2)) - { - char *endp; - int num = strtol(key.GetChars(), &endp, 10); - if (num > 0 && *endp == NULL) - { - // only allow ID## with ## as a proper number - sec->SetTag((short)CheckInt(key), false); - } - } -#endif - if ((namespace_bits & (Zd | Zdt)) && !strnicmp("user_", key.GetChars(), 5)) { AddUserKey(key, UDMF_Sector, index); } } + if (tagstring.IsNotEmpty()) + { + FScanner sc; + sc.OpenString("tagstring", tagstring); + // scan the string as long as valid numbers can be found + while (sc.CheckNumber()) + { + if (sc.Number != 0) tagManager.AddSectorTag(index, sc.Number); + } + } + sec->secretsector = !!(sec->special&SECRET_MASK); // Reset the planes to their defaults if not all of the plane equation's parameters were found. diff --git a/src/p_writemap.cpp b/src/p_writemap.cpp index 2f11ee5c12..e31d2c4c3c 100644 --- a/src/p_writemap.cpp +++ b/src/p_writemap.cpp @@ -262,7 +262,7 @@ static int WriteSECTORS (FILE *file) uppercopy (ms.ceilingpic, GetTextureName (sectors[i].GetTexture(sector_t::ceiling))); ms.lightlevel = LittleShort((short)sectors[i].lightlevel); ms.special = LittleShort(sectors[i].special); - ms.tag = LittleShort(sectors[i].GetMainTag()); + ms.tag = LittleShort(tagManager.GetFirstSectorTag(§ors[i])); fwrite (&ms, sizeof(ms), 1, file); } return numsectors * sizeof(ms); diff --git a/src/p_xlat.cpp b/src/p_xlat.cpp index 76d0cb8110..6e1718273b 100644 --- a/src/p_xlat.cpp +++ b/src/p_xlat.cpp @@ -60,7 +60,7 @@ typedef enum PushMany, } triggertype_e; -void P_TranslateLineDef (line_t *ld, maplinedef_t *mld, bool setid) +void P_TranslateLineDef (line_t *ld, maplinedef_t *mld, int lineindexforid) { unsigned short special = (unsigned short) LittleShort(mld->special); short tag = LittleShort(mld->tag); @@ -100,13 +100,13 @@ void P_TranslateLineDef (line_t *ld, maplinedef_t *mld, bool setid) } flags = newflags; - if (setid) + if (lineindexforid >= 0) { // For purposes of maintaining BOOM compatibility, each // line also needs to have its ID set to the same as its tag. // An external conversion program would need to do this more // intelligently. - ld->SetMainId(tag); + tagManager.AddLineID(lineindexforid, tag); } // 0 specials are never translated. @@ -307,7 +307,7 @@ void P_TranslateTeleportThings () while ( (dest = iterator.Next()) ) { - if (!dest->Sector->HasTags()) + if (!tagManager.SectorHasTags(dest->Sector)) { dest->tid = 1; dest->AddToHash (); diff --git a/src/r_defs.h b/src/r_defs.h index 3cf25573c0..afda92089d 100644 --- a/src/r_defs.h +++ b/src/r_defs.h @@ -633,12 +633,6 @@ struct sector_t return pos == floor? floorplane:ceilingplane; } - bool HasTag(int checktag) const; - bool HasTags() const; - void SetMainTag(int tagnum); - int GetMainTag() const; - void ClearTags(); - static void HashTags(); bool PlaneMoving(int pos); @@ -657,12 +651,9 @@ struct sector_t TObjPtr SoundTarget; short special; - short tag; short lightlevel; short seqType; // this sector's sound sequence - int nexttag,firsttag; // killough 1/30/98: improves searches for tags. - int sky; FNameNoInit SeqName; // Sound sequence name. Setting seqType non-negative will override this. @@ -897,21 +888,12 @@ struct line_t DWORD activation; // activation type int special; fixed_t Alpha; // <--- translucency (0=invisibile, FRACUNIT=opaque) - int id; // <--- same as tag or set with Line_SetIdentification int args[5]; // <--- hexen-style arguments (expanded to ZDoom's full width) - int firstid, nextid; side_t *sidedef[2]; - //DWORD sidenum[2]; // sidenum[1] will be NO_SIDE if one sided fixed_t bbox[4]; // bounding box, for the extent of the LineDef. sector_t *frontsector, *backsector; int validcount; // if == validcount, already checked int locknumber; // [Dusk] lock number for special - - - void SetMainId(int newid); - void ClearIds(); - bool HasId(int id) const; - static void HashIds(); }; // phares 3/14/98 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 f78e753d80..f6b24a70cf 100644 --- a/src/s_sound.cpp +++ b/src/s_sound.cpp @@ -383,7 +383,7 @@ void S_Start () { // kill all playing sounds at start of level (trust me - a good idea) S_StopAllChannels(); - + // Check for local sound definitions. Only reload if they differ // from the previous ones. FString LocalSndInfo; @@ -487,6 +487,11 @@ void S_PrecacheLevel () { level.info->PrecacheSounds[i].MarkUsed(); } + // Don't unload sounds that are playing right now. + for (FSoundChan *chan = Channels; chan != NULL; chan = chan->NextChan) + { + chan->SoundID.MarkUsed(); + } for (i = 1; i < S_sfx.Size(); ++i) { @@ -1311,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()) @@ -1925,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(); + } } } @@ -2083,12 +2074,6 @@ void S_ChannelEnded(FISoundChannel *ichan) evicted = (pos < len); } } - /* - else - { - evicted = false; - } - */ if (!evicted) { S_ReturnChannel(schan); @@ -2346,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) @@ -2427,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; @@ -2448,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) @@ -2470,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); @@ -2509,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; } @@ -2516,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); } } @@ -2623,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/sc_man.cpp b/src/sc_man.cpp index 05abe17841..03b52a0502 100644 --- a/src/sc_man.cpp +++ b/src/sc_man.cpp @@ -196,9 +196,22 @@ void FScanner::OpenFile (const char *name) //========================================================================== void FScanner::OpenMem (const char *name, const char *buffer, int size) +{ + OpenString(name, FString(buffer, size)); +} + +//========================================================================== +// +// FScanner :: OpenString +// +// Like OpenMem, but takes a string directly. +// +//========================================================================== + +void FScanner::OpenString (const char *name, FString buffer) { Close (); - ScriptBuffer = FString(buffer, size); + ScriptBuffer = buffer; ScriptName = name; LumpNum = -1; PrepareScript (); diff --git a/src/sc_man.h b/src/sc_man.h index 61305958f2..6e0a710ba4 100644 --- a/src/sc_man.h +++ b/src/sc_man.h @@ -21,6 +21,7 @@ public: void Open(const char *lumpname); void OpenFile(const char *filename); void OpenMem(const char *name, const char *buffer, int size); + void OpenString(const char *name, FString buffer); void OpenLumpNum(int lump); void Close(); 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..45c952805f --- /dev/null +++ b/src/sound/except.h @@ -0,0 +1,41 @@ +#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 + +#ifdef __try +#undef __try +#endif +#define __try + +#ifdef __except +#undef __except +#endif +#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 eae45c53b9..56e40a19ff 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 b5af6a4859..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; @@ -165,9 +168,6 @@ static const FEnumList OutputNames[] = { "Windows Multimedia", FMOD_OUTPUTTYPE_WINMM }, { "WinMM", FMOD_OUTPUTTYPE_WINMM }, { "WaveOut", FMOD_OUTPUTTYPE_WINMM }, -#if FMOD_VERSION < 0x43400 - { "OpenAL", FMOD_OUTPUTTYPE_OPENAL }, -#endif { "WASAPI", FMOD_OUTPUTTYPE_WASAPI }, { "ASIO", FMOD_OUTPUTTYPE_ASIO }, @@ -182,9 +182,6 @@ static const FEnumList OutputNames[] = { "SDL", 666 }, // Mac -#if FMOD_VERSION < 0x43000 - { "Sound Manager", FMOD_OUTPUTTYPE_SOUNDMANAGER }, -#endif { "Core Audio", FMOD_OUTPUTTYPE_COREAUDIO }, { NULL, 0 } @@ -307,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() @@ -327,6 +331,10 @@ public: { Stream->release(); } + if (Reader != NULL) + { + delete Reader; + } } void SetStream(FMOD::Sound *stream) @@ -596,6 +604,7 @@ private: FMOD::Channel *Channel; void *UserData; SoundStreamCallback Callback; + FileReader *Reader; FString URL; bool Ended; bool JustStarted; @@ -625,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 @@ -686,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" @@ -703,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; @@ -895,6 +876,15 @@ bool FMODSoundRenderer::Init() // Set software format eval = Enum_NumForName(SoundFormatNames, snd_output_format); format = eval >= 0 ? FMOD_SOUND_FORMAT(eval) : FMOD_SOUND_FORMAT_PCM16; + if (format == FMOD_SOUND_FORMAT_PCM8) + { + // PCM-8 sounds like garbage with anything but DirectSound. + FMOD_OUTPUTTYPE output; + if (FMOD_OK != Sys->getOutput(&output) || output != FMOD_OUTPUTTYPE_DSOUND) + { + format = FMOD_SOUND_FORMAT_PCM16; + } + } eval = Enum_NumForName(ResamplerNames, snd_resampler); resampler = eval >= 0 ? FMOD_DSP_RESAMPLER(eval) : FMOD_DSP_RESAMPLER_LINEAR; // These represented the frequency limits for hardware channels, which we never used anyway. @@ -1601,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); } //========================================================================== @@ -2047,7 +2125,7 @@ FISoundChannel *FMODSoundRenderer::CommonChannelSetup(FMOD::Channel *chan, FISou //========================================================================== // -// FMODSoundRenderer :: StopSound +// FMODSoundRenderer :: StopChannel // //========================================================================== @@ -2055,7 +2133,10 @@ void FMODSoundRenderer::StopChannel(FISoundChannel *chan) { if (chan != NULL && chan->SysChannel != NULL) { - ((FMOD::Channel *)chan->SysChannel)->stop(); + if (((FMOD::Channel *)chan->SysChannel)->stop() == FMOD_ERR_INVALID_HANDLE) + { // The channel handle was invalid; pretend it ended. + S_ChannelEnded(chan); + } } } @@ -3092,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..5b33100a51 --- /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 "mpg123_decoder.h" +#include "files.h" +#include "except.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..447de49f98 --- /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..52376a6c73 --- /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 "sndfile_decoder.h" +#include "templates.h" +#include "files.h" +#include "xs_Float.h" +#include "except.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 57de62984e..84a21728c1 100644 --- a/src/tarray.h +++ b/src/tarray.h @@ -160,6 +160,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/thingdef/thingdef_codeptr.cpp b/src/thingdef/thingdef_codeptr.cpp index a467664722..dc2cbc418c 100644 --- a/src/thingdef/thingdef_codeptr.cpp +++ b/src/thingdef/thingdef_codeptr.cpp @@ -1679,6 +1679,7 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_RailAttack) PARAM_FLOAT_OPT (driftspeed) { driftspeed = 1; } PARAM_CLASS_OPT (spawnclass, AActor){ spawnclass = NULL; } PARAM_FIXED_OPT (spawnofs_z) { spawnofs_z = 0; } + PARAM_INT_OPT (SpiralOffset) { SpiralOffset = 270; } if (range == 0) range = 8192*FRACUNIT; if (sparsity == 0) sparsity=1.0; @@ -1709,7 +1710,7 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_RailAttack) slope = pr_crailgun.Random2() * (spread_z / 255); } - P_RailAttack (self, damage, spawnofs_xy, spawnofs_z, color1, color2, maxdiff, flags, pufftype, angle, slope, range, duration, sparsity, driftspeed, spawnclass); + P_RailAttack (self, damage, spawnofs_xy, spawnofs_z, color1, color2, maxdiff, flags, pufftype, angle, slope, range, duration, sparsity, driftspeed, spawnclass, SpiralOffset); return 0; } @@ -1745,6 +1746,7 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_CustomRailgun) PARAM_FLOAT_OPT (driftspeed) { driftspeed = 1; } PARAM_CLASS_OPT (spawnclass, AActor){ spawnclass = NULL; } PARAM_FIXED_OPT (spawnofs_z) { spawnofs_z = 0; } + PARAM_INT_OPT (SpiralOffset) { SpiralOffset = 270; } if (range == 0) range = 8192*FRACUNIT; if (sparsity == 0) sparsity = 1; @@ -1828,7 +1830,7 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_CustomRailgun) slopeoffset = pr_crailgun.Random2() * (spread_z / 255); } - P_RailAttack (self, damage, spawnofs_xy, spawnofs_z, color1, color2, maxdiff, flags, pufftype, angleoffset, slopeoffset, range, duration, sparsity, driftspeed, spawnclass); + P_RailAttack (self, damage, spawnofs_xy, spawnofs_z, color1, color2, maxdiff, flags, pufftype, angleoffset, slopeoffset, range, duration, sparsity, driftspeed, spawnclass,SpiralOffset); self->x = saved_x; self->y = saved_y; @@ -4871,7 +4873,7 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_LineEffect) if ((oldjunk.special = special)) // Linedef type { oldjunk.tag = tag; // Sector tag for linedef - P_TranslateLineDef(&junk, &oldjunk, false); // Turn into native type + P_TranslateLineDef(&junk, &oldjunk); // Turn into native type res = !!P_ExecuteSpecial(junk.special, NULL, self, false, junk.args[0], junk.args[1], junk.args[2], junk.args[3], junk.args[4]); if (res && !(junk.flags & ML_REPEAT_SPECIAL)) // If only once, diff --git a/src/v_draw.cpp b/src/v_draw.cpp index e2890841b9..ce502c772e 100644 --- a/src/v_draw.cpp +++ b/src/v_draw.cpp @@ -333,6 +333,7 @@ bool DCanvas::ParseDrawTextureTags (FTexture *img, double x, double y, DWORD tag int intval; bool translationset = false; bool virtBottom; + bool fillcolorset = false; if (img == NULL || img->UseType == FTexture::TEX_Null) { @@ -539,6 +540,7 @@ bool DCanvas::ParseDrawTextureTags (FTexture *img, double x, double y, DWORD tag case DTA_FillColor: parms->fillcolor = va_arg(tags, uint32); + fillcolorset = true; break; case DTA_Translation: @@ -711,7 +713,7 @@ bool DCanvas::ParseDrawTextureTags (FTexture *img, double x, double y, DWORD tag if (parms->style.BlendOp == 255) { - if (parms->fillcolor != ~0u) + if (fillcolorset) { if (parms->alphaChannel) { diff --git a/src/v_video.h b/src/v_video.h index 5250cdaa97..7593b7b4b5 100644 --- a/src/v_video.h +++ b/src/v_video.h @@ -74,7 +74,7 @@ enum DTA_DestWidth, // width of area to draw to DTA_DestHeight, // height of area to draw to DTA_Alpha, // alpha value for translucency - DTA_FillColor, // color to stencil onto the destination + DTA_FillColor, // color to stencil onto the destination (RGB is the color for truecolor drawers, A is the palette index for paletted drawers) DTA_Translation, // translation table to recolor the source DTA_AlphaChannel, // bool: the source is an alpha channel; used with DTA_FillColor DTA_Clean, // bool: scale texture size and position by CleanXfac and CleanYfac diff --git a/src/version.h b/src/version.h index f45f58b2fc..3d28dd2ac3 100644 --- a/src/version.h +++ b/src/version.h @@ -76,7 +76,7 @@ const char *GetVersionString(); // Use 4500 as the base git save version, since it's higher than the // SVN revision ever got. -#define SAVEVER 4522 +#define SAVEVER 4523 #define SAVEVERSTRINGIFY2(x) #x #define SAVEVERSTRINGIFY(x) SAVEVERSTRINGIFY2(x) 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 2dabe7de9a..1d2bae8e1e 100644 --- a/src/win32/i_system.cpp +++ b/src/win32/i_system.cpp @@ -1600,13 +1600,20 @@ unsigned int I_MakeRNGSeed() FString I_GetLongPathName(FString shortpath) { - DWORD buffsize = GetLongPathName(shortpath.GetChars(), NULL, 0); + static TOptWin32Proc + GetLongPathNameA("kernel32.dll", "GetLongPathNameA"); + + // Doesn't exist on NT4 + if (GetLongPathName == NULL) + return shortpath; + + 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(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 647a08d134..566ca19778 100644 --- a/src/win32/i_system.h +++ b/src/win32/i_system.h @@ -51,6 +51,30 @@ typedef enum { extern os_t OSPlatform; +// Helper template so that we can access newer Win32 functions with a single static +// variable declaration. If this were C++11 it could be totally transparent. +template +class TOptWin32Proc +{ + static Proto GetOptionalWin32Proc(const char* module, const char* function) + { + HMODULE hmodule = GetModuleHandle(module); + if (hmodule == NULL) + return NULL; + + return (Proto)GetProcAddress(hmodule, function); + } + +public: + const Proto Call; + + 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; } +}; + // Called by DoomMain. void I_Init (void); diff --git a/src/win32/win32video.cpp b/src/win32/win32video.cpp index 8aa74bfb58..690bb5725b 100644 --- a/src/win32/win32video.cpp +++ b/src/win32/win32video.cpp @@ -414,7 +414,10 @@ void Win32Video::DumpAdapters() HMONITOR hm = D3D->GetAdapterMonitor(i); MONITORINFOEX mi; mi.cbSize = sizeof(mi); - if (GetMonitorInfo(hm, &mi)) + + TOptWin32Proc GetMonitorInfo("user32.dll", "GetMonitorInfoW"); + assert(GetMonitorInfo != NULL); // Missing in NT4, but so is D3D + if (GetMonitorInfo.Call(hm, &mi)) { mysnprintf(moreinfo, countof(moreinfo), " [%ldx%ld @ (%ld,%ld)]%s", mi.rcMonitor.right - mi.rcMonitor.left, diff --git a/wadsrc/static/actors/actor.txt b/wadsrc/static/actors/actor.txt index 0d32cf8939..d993a4de72 100644 --- a/wadsrc/static/actors/actor.txt +++ b/wadsrc/static/actors/actor.txt @@ -178,7 +178,7 @@ ACTOR Actor native //: Thinker action native A_Jump(int chance = 256, state label, ...); action native A_CustomMissile(class missiletype, float spawnheight = 32, int spawnofs_xy = 0, float angle = 0, int flags = 0, float pitch = 0, int ptr = AAPTR_TARGET); action native A_CustomBulletAttack(float/*angle*/ spread_xy, float/*angle*/ spread_z, int numbullets, int damageperbullet, class pufftype = "BulletPuff", float range = 0, int flags = 0, int ptr = AAPTR_TARGET); - action native A_CustomRailgun(int damage, int spawnofs_xy = 0, color color1 = "", color color2 = "", int flags = 0, int aim = 0, float maxdiff = 0, class pufftype = "BulletPuff", float/*angle*/ spread_xy = 0, float/*angle*/ spread_z = 0, float range = 0, int duration = 0, float sparsity = 1.0, float driftspeed = 1.0, class spawnclass = "none", float spawnofs_z = 0); + action native A_CustomRailgun(int damage, int spawnofs_xy = 0, color color1 = "", color color2 = "", int flags = 0, int aim = 0, float maxdiff = 0, class pufftype = "BulletPuff", float/*angle*/ spread_xy = 0, float/*angle*/ spread_z = 0, float range = 0, int duration = 0, float sparsity = 1.0, float driftspeed = 1.0, class spawnclass = "none", float spawnofs_z = 0, int spiraloffset = 270); action native A_JumpIfHealthLower(int health, state label, int ptr_selector = AAPTR_DEFAULT); action native A_JumpIfCloser(float distance, state label); action native A_JumpIfTracerCloser(float distance, state label); diff --git a/wadsrc/static/actors/chex/chexweapons.txt b/wadsrc/static/actors/chex/chexweapons.txt index 755e7ad451..0cdb968491 100644 --- a/wadsrc/static/actors/chex/chexweapons.txt +++ b/wadsrc/static/actors/chex/chexweapons.txt @@ -21,6 +21,7 @@ actor MiniZorcher : Pistol states { Spawn: + MINZ A -1 stop } } diff --git a/wadsrc/static/actors/shared/inventory.txt b/wadsrc/static/actors/shared/inventory.txt index 4fa1d70bd5..594848781d 100644 --- a/wadsrc/static/actors/shared/inventory.txt +++ b/wadsrc/static/actors/shared/inventory.txt @@ -11,7 +11,7 @@ ACTOR Inventory native action native A_CustomPunch(int damage, bool norandom = false, int flags = CPF_USEAMMO, class pufftype = "BulletPuff", float range = 0, float lifesteal = 0, int lifestealmax = 0, class armorbonustype = "ArmorBonus"); action native A_FireBullets(float/*angle*/ spread_xy, float/*angle*/ spread_z, int numbullets, int damageperbullet, class pufftype = "BulletPuff", int flags = 1, float range = 0); action native A_FireCustomMissile(class missiletype, float angle = 0, bool useammo = true, int spawnofs_xy = 0, float spawnheight = 0, bool aimatangle = false, float pitch = 0); - action native A_RailAttack(int damage, int spawnofs_xy = 0, bool useammo = true, color color1 = "", color color2 = "", int flags = 0, float maxdiff = 0, class pufftype = "BulletPuff", float/*angle*/ spread_xy = 0, float/*angle*/ spread_z = 0, float range = 0, int duration = 0, float sparsity = 1.0, float driftspeed = 1.0, class spawnclass = "none", float spawnofs_z = 0); + action native A_RailAttack(int damage, int spawnofs_xy = 0, bool useammo = true, color color1 = "", color color2 = "", int flags = 0, float maxdiff = 0, class pufftype = "BulletPuff", float/*angle*/ spread_xy = 0, float/*angle*/ spread_z = 0, float range = 0, int duration = 0, float sparsity = 1.0, float driftspeed = 1.0, class spawnclass = "none", float spawnofs_z = 0, int spiraloffset = 270); action native A_Light(int extralight); action native A_Light0(); action native A_Light1(); diff --git a/wadsrc/static/mapinfo/chex.txt b/wadsrc/static/mapinfo/chex.txt index 9bd974bc7f..ba93ec36de 100644 --- a/wadsrc/static/mapinfo/chex.txt +++ b/wadsrc/static/mapinfo/chex.txt @@ -124,6 +124,7 @@ DoomEdNums 3003 = Flembrane 3004 = FlemoidusCommonus 3006 = ChexSoul + 5010 = MiniZorcher } skill baby diff --git a/wadsrc/static/mapinfo/common.txt b/wadsrc/static/mapinfo/common.txt index eece14c84a..f26ca67315 100644 --- a/wadsrc/static/mapinfo/common.txt +++ b/wadsrc/static/mapinfo/common.txt @@ -37,6 +37,7 @@ DoomEdNums 5001 = PointPusher 5002 = PointPuller 5004 = FS_Mapspot + 5006 = SkyCamCompat 5061 = InvisibleBridge32 5064 = InvisibleBridge16 5065 = InvisibleBridge8 diff --git a/wadsrc/static/menudef.txt b/wadsrc/static/menudef.txt index 07f841545a..f15f91dff3 100644 --- a/wadsrc/static/menudef.txt +++ b/wadsrc/static/menudef.txt @@ -1390,7 +1390,6 @@ OptionString SoundOutputsWindows "WASAPI", "Vista WASAPI" "ASIO", "ASIO" "WaveOut", "WaveOut" - "OpenAL", "OpenAL (very beta)" "No sound", "No sound" } @@ -1408,11 +1407,15 @@ OptionString SoundOutputsUnix OptionString SoundOutputsMac { - "Sound Manager", "Sound Manager" "Core Audio", "Core Audio" "No sound", "No sound" } +OptionString ALDevices +{ + // filled in by the sound code +} + OptionString OutputFormats { "PCM-8", "8-bit" @@ -1444,6 +1447,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 @@ -1458,28 +1515,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" @@ -1513,8 +1581,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 @@ -1536,7 +1602,7 @@ OptionMenu AdvSoundOptions OptionValue ModReplayers { - 0.0, "FMOD" + 0.0, "Sound System" 1.0, "foo_dumb" } diff --git a/wadsrc/static/sprites/minza0.png b/wadsrc/static/sprites/minza0.png new file mode 100644 index 0000000000..6a00cb73db Binary files /dev/null and b/wadsrc/static/sprites/minza0.png differ 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 9d6cfcca2e..82df5a6f08 100644 --- a/zdoom.vcproj +++ b/zdoom.vcproj @@ -57,7 +57,7 @@ OmitFramePointers="true" WholeProgramOptimization="false" AdditionalIncludeDirectories="src\zscript;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\zscript;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\zscript;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\zscript;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" @@ -890,6 +890,10 @@ RelativePath=".\src\p_switch.cpp" > + + @@ -1451,6 +1455,10 @@ RelativePath=".\src\p_spec.h" > + + @@ -2473,6 +2481,10 @@ + + @@ -2585,6 +2597,14 @@ RelativePath=".\src\sound\music_xmi_midiout.cpp" > + + + +