From 69af01d6293b0514f15ed14fddbd728e1ddb67db Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Sun, 30 Jun 2013 07:44:19 -0700 Subject: [PATCH] Use SDL_sound when available to decode files --- FindSDL_sound.cmake | 382 +++++ src/CMakeLists.txt | 13 +- src/sound/oalsdlsound.cpp | 494 ++++++ src/sound/oalsdlsound.h | 66 + src/sound/oalsound.cpp | 3362 ++++++++++++++++++++----------------- src/sound/oalsound.h | 5 +- 6 files changed, 2744 insertions(+), 1578 deletions(-) create mode 100644 FindSDL_sound.cmake create mode 100644 src/sound/oalsdlsound.cpp create mode 100644 src/sound/oalsdlsound.h diff --git a/FindSDL_sound.cmake b/FindSDL_sound.cmake new file mode 100644 index 000000000..97d2a84c9 --- /dev/null +++ b/FindSDL_sound.cmake @@ -0,0 +1,382 @@ +# - Locates the SDL_sound library +# +# This module depends on SDL being found and +# must be called AFTER FindSDL.cmake is called. +# +# This module defines +# SDL_SOUND_INCLUDE_DIR, where to find SDL_sound.h +# SDL_SOUND_FOUND, if false, do not try to link to SDL_sound +# SDL_SOUND_LIBRARIES, this contains the list of libraries that you need +# to link against. This is a read-only variable and is marked INTERNAL. +# SDL_SOUND_EXTRAS, this is an optional variable for you to add your own +# flags to SDL_SOUND_LIBRARIES. This is prepended to SDL_SOUND_LIBRARIES. +# This is available mostly for cases this module failed to anticipate for +# and you must add additional flags. This is marked as ADVANCED. +# SDL_SOUND_VERSION_STRING, human-readable string containing the version of SDL_sound +# +# This module also defines (but you shouldn't need to use directly) +# SDL_SOUND_LIBRARY, the name of just the SDL_sound library you would link +# against. Use SDL_SOUND_LIBRARIES for you link instructions and not this one. +# And might define the following as needed +# MIKMOD_LIBRARY +# MODPLUG_LIBRARY +# OGG_LIBRARY +# VORBIS_LIBRARY +# SMPEG_LIBRARY +# FLAC_LIBRARY +# SPEEX_LIBRARY +# +# Typically, you should not use these variables directly, and you should use +# SDL_SOUND_LIBRARIES which contains SDL_SOUND_LIBRARY and the other audio libraries +# (if needed) to successfully compile on your system. +# +# Created by Eric Wing. +# This module is a bit more complicated than the other FindSDL* family modules. +# The reason is that SDL_sound can be compiled in a large variety of different ways +# which are independent of platform. SDL_sound may dynamically link against other 3rd +# party libraries to get additional codec support, such as Ogg Vorbis, SMPEG, ModPlug, +# MikMod, FLAC, Speex, and potentially others. +# Under some circumstances which I don't fully understand, +# there seems to be a requirement +# that dependent libraries of libraries you use must also be explicitly +# linked against in order to successfully compile. SDL_sound does not currently +# have any system in place to know how it was compiled. +# So this CMake module does the hard work in trying to discover which 3rd party +# libraries are required for building (if any). +# This module uses a brute force approach to create a test program that uses SDL_sound, +# and then tries to build it. If the build fails, it parses the error output for +# known symbol names to figure out which libraries are needed. +# +# Responds to the $SDLDIR and $SDLSOUNDDIR environmental variable that would +# correspond to the ./configure --prefix=$SDLDIR used in building SDL. +# +# On OSX, this will prefer the Framework version (if found) over others. +# People will have to manually change the cache values of +# SDL_LIBRARY to override this selectionor set the CMake environment +# CMAKE_INCLUDE_PATH to modify the search paths. + +#============================================================================= +# Copyright 2005-2009 Kitware, Inc. +# Copyright 2012 Benjamin Eikel +# +# Distributed under the OSI-approved BSD License (the "License"); +# see accompanying file Copyright.txt for details. +# +# This software is distributed WITHOUT ANY WARRANTY; without even the +# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the License for more information. +#============================================================================= +# (To distribute this file outside of CMake, substitute the full +# License text for the above reference.) + +set(SDL_SOUND_EXTRAS "" CACHE STRING "SDL_sound extra flags") +mark_as_advanced(SDL_SOUND_EXTRAS) + +# Find SDL_sound.h +find_path(SDL_SOUND_INCLUDE_DIR SDL_sound.h + HINTS + ENV SDLSOUNDDIR + ENV SDLDIR + PATH_SUFFIXES SDL SDL12 SDL11 + ) + +find_library(SDL_SOUND_LIBRARY + NAMES SDL_sound + HINTS + ENV SDLSOUNDDIR + ENV SDLDIR + ) + +if(SDL_FOUND AND SDL_SOUND_INCLUDE_DIR AND SDL_SOUND_LIBRARY) + + # CMake is giving me problems using TRY_COMPILE with the CMAKE_FLAGS + # for the :STRING syntax if I have multiple values contained in a + # single variable. This is a problem for the SDL_LIBRARY variable + # because it does just that. When I feed this variable to the command, + # only the first value gets the appropriate modifier (e.g. -I) and + # the rest get dropped. + # To get multiple single variables to work, I must separate them with a "\;" + # I could go back and modify the FindSDL.cmake module, but that's kind of painful. + # The solution would be to try something like: + # set(SDL_TRY_COMPILE_LIBRARY_LIST "${SDL_TRY_COMPILE_LIBRARY_LIST}\;${CMAKE_THREAD_LIBS_INIT}") + # Instead, it was suggested on the mailing list to write a temporary CMakeLists.txt + # with a temporary test project and invoke that with TRY_COMPILE. + # See message thread "Figuring out dependencies for a library in order to build" + # 2005-07-16 + # try_compile( + # MY_RESULT + # ${CMAKE_BINARY_DIR} + # ${PROJECT_SOURCE_DIR}/DetermineSoundLibs.c + # CMAKE_FLAGS + # -DINCLUDE_DIRECTORIES:STRING=${SDL_INCLUDE_DIR}\;${SDL_SOUND_INCLUDE_DIR} + # -DLINK_LIBRARIES:STRING=${SDL_SOUND_LIBRARY}\;${SDL_LIBRARY} + # OUTPUT_VARIABLE MY_OUTPUT + # ) + + # To minimize external dependencies, create a sdlsound test program + # which will be used to figure out if additional link dependencies are + # required for the link phase. + file(WRITE ${PROJECT_BINARY_DIR}/CMakeTmp/DetermineSoundLibs.c + "#include \"SDL_sound.h\" + #include \"SDL.h\" + int main(int argc, char* argv[]) + { + Sound_AudioInfo desired; + Sound_Sample* sample; + + SDL_Init(0); + Sound_Init(); + + /* This doesn't actually have to work, but Init() is a no-op + * for some of the decoders, so this should force more symbols + * to be pulled in. + */ + sample = Sound_NewSampleFromFile(argv[1], &desired, 4096); + + Sound_Quit(); + SDL_Quit(); + return 0; + }" + ) + + # Calling + # target_link_libraries(DetermineSoundLibs "${SDL_SOUND_LIBRARY} ${SDL_LIBRARY}) + # causes problems when SDL_LIBRARY looks like + # /Library/Frameworks/SDL.framework;-framework Cocoa + # The ;-framework Cocoa seems to be confusing CMake once the OS X + # framework support was added. I was told that breaking up the list + # would fix the problem. + set(TMP_TRY_LIBS) + foreach(lib ${SDL_SOUND_LIBRARY} ${SDL_LIBRARY}) + set(TMP_TRY_LIBS "${TMP_TRY_LIBS} \"${lib}\"") + endforeach() + + # message("TMP_TRY_LIBS ${TMP_TRY_LIBS}") + + # Write the CMakeLists.txt and test project + # Weird, this is still sketchy. If I don't quote the variables + # in the TARGET_LINK_LIBRARIES, I seem to loose everything + # in the SDL_LIBRARY string after the "-framework". + # But if I quote the stuff in INCLUDE_DIRECTORIES, it doesn't work. + file(WRITE ${PROJECT_BINARY_DIR}/CMakeTmp/CMakeLists.txt + "cmake_minimum_required(VERSION 2.8) + project(DetermineSoundLibs C) + include_directories(${SDL_INCLUDE_DIR} ${SDL_SOUND_INCLUDE_DIR}) + add_executable(DetermineSoundLibs DetermineSoundLibs.c) + target_link_libraries(DetermineSoundLibs ${TMP_TRY_LIBS})" + ) + + try_compile( + MY_RESULT + ${PROJECT_BINARY_DIR}/CMakeTmp + ${PROJECT_BINARY_DIR}/CMakeTmp + DetermineSoundLibs + OUTPUT_VARIABLE MY_OUTPUT + ) + + # message("${MY_RESULT}") + # message(${MY_OUTPUT}) + + if(NOT MY_RESULT) + + # I expect that MPGLIB, VOC, WAV, AIFF, and SHN are compiled in statically. + # I think Timidity is also compiled in statically. + # I've never had to explcitly link against Quicktime, so I'll skip that for now. + + set(SDL_SOUND_LIBRARIES_TMP ${SDL_SOUND_LIBRARY}) + + # Find MikMod + if("${MY_OUTPUT}" MATCHES "MikMod_") + find_library(MIKMOD_LIBRARY + NAMES libmikmod-coreaudio mikmod + PATHS + ENV MIKMODDIR + ENV SDLSOUNDDIR + ENV SDLDIR + /sw + /opt/local + /opt/csw + /opt + PATH_SUFFIXES + lib + ) + if(MIKMOD_LIBRARY) + set(SDL_SOUND_LIBRARIES_TMP ${SDL_SOUND_LIBRARIES_TMP} ${MIKMOD_LIBRARY}) + endif(MIKMOD_LIBRARY) + endif("${MY_OUTPUT}" MATCHES "MikMod_") + + # Find ModPlug + if("${MY_OUTPUT}" MATCHES "MODPLUG_") + find_library(MODPLUG_LIBRARY + NAMES modplug + PATHS + ENV MODPLUGDIR + ENV SDLSOUNDDIR + ENV SDLDIR + /sw + /opt/local + /opt/csw + /opt + PATH_SUFFIXES + lib + ) + if(MODPLUG_LIBRARY) + set(SDL_SOUND_LIBRARIES_TMP ${SDL_SOUND_LIBRARIES_TMP} ${MODPLUG_LIBRARY}) + endif() + endif() + + + # Find Ogg and Vorbis + if("${MY_OUTPUT}" MATCHES "ov_") + find_library(VORBIS_LIBRARY + NAMES vorbis Vorbis VORBIS + PATHS + ENV VORBISDIR + ENV OGGDIR + ENV SDLSOUNDDIR + ENV SDLDIR + /sw + /opt/local + /opt/csw + /opt + PATH_SUFFIXES + lib + ) + if(VORBIS_LIBRARY) + set(SDL_SOUND_LIBRARIES_TMP ${SDL_SOUND_LIBRARIES_TMP} ${VORBIS_LIBRARY}) + endif() + + find_library(OGG_LIBRARY + NAMES ogg Ogg OGG + PATHS + ENV OGGDIR + ENV VORBISDIR + ENV SDLSOUNDDIR + ENV SDLDIR + /sw + /opt/local + /opt/csw + /opt + PATH_SUFFIXES + lib + ) + if(OGG_LIBRARY) + set(SDL_SOUND_LIBRARIES_TMP ${SDL_SOUND_LIBRARIES_TMP} ${OGG_LIBRARY}) + endif() + endif() + + + # Find SMPEG + if("${MY_OUTPUT}" MATCHES "SMPEG_") + find_library(SMPEG_LIBRARY + NAMES smpeg SMPEG Smpeg SMpeg + PATHS + ENV SMPEGDIR + ENV SDLSOUNDDIR + ENV SDLDIR + /sw + /opt/local + /opt/csw + /opt + PATH_SUFFIXES + lib + ) + if(SMPEG_LIBRARY) + set(SDL_SOUND_LIBRARIES_TMP ${SDL_SOUND_LIBRARIES_TMP} ${SMPEG_LIBRARY}) + endif() + endif() + + + # Find FLAC + if("${MY_OUTPUT}" MATCHES "FLAC_") + find_library(FLAC_LIBRARY + NAMES flac FLAC + PATHS + ENV FLACDIR + ENV SDLSOUNDDIR + ENV SDLDIR + /sw + /opt/local + /opt/csw + /opt + PATH_SUFFIXES + lib + ) + if(FLAC_LIBRARY) + set(SDL_SOUND_LIBRARIES_TMP ${SDL_SOUND_LIBRARIES_TMP} ${FLAC_LIBRARY}) + endif() + endif() + + + # Hmmm...Speex seems to depend on Ogg. This might be a problem if + # the TRY_COMPILE attempt gets blocked at SPEEX before it can pull + # in the Ogg symbols. I'm not sure if I should duplicate the ogg stuff + # above for here or if two ogg entries will screw up things. + if("${MY_OUTPUT}" MATCHES "speex_") + find_library(SPEEX_LIBRARY + NAMES speex SPEEX + PATHS + ENV SPEEXDIR + ENV SDLSOUNDDIR + ENV SDLDIR + /sw + /opt/local + /opt/csw + /opt + PATH_SUFFIXES + lib + ) + if(SPEEX_LIBRARY) + set(SDL_SOUND_LIBRARIES_TMP ${SDL_SOUND_LIBRARIES_TMP} ${SPEEX_LIBRARY}) + endif() + + # Find OGG (needed for Speex) + # We might have already found Ogg for Vorbis, so skip it if so. + if(NOT OGG_LIBRARY) + find_library(OGG_LIBRARY + NAMES ogg Ogg OGG + PATHS + ENV OGGDIR + ENV VORBISDIR + ENV SPEEXDIR + ENV SDLSOUNDDIR + ENV SDLDIR + /sw + /opt/local + /opt/csw + /opt + PATH_SUFFIXES lib + ) + if(OGG_LIBRARY) + set(SDL_SOUND_LIBRARIES_TMP ${SDL_SOUND_LIBRARIES_TMP} ${OGG_LIBRARY}) + endif() + endif() + endif() + + set(SDL_SOUND_LIBRARIES ${SDL_SOUND_EXTRAS} ${SDL_SOUND_LIBRARIES_TMP} CACHE INTERNAL "SDL_sound and dependent libraries") + else() + set(SDL_SOUND_LIBRARIES ${SDL_SOUND_EXTRAS} ${SDL_SOUND_LIBRARY} CACHE INTERNAL "SDL_sound and dependent libraries") + endif() +endif() + +if(SDL_SOUND_INCLUDE_DIR AND EXISTS "${SDL_SOUND_INCLUDE_DIR}/SDL_sound.h") + file(STRINGS "${SDL_SOUND_INCLUDE_DIR}/SDL_sound.h" SDL_SOUND_VERSION_MAJOR_LINE REGEX "^#define[ \t]+SOUND_VER_MAJOR[ \t]+[0-9]+$") + file(STRINGS "${SDL_SOUND_INCLUDE_DIR}/SDL_sound.h" SDL_SOUND_VERSION_MINOR_LINE REGEX "^#define[ \t]+SOUND_VER_MINOR[ \t]+[0-9]+$") + file(STRINGS "${SDL_SOUND_INCLUDE_DIR}/SDL_sound.h" SDL_SOUND_VERSION_PATCH_LINE REGEX "^#define[ \t]+SOUND_VER_PATCH[ \t]+[0-9]+$") + string(REGEX REPLACE "^#define[ \t]+SOUND_VER_MAJOR[ \t]+([0-9]+)$" "\\1" SDL_SOUND_VERSION_MAJOR "${SDL_SOUND_VERSION_MAJOR_LINE}") + string(REGEX REPLACE "^#define[ \t]+SOUND_VER_MINOR[ \t]+([0-9]+)$" "\\1" SDL_SOUND_VERSION_MINOR "${SDL_SOUND_VERSION_MINOR_LINE}") + string(REGEX REPLACE "^#define[ \t]+SOUND_VER_PATCH[ \t]+([0-9]+)$" "\\1" SDL_SOUND_VERSION_PATCH "${SDL_SOUND_VERSION_PATCH_LINE}") + set(SDL_SOUND_VERSION_STRING ${SDL_SOUND_VERSION_MAJOR}.${SDL_SOUND_VERSION_MINOR}.${SDL_SOUND_VERSION_PATCH}) + unset(SDL_SOUND_VERSION_MAJOR_LINE) + unset(SDL_SOUND_VERSION_MINOR_LINE) + unset(SDL_SOUND_VERSION_PATCH_LINE) + unset(SDL_SOUND_VERSION_MAJOR) + unset(SDL_SOUND_VERSION_MINOR) + unset(SDL_SOUND_VERSION_PATCH) +endif() + +include(FindPackageHandleStandardArgs) + +FIND_PACKAGE_HANDLE_STANDARD_ARGS(SDL_sound + REQUIRED_VARS SDL_SOUND_LIBRARY SDL_SOUND_INCLUDE_DIR + VERSION_VAR SDL_SOUND_VERSION_STRING) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 822f10331..786375b3b 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -202,11 +202,22 @@ else( WIN32 ) endif( WIN32 ) +set( OAL_SOURCES sound/oalsound.cpp ) if( NOT NO_OPENAL ) find_package( OpenAL ) if( OPENAL_FOUND ) include_directories( ${OPENAL_INCLUDE_DIR} ) set( ZDOOM_LIBS ${OPENAL_LIBRARY} ${ZDOOM_LIBS} ) + find_package( SDL ) + if( SDL_FOUND ) + find_package( SDL_sound ) + if( SDL_SOUND_FOUND ) + set_source_files_properties( sound/oalsound.cpp PROPERTIES COMPILE_FLAGS "-DWITH_SDL_SOUND=1" ) + set( OAL_SOURCES ${OAL_SOURCES} sound/oalsdlsound.cpp ) + include_directories( ${SDL_SOUND_INCLUDE_DIR} ) + set( ZDOOM_LIBS ${SDL_SOUND_LIBRARIES} ${ZDOOM_LIBS} ) + endif( SDL_SOUND_FOUND ) + endif( SDL_FOUND ) else( OPENAL_FOUND ) set( NO_OPENAL ON ) endif( OPENAL_FOUND ) @@ -900,7 +911,7 @@ add_executable( zdoom WIN32 sound/music_softsynth_mididevice.cpp sound/music_timidity_mididevice.cpp sound/music_win_mididevice.cpp - sound/oalsound.cpp + ${OAL_SOURCES} sound/music_pseudo_mididevice.cpp textures/animations.cpp textures/anim_switches.cpp diff --git a/src/sound/oalsdlsound.cpp b/src/sound/oalsdlsound.cpp new file mode 100644 index 000000000..6e704cddc --- /dev/null +++ b/src/sound/oalsdlsound.cpp @@ -0,0 +1,494 @@ +/* +** oalsdlsound.cpp +** Interface for SDL_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 "doomstat.h" +#include "templates.h" +#include "oalsound.h" +#include "oalsdlsound.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 +#include + + +struct RWSubFile { +private: + FILE *fp; + size_t start; + size_t length; + +public: + RWSubFile(FILE *f, size_t offset, size_t len) + : fp(f), start(offset), length(len) + { + fseek(fp, start, SEEK_SET); + } + ~RWSubFile() + { fclose(fp); } + + static int seek(SDL_RWops *context, int offset, int whence) + { + RWSubFile *self = static_cast(context->hidden.unknown.data1); + + if(whence == SEEK_END) + { + if(offset <= 0) + offset = self->length + offset; + } + else if(whence == SEEK_CUR) + offset = offset + ftell(self->fp) - self->start; + else if(whence != SEEK_SET) + { + SDL_SetError("Invalid seek mode"); + return -1; + } + + if(offset >= 0 && size_t(offset) <= self->length) + { + if(fseek(self->fp, offset + self->start, SEEK_SET) == 0) + return offset; + } + + SDL_SetError("Invalid file seek"); + return -1; + } + + static int read(SDL_RWops *context, void *ptr, int size, int maxnum) + { + RWSubFile *self = static_cast(context->hidden.unknown.data1); + return fread(ptr, size, maxnum, self->fp); + } + + static int write(SDL_RWops *context, const void *ptr, int size, int num) + { + RWSubFile *self = static_cast(context->hidden.unknown.data1); + return fwrite(ptr, size, num, self->fp); + } + + static int close(SDL_RWops *context) + { + RWSubFile *self = static_cast(context->hidden.unknown.data1); + if(context->type != 0xdeadbeef) + { + SDL_SetError("Wrong kind of RWops for RWSubfile::close"); + return -1; + } + + delete self; + SDL_FreeRW(context); + + return 0; + } +}; + + +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__) + + +bool OpenALSoundStream::SetupSource() +{ + if(Renderer->FreeSfx.size() == 0) + { + FSoundChan *lowest = Renderer->FindLowestChannel(); + if(lowest) Renderer->StopChannel(lowest); + + if(Renderer->FreeSfx.size() == 0) + return false; + } + Source = Renderer->FreeSfx.back(); + Renderer->FreeSfx.pop_back(); + + 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, Renderer->MusicVolume); + alSourcef(Source, AL_GAIN, Volume*Renderer->MusicVolume); + 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(Buffers.size(), &Buffers[0]); + return (getALError() == AL_NO_ERROR); +} + +OpenALSoundStream::OpenALSoundStream(OpenALSoundRenderer *renderer) + : Renderer(renderer), Sample(NULL), Source(0), Playing(false), + Looping(false), Buffers(4), SampleRate(0), Format(0), + NeedSwab(false), Volume(1.f) +{ + for(size_t i = 0;i < Buffers.size();i++) + Buffers[i] = 0; + Renderer->Streams.push_back(this); +} + +OpenALSoundStream::~OpenALSoundStream() +{ + Playing = false; + + Sound_FreeSample(Sample); + Sample = NULL; + + if(Source) + { + alSourceRewind(Source); + alSourcei(Source, AL_BUFFER, 0); + + Renderer->FreeSfx.push_back(Source); + Source = 0; + } + + if(Buffers.size() > 0) + { + alDeleteBuffers(Buffers.size(), &Buffers[0]); + Buffers.clear(); + } + getALError(); + + Renderer->Streams.erase(std::find(Renderer->Streams.begin(), + Renderer->Streams.end(), this)); + Renderer = NULL; +} + +bool OpenALSoundStream::Play(bool looping, float vol) +{ + if(Playing) + return true; + + alSourceRewind(Source); + alSourcei(Source, AL_BUFFER, 0); + + Looping = looping; + SetVolume(vol); + + for(size_t i = 0;i < Buffers.size();i++) + { + size_t count = Sound_Decode(Sample); + if(count == 0 && Looping) + { + Sound_Seek(Sample, 0); + count = Sound_Decode(Sample); + } + alBufferData(Buffers[i], Format, GetData(count), count, SampleRate); + } + Playing = (getALError() == AL_NO_ERROR); + if(Playing) + { + alSourceQueueBuffers(Source, Buffers.size(), &Buffers[0]); + alSourcePlay(Source); + Playing = (getALError() == AL_NO_ERROR); + } + return Playing; +} + +void OpenALSoundStream::Stop() +{ + alSourceRewind(Source); + alSourcei(Source, AL_BUFFER, 0); +} + +bool OpenALSoundStream::SetPaused(bool paused) +{ + if(paused) alSourcePause(Source); + else alSourcePlay(Source); + return (getALError() == AL_NO_ERROR); +} + +void OpenALSoundStream::SetVolume(float vol) +{ + if(vol >= 0.f) Volume = vol; + alSourcef(Source, AL_GAIN, Volume*Renderer->MusicVolume); +} + +unsigned int OpenALSoundStream::GetPosition() +{ + return 0; +} + +bool OpenALSoundStream::SetPosition(unsigned int val) +{ + return false; +} + +bool OpenALSoundStream::IsEnded() +{ + if(!Playing) + return true; + + ALint processed, state, queued; + alGetSourcei(Source, AL_SOURCE_STATE, &state); + alGetSourcei(Source, AL_BUFFERS_QUEUED, &queued); + alGetSourcei(Source, AL_BUFFERS_PROCESSED, &processed); + while(processed-- > 0) + { + ALuint buf = 0; + alSourceUnqueueBuffers(Source, 1, &buf); + queued--; + + size_t count = Sound_Decode(Sample); + if(count == 0 && Looping) + { + Sound_Seek(Sample, 0); + count = Sound_Decode(Sample); + } + if(count > 0) + { + alBufferData(buf, Format, GetData(count), count, SampleRate); + alSourceQueueBuffers(Source, 1, &buf); + queued++; + } + } + Playing = (getALError() == AL_NO_ERROR && queued > 0); + if(Playing && state != AL_PLAYING && state != AL_PAUSED) + { + alSourcePlay(Source); + Playing = (getALError() == AL_NO_ERROR); + } + + return !Playing; +} + +void *OpenALSoundStream::GetData(size_t bytes) +{ + void *data = Sample->buffer; + if(NeedSwab) + { + short *samples = reinterpret_cast(data); + size_t count = bytes >> 1; + for(size_t i = 0;i < count;i++) + { + short smp = *samples; + *(samples++) = ((smp>>8)&0x00FF) | ((smp<<8)*0xFF00); + } + } + return data; +} + +bool OpenALSoundStream::InitSample() +{ + UInt32 smpsize = 0; + SampleRate = Sample->actual.rate; + + Format = AL_NONE; + if(Sample->actual.format == AUDIO_U8) + { + if(Sample->actual.channels == 1) + Format = AL_FORMAT_MONO8; + else if(Sample->actual.channels == 2) + Format = AL_FORMAT_STEREO8; + smpsize = 1 * Sample->actual.channels; + } + else if(Sample->actual.format == AUDIO_S16LSB || Sample->actual.format == AUDIO_S16MSB) + { + NeedSwab = (Sample->actual.format != AUDIO_S16SYS); + if(Sample->actual.channels == 1) + Format = AL_FORMAT_MONO16; + else if(Sample->actual.channels == 2) + Format = AL_FORMAT_STEREO16; + smpsize = 1 * Sample->actual.channels; + } + + if(Format == AL_NONE) + { + Printf("Unsupported sound format (0x%04x, %d channels)\n", Sample->actual.format, Sample->actual.channels); + return false; + } + + Uint32 bufsize = (UInt32)(BufferTime*SampleRate) * smpsize; + if(Sound_SetBufferSize(Sample, bufsize) == 0) + { + Printf("Failed to set buffer size to %u bytes: %s\n", bufsize, Sound_GetError()); + return false; + } + + return true; +} + +bool OpenALSoundStream::Init(const char *filename, int offset, int length) +{ + if(!SetupSource()) + return false; + + if(offset == 0) + Sample = Sound_NewSampleFromFile(filename, NULL, 0); + else + { + FILE *fp = fopen(filename, "rb"); + if(!fp) + { + Printf("Failed to open %s\n", filename); + return false; + } + + const char *ext = strrchr(filename, '.'); + if(ext) ext++; + + SDL_RWops *ops = SDL_AllocRW(); + ops->seek = RWSubFile::seek; + ops->read = RWSubFile::read; + ops->write = RWSubFile::write; + ops->close = RWSubFile::close; + ops->type = 0xdeadbeef; + ops->hidden.unknown.data1 = new RWSubFile(fp, offset, length); + + Sample = Sound_NewSample(ops, ext, NULL, 0); + } + if(!Sample) + { + Printf("Could not open audio in %s (%s)\n", filename, Sound_GetError()); + return false; + } + + return InitSample(); +} + +bool OpenALSoundStream::Init(const BYTE *data, unsigned int datalen) +{ + if(!SetupSource()) + return false; + + Sample = Sound_NewSample(SDL_RWFromConstMem(data, datalen), NULL, NULL, 0); + if(!Sample) + { + Printf("Could not read audio: %s\n", Sound_GetError()); + return false; + } + + return InitSample(); +} + + +Decoder::Decoder(const void* data, unsigned int datalen) + : Sample(NULL), NeedSwab(false) +{ + Sample = Sound_NewSample(SDL_RWFromConstMem(data, datalen), NULL, NULL, 65536); +} + + +Decoder::~Decoder() +{ + Sound_FreeSample(Sample); + Sample = NULL; +} + +bool Decoder::GetFormat(ALenum *format, ALuint *rate) +{ + ALenum fmt = AL_NONE; + if(Sample->actual.format == AUDIO_U8) + { + if(Sample->actual.channels == 1) + fmt = AL_FORMAT_MONO8; + else if(Sample->actual.channels == 2) + fmt = AL_FORMAT_STEREO8; + } + else if(Sample->actual.format == AUDIO_S16LSB || Sample->actual.format == AUDIO_S16MSB) + { + NeedSwab = (Sample->actual.format != AUDIO_S16SYS); + if(Sample->actual.channels == 1) + fmt = AL_FORMAT_MONO16; + else if(Sample->actual.channels == 2) + fmt = AL_FORMAT_STEREO16; + } + + if(fmt == AL_NONE) + { + Printf("Unsupported sound format (0x%04x, %d channels)\n", Sample->actual.format, Sample->actual.channels); + return false; + } + + *format = fmt; + *rate = Sample->actual.rate; + return true; +} + +void* Decoder::GetData(ALsizei *size) +{ + UInt32 got = Sound_DecodeAll(Sample); + if(got == 0) + { + *size = 0; + return NULL; + } + + void *data = Sample->buffer; + if(NeedSwab) + { + short *samples = reinterpret_cast(data); + size_t count = got >> 1; + for(size_t i = 0;i < count;i++) + { + short smp = *samples; + *(samples++) = ((smp>>8)&0x00FF) | ((smp<<8)*0xFF00); + } + } + + *size = got; + return data; +} diff --git a/src/sound/oalsdlsound.h b/src/sound/oalsdlsound.h new file mode 100644 index 000000000..847edfb32 --- /dev/null +++ b/src/sound/oalsdlsound.h @@ -0,0 +1,66 @@ +#ifndef OALSDLSOUND_H +#define OALSDLSOUND_H + +#include "oalsound.h" +#include "tempfiles.h" + +#include "SDL_sound.h" + +class OpenALSoundStream : public SoundStream +{ + OpenALSoundRenderer *Renderer; + Sound_Sample *Sample; + ALuint Source; + + bool Playing; + bool Looping; + + static const ALfloat BufferTime = 0.2f; + std::vector Buffers; + + ALuint SampleRate; + ALenum Format; + + bool NeedSwab; + void *GetData(size_t bytes); + + // General methods + bool SetupSource(); + bool InitSample(); + + ALfloat Volume; + +public: + OpenALSoundStream(OpenALSoundRenderer *renderer); + virtual ~OpenALSoundStream(); + + virtual bool Play(bool looping, float vol); + virtual void Stop(); + virtual bool SetPaused(bool paused); + + virtual void SetVolume(float vol); + + virtual unsigned int GetPosition(); + virtual bool SetPosition(unsigned int val); + + virtual bool IsEnded(); + + bool Init(const char *filename, int offset, int length); + bool Init(const BYTE *data, unsigned int datalen); +}; + + +class Decoder +{ + Sound_Sample *Sample; + bool NeedSwab; + +public: + Decoder(const void *data, unsigned int datalen); + virtual ~Decoder(); + + bool GetFormat(ALenum *format, ALuint *rate); + void *GetData(ALsizei *size); +}; + +#endif /* OALSDLSOUND_H */ diff --git a/src/sound/oalsound.cpp b/src/sound/oalsound.cpp index 541308a64..afcfe9727 100644 --- a/src/sound/oalsound.cpp +++ b/src/sound/oalsound.cpp @@ -1,1575 +1,1787 @@ -/* -** 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 "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) -CUSTOM_CVAR (Float, snd_waterabsorption, 10.0f, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) -{ - if(*self < 0.0f) - self = 0.0f; - else if(*self > 10.0f) - self = 10.0f; -} - -void I_BuildALDeviceList(FOptionValues *opt) -{ - opt->mValues.Resize(1); - opt->mValues[0].TextValue = "Default"; - opt->mValues[0].Text = "Default"; - -#ifndef NO_OPENAL - 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 - -#include -#include -#include -#include - - -EXTERN_CVAR (Int, snd_channels) -EXTERN_CVAR (Int, snd_samplerate) -EXTERN_CVAR (Bool, snd_waterreverb) -EXTERN_CVAR (Bool, snd_pitched) - - -#define foreach(type, name, vec) \ - for(std::vector::iterator (name) = (vec).begin(), \ - (_end_##name) = (vec).end(); \ - (name) != (_end_##name);(name)++) - - -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__) - -extern ReverbContainer *ForcedEnvironment; - -#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; -} - - -static ALenum FormatFromDesc(int bits, int channels) -{ - if(bits == 8) - { - if(channels == 1) return AL_FORMAT_MONO8; - if(channels == 2) return AL_FORMAT_STEREO8; - if(channels == 4) return AL_FORMAT_QUAD8; - if(channels == 6) return AL_FORMAT_51CHN8; - if(channels == 7) return AL_FORMAT_61CHN8; - if(channels == 8) return AL_FORMAT_71CHN8; - } - if(bits == 16) - { - if(channels == 1) return AL_FORMAT_MONO16; - if(channels == 2) return AL_FORMAT_STEREO16; - if(channels == 4) return AL_FORMAT_QUAD16; - if(channels == 6) return AL_FORMAT_51CHN16; - if(channels == 7) return AL_FORMAT_61CHN16; - if(channels == 8) return AL_FORMAT_71CHN16; - } - if(bits == 32) - { - if(channels == 1) return AL_FORMAT_MONO_FLOAT32; - if(channels == 2) return AL_FORMAT_STEREO_FLOAT32; - if(channels == 4) return AL_FORMAT_QUAD32; - if(channels == 6) return AL_FORMAT_51CHN32; - if(channels == 7) return AL_FORMAT_61CHN32; - if(channels == 8) return AL_FORMAT_71CHN32; - } - return AL_NONE; -} - - -class OpenALSoundStream : public SoundStream -{ - OpenALSoundRenderer *Renderer; - -public: - FTempFileName tmpfile; - ALfloat Volume; - - OpenALSoundStream(OpenALSoundRenderer *renderer) - : Renderer(renderer), Volume(1.0f) - { Renderer->Streams.push_back(this); } - - virtual ~OpenALSoundStream() - { - Renderer->Streams.erase(std::find(Renderer->Streams.begin(), - Renderer->Streams.end(), this)); - Renderer = NULL; - } - - - virtual bool Play(bool, float) - { return false; } - - virtual void Stop() - { } - - virtual void SetVolume(float vol) - { Volume = vol; } - - virtual bool SetPaused(bool) - { return false; } - - virtual unsigned int GetPosition() - { return 0; } - - virtual bool IsEnded() - { return true; } - - bool Init(const char*) - { return false; } - - bool Init(const BYTE*, unsigned int) - { return false; } - - bool Init(SoundStreamCallback, int, int, int, void*) - { return false; } -}; - -class Decoder -{ -public: - std::vector OutData; - ALint LoopPts[2]; - ALsizei OutRate; - ALuint OutChannels; - ALuint OutBits; - - Decoder() - : OutRate(0), OutChannels(0), OutBits(0) - { LoopPts[0] = LoopPts[1] = 0; } - - bool Decode(const void*, unsigned int, int=0) - { return false; } -}; - - -template -static void LoadALFunc(const char *name, T *x) -{ *x = reinterpret_cast(alGetProcAddress(name)); } - -OpenALSoundRenderer::OpenALSoundRenderer() - : Device(NULL), Context(NULL), SrcDistanceModel(false), SFXPaused(0), - PrevEnvironment(NULL), EnvSlot(0) -{ - EnvFilters[0] = EnvFilters[1] = 0; - - Printf("I_InitSound: Initializing OpenAL\n"); - - 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"); - return; - } - } - - Printf(" Opened device "TEXTCOLOR_ORANGE"%s\n", alcGetString(Device, ALC_DEVICE_SPECIFIER)); - 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)); - - DisconnectNotify = alcIsExtensionPresent(Device, "ALC_EXT_disconnect"); - - std::vector attribs; - if(*snd_samplerate > 0) - { - attribs.push_back(ALC_FREQUENCY); - attribs.push_back(*snd_samplerate); - } - // Make sure one source is capable of stereo output with the rest doing - // mono, without running out of voices - attribs.push_back(ALC_MONO_SOURCES); - attribs.push_back((std::max)(*snd_channels, 2) - 1); - attribs.push_back(ALC_STEREO_SOURCES); - attribs.push_back(1); - // Other attribs..? - attribs.push_back(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)); - - SrcDistanceModel = alIsExtensionPresent("AL_EXT_source_distance_model"); - LoopPoints = alIsExtensionPresent("AL_SOFT_loop_points"); - - alDopplerFactor(0.5f); - alSpeedOfSound(343.3f * 96.0f); - alDistanceModel(AL_INVERSE_DISTANCE); - if(SrcDistanceModel) - alEnable(AL_SOURCE_DISTANCE_MODEL); - - 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((std::min)((std::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); - break; - } - FreeSfx.push_back(Sources[i]); - } - 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; - } - DPrintf(" Allocated "TEXTCOLOR_BLUE"%u"TEXTCOLOR_NORMAL" sources\n", Sources.size()); - - LastWaterAbsorb = 0.0f; - if(*snd_efx && alcIsExtensionPresent(Device, "ALC_EXT_EFX")) - { - // EFX function pointers -#define LOAD_FUNC(x) (LoadALFunc(#x, &x)) - 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); -#undef LOAD_FUNC - 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"); - - snd_sfxvolume.Callback(); -} - -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(); - - for(EffectMap::iterator i = EnvEffects.begin();i != EnvEffects.end();i++) - { - if(i->second) - alDeleteEffects(1, &(i->second)); - } - 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 = *((ALuint*)schan->SysChannel); - volume = SfxVolume; - - alcSuspendContext(Context); - alSourcef(source, AL_MAX_GAIN, volume); - if(schan->ManualGain) - volume *= GetRolloff(&schan->Rolloff, sqrt(schan->DistanceSqr)); - alSourcef(source, AL_GAIN, volume * ((FSoundChan*)schan)->Volume); - } - schan = schan->NextChan; - } - - getALError(); -} - -void OpenALSoundRenderer::SetMusicVolume(float volume) -{ - MusicVolume = volume; - foreach(OpenALSoundStream*, i, Streams) - (*i)->SetVolume((*i)->Volume); -} - -unsigned int OpenALSoundRenderer::GetMSLength(SoundHandle sfx) -{ - if(sfx.data) - { - ALuint buffer = *((ALuint*)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 = *((ALuint*)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) && LoopPoints) - { - if(loopstart < 0) - loopstart = 0; - if(loopend < loopstart) - loopend = length / (channels*bits/8); - - ALint loops[2] = { loopstart, loopend }; - Printf("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 = new ALuint(buffer); - return retval; -} - -SoundHandle OpenALSoundRenderer::LoadSound(BYTE *sfxdata, int length) -{ - SoundHandle retval = { NULL }; - - Decoder decoder; - if(!decoder.Decode(sfxdata, length)) - { - DPrintf("Failed to decode sound\n"); - return retval; - } - - ALenum format = FormatFromDesc(decoder.OutBits, decoder.OutChannels); - if(format == AL_NONE) - { - Printf("Unhandled format: %d bit, %d channel\n", decoder.OutBits, decoder.OutChannels); - return retval; - } - - ALenum err; - ALuint buffer = 0; - alGenBuffers(1, &buffer); - alBufferData(buffer, format, &decoder.OutData[0], - decoder.OutData.size(), decoder.OutRate); - if((err=getALError()) != AL_NO_ERROR) - { - Printf("Failed to buffer data: %s\n", alGetString(err)); - alDeleteBuffers(1, &buffer); - getALError(); - return retval; - } - - if(LoopPoints && decoder.LoopPts[1] > decoder.LoopPts[0]) - { - alBufferiv(buffer, AL_LOOP_POINTS_SOFT, decoder.LoopPts); - getALError(); - } - else if(decoder.LoopPts[1] > decoder.LoopPts[0]) - { - static bool warned = false; - if(!warned) - Printf("Loop points not supported!\n"); - warned = true; - } - - retval.data = new ALuint(buffer); - return retval; -} - -void OpenALSoundRenderer::UnloadSound(SoundHandle sfx) -{ - if(!sfx.data) - return; - - FSoundChan *schan = Channels; - while(schan) - { - if(schan->SysChannel) - { - ALint bufID = 0; - alGetSourcei(*((ALuint*)schan->SysChannel), AL_BUFFER, &bufID); - if(bufID == *((ALint*)sfx.data)) - { - FSoundChan *next = schan->NextChan; - StopChannel(schan); - schan = next; - continue; - } - } - schan = schan->NextChan; - } - - alDeleteBuffers(1, ((ALuint*)sfx.data)); - getALError(); - delete ((ALuint*)sfx.data); -} - -short *OpenALSoundRenderer::DecodeSample(int outlen, const void *coded, int sizebytes, ECodecType type) -{ - Decoder decoder; - // Force 16-bit - if(!decoder.Decode(coded, sizebytes, 16)) - { - DPrintf("Failed to decode sample\n"); - return NULL; - } - if(decoder.OutChannels != 1) - { - DPrintf("Sample is not mono\n"); - return NULL; - } - - short *samples = (short*)malloc(outlen); - if((size_t)outlen > decoder.OutData.size()) - { - memcpy(samples, &decoder.OutData[0], decoder.OutData.size()); - memset(&samples[decoder.OutData.size()/sizeof(short)], 0, outlen-decoder.OutData.size()); - } - else - memcpy(samples, &decoder.OutData[0], outlen); - - return samples; -} - -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; - stream = NULL; - } - return stream; -} - -SoundStream *OpenALSoundRenderer::OpenStream(const char *filename, int flags, int offset, int length) -{ - std::auto_ptr stream(new OpenALSoundStream(this)); - - if(offset > 0) - { - // If there's an offset to the start of the data, separate it into its - // own temp file - FILE *infile = fopen(filename, "rb"); - FILE *f = fopen(stream->tmpfile, "wb"); - if(!infile || !f || fseek(infile, offset, SEEK_SET) != 0) - { - if(infile) fclose(infile); - if(f) fclose(f); - return NULL; - } - BYTE buf[1024]; - size_t got; - do { - got = (std::min)(sizeof(buf), (size_t)length); - got = fread(buf, 1, got, infile); - if(got == 0) - break; - } while(fwrite(buf, 1, got, f) == got && (length-=got) > 0); - fclose(f); - fclose(infile); - - filename = stream->tmpfile; - } - - bool ok = ((offset == -1) ? stream->Init((const BYTE*)filename, length) : - stream->Init(filename)); - if(ok == false) - return NULL; - - return stream.release(); -} - -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 = *((ALuint*)sfx.data); - ALuint &source = *std::find(Sources.begin(), Sources.end(), FreeSfx.back()); - 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]); - alSourcef(source, AL_AIR_ABSORPTION_FACTOR, LastWaterAbsorb); - } - else - { - alSourcei(source, AL_DIRECT_FILTER, AL_FILTER_NULL); - alSource3i(source, AL_AUXILIARY_SEND_FILTER, 0, 0, AL_FILTER_NULL); - alSourcef(source, AL_AIR_ABSORPTION_FACTOR, 0.f); - } - alSourcef(source, AL_ROOM_ROLLOFF_FACTOR, 0.f); - alSourcef(source, AL_PITCH, PITCH(pitch)); - } - else if(LastWaterAbsorb > 0.f && !(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_back(source); - if(!(chanflags&SNDF_NOPAUSE)) - PausableSfx.push_back(source); - SfxGroup.push_back(source); - FreeSfx.pop_back(); - - FISoundChannel *chan = reuse_chan; - if(!chan) chan = S_GetChannel(&source); - else chan->SysChannel = &source; - - chan->Rolloff.RolloffType = ROLLOFF_Linear; - chan->Rolloff.MaxDistance = 1000.f; - chan->Rolloff.MinDistance = 1.f; - chan->DistanceScale = 1.f; - chan->DistanceSqr = 1.f; - chan->ManualGain = 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 = (pos - listener->position).LengthSquared() * - distscale*distscale; - - 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; - } - - float rolloffFactor, gain; - bool manualGain = true; - - ALuint buffer = *((ALuint*)sfx.data); - ALint channels = 1; - alGetBufferi(buffer, AL_CHANNELS, &channels); - - ALuint &source = *std::find(Sources.begin(), Sources.end(), FreeSfx.back()); - 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); - - // Multi-channel sources won't attenuate in OpenAL, and "area sounds" have - // special rolloff properties (they have a panning radius of 32 units, but - // start attenuating at MinDistance). - if(channels == 1 && !(chanflags&SNDF_AREA)) - { - if(rolloff->RolloffType == ROLLOFF_Log) - { - if(SrcDistanceModel) - 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); - rolloffFactor = rolloff->RolloffFactor; - manualGain = false; - gain = 1.f; - } - else if(rolloff->RolloffType == ROLLOFF_Linear && SrcDistanceModel) - { - alSourcei(source, AL_DISTANCE_MODEL, AL_LINEAR_DISTANCE); - alSourcef(source, AL_REFERENCE_DISTANCE, rolloff->MinDistance/distscale); - alSourcef(source, AL_MAX_DISTANCE, rolloff->MaxDistance/distscale); - rolloffFactor = 1.f; - manualGain = false; - gain = 1.f; - } - } - if(manualGain) - { - if(SrcDistanceModel) - alSourcei(source, AL_DISTANCE_MODEL, AL_NONE); - if((chanflags&SNDF_AREA) && rolloff->MinDistance < 32.f) - alSourcef(source, AL_REFERENCE_DISTANCE, 32.f/distscale); - else - alSourcef(source, AL_REFERENCE_DISTANCE, rolloff->MinDistance/distscale); - alSourcef(source, AL_MAX_DISTANCE, (1000.f+rolloff->MinDistance)/distscale); - rolloffFactor = 0.f; - gain = GetRolloff(rolloff, sqrt(dist_sqr)); - } - alSourcef(source, AL_ROLLOFF_FACTOR, rolloffFactor); - alSourcef(source, AL_MAX_GAIN, SfxVolume); - alSourcef(source, AL_GAIN, SfxVolume * gain); - - if(EnvSlot) - { - if(!(chanflags&SNDF_NOREVERB)) - { - alSourcei(source, AL_DIRECT_FILTER, EnvFilters[0]); - alSource3i(source, AL_AUXILIARY_SEND_FILTER, EnvSlot, 0, EnvFilters[1]); - alSourcef(source, AL_AIR_ABSORPTION_FACTOR, LastWaterAbsorb); - } - else - { - alSourcei(source, AL_DIRECT_FILTER, AL_FILTER_NULL); - alSource3i(source, AL_AUXILIARY_SEND_FILTER, 0, 0, AL_FILTER_NULL); - alSourcef(source, AL_AIR_ABSORPTION_FACTOR, 0.f); - } - alSourcef(source, AL_ROOM_ROLLOFF_FACTOR, rolloffFactor); - alSourcef(source, AL_PITCH, PITCH(pitch)); - } - else if(LastWaterAbsorb > 0.f && !(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_back(source); - if(!(chanflags&SNDF_NOPAUSE)) - PausableSfx.push_back(source); - SfxGroup.push_back(source); - FreeSfx.pop_back(); - - FISoundChannel *chan = reuse_chan; - if(!chan) chan = S_GetChannel(&source); - else chan->SysChannel = &source; - - chan->Rolloff = *rolloff; - chan->DistanceScale = distscale; - chan->DistanceSqr = dist_sqr; - chan->ManualGain = manualGain; - - return chan; -} - -void OpenALSoundRenderer::ChannelVolume(FISoundChannel *chan, float volume) -{ - if(chan == NULL || chan->SysChannel == NULL) - return; - - alcSuspendContext(Context); - - ALuint source = *((ALuint*)chan->SysChannel); - - if(chan->ManualGain) - volume *= GetRolloff(&chan->Rolloff, sqrt(chan->DistanceSqr)); - alSourcef(source, AL_GAIN, SfxVolume * volume); -} - -void OpenALSoundRenderer::StopChannel(FISoundChannel *chan) -{ - if(chan == NULL || chan->SysChannel == NULL) - return; - - ALuint source = *((ALuint*)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(); - - std::vector::iterator i; - - i = std::find(PausableSfx.begin(), PausableSfx.end(), source); - if(i != PausableSfx.end()) PausableSfx.erase(i); - i = std::find(ReverbSfx.begin(), ReverbSfx.end(), source); - if(i != ReverbSfx.end()) ReverbSfx.erase(i); - - SfxGroup.erase(std::find(SfxGroup.begin(), SfxGroup.end(), source)); - FreeSfx.push_back(source); -} - -unsigned int OpenALSoundRenderer::GetPosition(FISoundChannel *chan) -{ - if(chan == NULL || chan->SysChannel == NULL) - return 0; - - ALint pos; - alGetSourcei(*((ALuint*)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(EInactiveState) -{ -} - -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). - std::vector toplay = SfxGroup; - if(SFXPaused) - { - std::vector::iterator i = toplay.begin(); - while(i != toplay.end()) - { - if(std::find(PausableSfx.begin(), PausableSfx.end(), *i) != PausableSfx.end()) - i = toplay.erase(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; - - alcSuspendContext(Context); - - ALuint source = *((ALuint*)chan->SysChannel); - alSource3f(source, AL_POSITION, pos[0], pos[1], -pos[2]); - alSource3f(source, AL_VELOCITY, vel[0], vel[1], -vel[2]); - - chan->DistanceSqr = (pos - listener->position).LengthSquared() * - chan->DistanceScale*chan->DistanceScale; - // Not all sources can use the distance models provided by OpenAL. - // For the ones that can't, apply the calculated attenuation as the - // source gain. Positions still handle the panning, - if(chan->ManualGain) - { - float gain = GetRolloff(&chan->Rolloff, sqrt(chan->DistanceSqr)); - alSourcef(source, AL_GAIN, SfxVolume*gain*((FSoundChan*)chan)->Volume); - } - - getALError(); -} - -void OpenALSoundRenderer::UpdateListener(SoundListener *listener) -{ - if(!listener->valid) - return; - - alcSuspendContext(Context); - - 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 (and changing water absorption) will - // undo pitch variations on sounds if either snd_waterreverb or EFX are - // disabled. - if(listener->underwater || env->SoftwareWater) - { - if(LastWaterAbsorb != *snd_waterabsorption) - { - LastWaterAbsorb = *snd_waterabsorption; - - if(EnvSlot != 0 && *snd_waterreverb) - { - // Find the "Underwater" reverb environment - env = Environments; - while(env && env->ID != 0x1600) - env = env->Next; - LoadReverb(env ? env : DefaultEnvironments[0]); - - alFilterf(EnvFilters[0], AL_LOWPASS_GAIN, 0.25f); - alFilterf(EnvFilters[0], AL_LOWPASS_GAINHF, 0.75f); - alFilterf(EnvFilters[1], AL_LOWPASS_GAIN, 1.f); - alFilterf(EnvFilters[1], AL_LOWPASS_GAINHF, 1.f); - - // Apply the updated filters on the sources - foreach(ALuint, i, ReverbSfx) - { - alSourcef(*i, AL_AIR_ABSORPTION_FACTOR, LastWaterAbsorb); - alSourcei(*i, AL_DIRECT_FILTER, EnvFilters[0]); - alSource3i(*i, AL_AUXILIARY_SEND_FILTER, EnvSlot, 0, EnvFilters[1]); - } - } - else - { - foreach(ALuint, i, ReverbSfx) - alSourcef(*i, AL_PITCH, PITCH_MULT); - } - getALError(); - } - } - else - { - if(LastWaterAbsorb > 0.f) - { - LastWaterAbsorb = 0.f; - - 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); - foreach(ALuint, i, ReverbSfx) - { - alSourcef(*i, AL_AIR_ABSORPTION_FACTOR, 0.f); - alSourcei(*i, AL_DIRECT_FILTER, EnvFilters[0]); - alSource3i(*i, AL_AUXILIARY_SEND_FILTER, EnvSlot, 0, EnvFilters[1]); - } - } - else - { - foreach(ALuint, i, ReverbSfx) - alSourcef(*i, AL_PITCH, 1.f); - } - getALError(); - } - } -} - -void OpenALSoundRenderer::UpdateSounds() -{ - alcProcessContext(Context); - - if(DisconnectNotify) - { - 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(); -} - -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 = *((ALuint*)chan->SysChannel); - ALfloat volume = 0.f; - - if(!chan->ManualGain) - volume = SfxVolume * ((FSoundChan*)chan)->Volume * - GetRolloff(&chan->Rolloff, sqrt(chan->DistanceSqr)); - else - { - alGetSourcef(source, AL_GAIN, &volume); - getALError(); - } - 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); - - ALuint total = Sources.size(); - ALuint used = SfxGroup.size()+Streams.size(); - ALuint 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)); - const ALCchar *current = alcGetString(Device, ALC_DEVICE_SPECIFIER); - if(drivers == NULL) - { - Printf(TEXTCOLOR_YELLOW"Failed to retrieve device list: %s\n", alcGetString(NULL, alcGetError(NULL))); - 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 - foreach(ALuint, i, SfxGroup) - { - ALint state = AL_PLAYING; - alGetSourcei(*i, AL_SOURCE_STATE, &state); - if(state == AL_PLAYING || state == AL_PAUSED) - continue; - - FSoundChan *schan = Channels; - while(schan) - { - if(schan->SysChannel != NULL && *i == *((ALuint*)schan->SysChannel)) - { - StopChannel(schan); - break; - } - schan = schan->NextChan; - } - } - getALError(); -} - -void OpenALSoundRenderer::LoadReverb(const ReverbContainer *env) -{ - ALuint &envReverb = EnvEffects[env->ID]; - bool doLoad = (env->Modified || !envReverb); - - if(!envReverb) - { - bool ok = false; - 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, DENSITY, props.Density/100.f); - SETPARAM(envReverb, DIFFUSION, props.Diffusion/100.f); - 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, DENSITY, props.Density/100.f); - SETPARAM(envReverb, DIFFUSION, props.Diffusion/100.f); - 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 +/* +** 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 "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) +CUSTOM_CVAR (Float, snd_waterabsorption, 10.0f, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) +{ + if(*self < 0.0f) + self = 0.0f; + else if(*self > 10.0f) + self = 10.0f; +} + +void I_BuildALDeviceList(FOptionValues *opt) +{ + opt->mValues.Resize(1); + opt->mValues[0].TextValue = "Default"; + opt->mValues[0].Text = "Default"; + +#ifndef NO_OPENAL + 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 + +#include +#include +#include +#include + +#ifdef WITH_SDL_SOUND +#include "SDL_sound.h" +#endif + +EXTERN_CVAR (Int, snd_channels) +EXTERN_CVAR (Int, snd_samplerate) +EXTERN_CVAR (Bool, snd_waterreverb) +EXTERN_CVAR (Bool, snd_pitched) + + +#define foreach(type, name, vec) \ + for(std::vector::iterator (name) = (vec).begin(), \ + (_end_##name) = (vec).end(); \ + (name) != (_end_##name);(name)++) + + +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__) + +#ifdef WITH_SDL_SOUND +#include "oalsdlsound.h" +#else + +class OpenALSoundStream : public SoundStream +{ + OpenALSoundRenderer *Renderer; + +public: + ALfloat Volume; + + OpenALSoundStream(OpenALSoundRenderer *renderer) + : Renderer(renderer), Volume(1.0f) + { Renderer->Streams.push_back(this); } + + virtual ~OpenALSoundStream() + { + Renderer->Streams.erase(std::find(Renderer->Streams.begin(), + Renderer->Streams.end(), this)); + Renderer = NULL; + } + + + virtual bool Play(bool, float) + { return false; } + + virtual void Stop() + { } + + virtual void SetVolume(float vol) + { Volume = vol; } + + virtual bool SetPaused(bool) + { return false; } + + virtual unsigned int GetPosition() + { return 0; } + + virtual bool IsEnded() + { return true; } + + bool Init(const char*) + { return false; } + + bool Init(const BYTE*, unsigned int) + { return false; } +}; + +class Decoder +{ +public: + Decoder(const void*, unsigned int) { } + virtual ~Decoder() { } + + bool GetFormat(ALenum*, ALuint*) + { return false; } + void *GetData(ALsizei *size) + { *size = 0; return NULL; } +}; +#endif + +class OpenALCallbackStream : public SoundStream +{ + OpenALSoundRenderer *Renderer; + + SoundStreamCallback Callback; + void *UserData; + + std::vector Data; + + ALsizei SampleRate; + ALenum Format; + + static const int BufferCount = 4; + ALuint Buffers[BufferCount]; + ALuint Source; + + bool Playing; + + 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; + } + Source = Renderer->FreeSfx.back(); + Renderer->FreeSfx.pop_back(); + + /* 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: + ALfloat Volume; + + OpenALCallbackStream(OpenALSoundRenderer *renderer) + : Renderer(renderer), Source(0), Playing(false), Volume(1.0f) + { + Renderer->Streams.push_back(this); + memset(Buffers, 0, sizeof(Buffers)); + } + + virtual ~OpenALCallbackStream() + { + if(Source) + { + alSourceRewind(Source); + alSourcei(Source, AL_BUFFER, 0); + + Renderer->FreeSfx.push_back(Source); + Source = 0; + } + + if(Buffers[0]) + { + alDeleteBuffers(BufferCount, &Buffers[0]); + memset(Buffers, 0, sizeof(Buffers)); + } + getALError(); + + Renderer->Streams.erase(std::find(Renderer->Streams.begin(), + Renderer->Streams.end(), this)); + Renderer = NULL; + } + + + 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) + { + if(vol >= 0.0f) Volume = vol; + 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 unsigned int GetPosition() + { return 0; } + + 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; + } + + 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&(Bits32|Float))) + { + 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; + } + + int smpsize = 1; + if((flags&Bits8)) + smpsize *= 1; + else if((flags&(Bits32|Float))) + smpsize *= 4; + else + smpsize *= 2; + + if((flags&Mono)) + smpsize *= 1; + else + smpsize *= 2; + + buffbytes += smpsize-1; + buffbytes -= buffbytes%smpsize; + Data.resize(buffbytes); + + return true; + } +}; + + +extern ReverbContainer *ForcedEnvironment; + +#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; +} + + +static ALenum FormatFromDesc(int bits, int channels) +{ + if(bits == 8) + { + if(channels == 1) return AL_FORMAT_MONO8; + if(channels == 2) return AL_FORMAT_STEREO8; + if(channels == 4) return AL_FORMAT_QUAD8; + if(channels == 6) return AL_FORMAT_51CHN8; + if(channels == 7) return AL_FORMAT_61CHN8; + if(channels == 8) return AL_FORMAT_71CHN8; + } + if(bits == 16) + { + if(channels == 1) return AL_FORMAT_MONO16; + if(channels == 2) return AL_FORMAT_STEREO16; + if(channels == 4) return AL_FORMAT_QUAD16; + if(channels == 6) return AL_FORMAT_51CHN16; + if(channels == 7) return AL_FORMAT_61CHN16; + if(channels == 8) return AL_FORMAT_71CHN16; + } + if(bits == 32) + { + if(channels == 1) return AL_FORMAT_MONO_FLOAT32; + if(channels == 2) return AL_FORMAT_STEREO_FLOAT32; + if(channels == 4) return AL_FORMAT_QUAD32; + if(channels == 6) return AL_FORMAT_51CHN32; + if(channels == 7) return AL_FORMAT_61CHN32; + if(channels == 8) return AL_FORMAT_71CHN32; + } + return AL_NONE; +} + + +template +static void LoadALFunc(const char *name, T *x) +{ *x = reinterpret_cast(alGetProcAddress(name)); } + +OpenALSoundRenderer::OpenALSoundRenderer() + : Device(NULL), Context(NULL), SrcDistanceModel(false), SFXPaused(0), + PrevEnvironment(NULL), EnvSlot(0) +{ + EnvFilters[0] = EnvFilters[1] = 0; + + Printf("I_InitSound: Initializing OpenAL\n"); + +#ifdef WITH_SDL_SOUND + static bool sdl_sound_inited = false; + if(!sdl_sound_inited) + { + if(Sound_Init() == 0) + { + Printf(TEXTCOLOR_RED" Failed to init SDL_sound: %s\n", Sound_GetError()); + return; + } + sdl_sound_inited = true; + } +#endif + + 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"); + return; + } + } + + Printf(" Opened device "TEXTCOLOR_ORANGE"%s\n", alcGetString(Device, ALC_DEVICE_SPECIFIER)); + 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)); + + DisconnectNotify = alcIsExtensionPresent(Device, "ALC_EXT_disconnect"); + + std::vector attribs; + if(*snd_samplerate > 0) + { + attribs.push_back(ALC_FREQUENCY); + attribs.push_back(*snd_samplerate); + } + // Make sure one source is capable of stereo output with the rest doing + // mono, without running out of voices + attribs.push_back(ALC_MONO_SOURCES); + attribs.push_back((std::max)(*snd_channels, 2) - 1); + attribs.push_back(ALC_STEREO_SOURCES); + attribs.push_back(1); + // Other attribs..? + attribs.push_back(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)); + + SrcDistanceModel = alIsExtensionPresent("AL_EXT_source_distance_model"); + LoopPoints = alIsExtensionPresent("AL_SOFT_loop_points"); + + alDopplerFactor(0.5f); + alSpeedOfSound(343.3f * 96.0f); + alDistanceModel(AL_INVERSE_DISTANCE); + if(SrcDistanceModel) + alEnable(AL_SOURCE_DISTANCE_MODEL); + + 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((std::min)((std::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); + break; + } + FreeSfx.push_back(Sources[i]); + } + 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; + } + DPrintf(" Allocated "TEXTCOLOR_BLUE"%u"TEXTCOLOR_NORMAL" sources\n", Sources.size()); + + LastWaterAbsorb = 0.0f; + if(*snd_efx && alcIsExtensionPresent(Device, "ALC_EXT_EFX")) + { + // EFX function pointers +#define LOAD_FUNC(x) (LoadALFunc(#x, &x)) + 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); +#undef LOAD_FUNC + 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"); + + snd_sfxvolume.Callback(); +} + +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(); + + for(EffectMap::iterator i = EnvEffects.begin();i != EnvEffects.end();i++) + { + if(i->second) + alDeleteEffects(1, &(i->second)); + } + 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 = *((ALuint*)schan->SysChannel); + volume = SfxVolume; + + alcSuspendContext(Context); + alSourcef(source, AL_MAX_GAIN, volume); + if(schan->ManualGain) + volume *= GetRolloff(&schan->Rolloff, sqrt(schan->DistanceSqr)); + alSourcef(source, AL_GAIN, volume * ((FSoundChan*)schan)->Volume); + } + schan = schan->NextChan; + } + + getALError(); +} + +void OpenALSoundRenderer::SetMusicVolume(float volume) +{ + MusicVolume = volume; + foreach(SoundStream*, i, Streams) + (*i)->SetVolume(-1.f); +} + +unsigned int OpenALSoundRenderer::GetMSLength(SoundHandle sfx) +{ + if(sfx.data) + { + ALuint buffer = *((ALuint*)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 = *((ALuint*)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) && LoopPoints) + { + if(loopstart < 0) + loopstart = 0; + if(loopend < loopstart) + loopend = length / (channels*bits/8); + + ALint loops[2] = { loopstart, loopend }; + Printf("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 = new ALuint(buffer); + return retval; +} + +SoundHandle OpenALSoundRenderer::LoadSound(BYTE *sfxdata, int length) +{ + SoundHandle retval = { NULL }; + ALenum format; + ALuint srate; + + Decoder decoder(sfxdata, length); + if(!decoder.GetFormat(&format, &srate)) + return retval; + + ALsizei size; + void *data = decoder.GetData(&size); + if(data == NULL) + return retval; + + ALuint buffer = 0; + alGenBuffers(1, &buffer); + alBufferData(buffer, format, data, size, srate); + + ALenum err; + if((err=getALError()) != AL_NO_ERROR) + { + Printf("Failed to buffer data: %s\n", alGetString(err)); + alDeleteBuffers(1, &buffer); + getALError(); + return retval; + } + + retval.data = new ALuint(buffer); + return retval; +} + +void OpenALSoundRenderer::UnloadSound(SoundHandle sfx) +{ + if(!sfx.data) + return; + + FSoundChan *schan = Channels; + while(schan) + { + if(schan->SysChannel) + { + ALint bufID = 0; + alGetSourcei(*((ALuint*)schan->SysChannel), AL_BUFFER, &bufID); + if(bufID == *((ALint*)sfx.data)) + { + FSoundChan *next = schan->NextChan; + StopChannel(schan); + schan = next; + continue; + } + } + schan = schan->NextChan; + } + + alDeleteBuffers(1, ((ALuint*)sfx.data)); + getALError(); + delete ((ALuint*)sfx.data); +} + +short *OpenALSoundRenderer::DecodeSample(int outlen, const void *coded, int sizebytes, ECodecType type) +{ + short *samples = (short*)malloc(outlen); + memset(samples, 0, outlen); + + Decoder decoder(coded, sizebytes); + ALenum format; + ALuint srate; + + if(!decoder.GetFormat(&format, &srate)) + return samples; + if(format != AL_FORMAT_MONO16) + { + DPrintf("Sample is not 16-bit mono\n"); + return samples; + } + + ALsizei size; + void *data = decoder.GetData(&size); + if(data != NULL) + memcpy(samples, data, std::min(size, outlen)); + + return samples; +} + +SoundStream *OpenALSoundRenderer::CreateStream(SoundStreamCallback callback, int buffbytes, int flags, int samplerate, void *userdata) +{ + std::auto_ptr stream(new OpenALCallbackStream(this)); + if(!stream->Init(callback, buffbytes, flags, samplerate, userdata)) + return NULL; + return stream.release(); +} + +SoundStream *OpenALSoundRenderer::OpenStream(const char *filename, int flags, int offset, int length) +{ + std::auto_ptr stream(new OpenALSoundStream(this)); + + bool ok = ((offset == -1) ? stream->Init((const BYTE*)filename, length) : + stream->Init(filename, offset, length)); + if(ok == false) + return NULL; + + return stream.release(); +} + +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 = *((ALuint*)sfx.data); + ALuint &source = *std::find(Sources.begin(), Sources.end(), FreeSfx.back()); + 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]); + alSourcef(source, AL_AIR_ABSORPTION_FACTOR, LastWaterAbsorb); + } + else + { + alSourcei(source, AL_DIRECT_FILTER, AL_FILTER_NULL); + alSource3i(source, AL_AUXILIARY_SEND_FILTER, 0, 0, AL_FILTER_NULL); + alSourcef(source, AL_AIR_ABSORPTION_FACTOR, 0.f); + } + alSourcef(source, AL_ROOM_ROLLOFF_FACTOR, 0.f); + alSourcef(source, AL_PITCH, PITCH(pitch)); + } + else if(LastWaterAbsorb > 0.f && !(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_back(source); + if(!(chanflags&SNDF_NOPAUSE)) + PausableSfx.push_back(source); + SfxGroup.push_back(source); + FreeSfx.pop_back(); + + FISoundChannel *chan = reuse_chan; + if(!chan) chan = S_GetChannel(&source); + else chan->SysChannel = &source; + + chan->Rolloff.RolloffType = ROLLOFF_Linear; + chan->Rolloff.MaxDistance = 1000.f; + chan->Rolloff.MinDistance = 1.f; + chan->DistanceScale = 1.f; + chan->DistanceSqr = 1.f; + chan->ManualGain = 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 = (pos - listener->position).LengthSquared() * + distscale*distscale; + + 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; + } + + float rolloffFactor, gain; + bool manualGain = true; + + ALuint buffer = *((ALuint*)sfx.data); + ALint channels = 1; + alGetBufferi(buffer, AL_CHANNELS, &channels); + + ALuint &source = *std::find(Sources.begin(), Sources.end(), FreeSfx.back()); + 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); + + // Multi-channel sources won't attenuate in OpenAL, and "area sounds" have + // special rolloff properties (they have a panning radius of 32 units, but + // start attenuating at MinDistance). + if(channels == 1 && !(chanflags&SNDF_AREA)) + { + if(rolloff->RolloffType == ROLLOFF_Log) + { + if(SrcDistanceModel) + 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); + rolloffFactor = rolloff->RolloffFactor; + manualGain = false; + gain = 1.f; + } + else if(rolloff->RolloffType == ROLLOFF_Linear && SrcDistanceModel) + { + alSourcei(source, AL_DISTANCE_MODEL, AL_LINEAR_DISTANCE); + alSourcef(source, AL_REFERENCE_DISTANCE, rolloff->MinDistance/distscale); + alSourcef(source, AL_MAX_DISTANCE, rolloff->MaxDistance/distscale); + rolloffFactor = 1.f; + manualGain = false; + gain = 1.f; + } + } + if(manualGain) + { + if(SrcDistanceModel) + alSourcei(source, AL_DISTANCE_MODEL, AL_NONE); + if((chanflags&SNDF_AREA) && rolloff->MinDistance < 32.f) + alSourcef(source, AL_REFERENCE_DISTANCE, 32.f/distscale); + else + alSourcef(source, AL_REFERENCE_DISTANCE, rolloff->MinDistance/distscale); + alSourcef(source, AL_MAX_DISTANCE, (1000.f+rolloff->MinDistance)/distscale); + rolloffFactor = 0.f; + gain = GetRolloff(rolloff, sqrt(dist_sqr)); + } + alSourcef(source, AL_ROLLOFF_FACTOR, rolloffFactor); + alSourcef(source, AL_MAX_GAIN, SfxVolume); + alSourcef(source, AL_GAIN, SfxVolume * gain); + + if(EnvSlot) + { + if(!(chanflags&SNDF_NOREVERB)) + { + alSourcei(source, AL_DIRECT_FILTER, EnvFilters[0]); + alSource3i(source, AL_AUXILIARY_SEND_FILTER, EnvSlot, 0, EnvFilters[1]); + alSourcef(source, AL_AIR_ABSORPTION_FACTOR, LastWaterAbsorb); + } + else + { + alSourcei(source, AL_DIRECT_FILTER, AL_FILTER_NULL); + alSource3i(source, AL_AUXILIARY_SEND_FILTER, 0, 0, AL_FILTER_NULL); + alSourcef(source, AL_AIR_ABSORPTION_FACTOR, 0.f); + } + alSourcef(source, AL_ROOM_ROLLOFF_FACTOR, rolloffFactor); + alSourcef(source, AL_PITCH, PITCH(pitch)); + } + else if(LastWaterAbsorb > 0.f && !(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_back(source); + if(!(chanflags&SNDF_NOPAUSE)) + PausableSfx.push_back(source); + SfxGroup.push_back(source); + FreeSfx.pop_back(); + + FISoundChannel *chan = reuse_chan; + if(!chan) chan = S_GetChannel(&source); + else chan->SysChannel = &source; + + chan->Rolloff = *rolloff; + chan->DistanceScale = distscale; + chan->DistanceSqr = dist_sqr; + chan->ManualGain = manualGain; + + return chan; +} + +void OpenALSoundRenderer::ChannelVolume(FISoundChannel *chan, float volume) +{ + if(chan == NULL || chan->SysChannel == NULL) + return; + + alcSuspendContext(Context); + + ALuint source = *((ALuint*)chan->SysChannel); + + if(chan->ManualGain) + volume *= GetRolloff(&chan->Rolloff, sqrt(chan->DistanceSqr)); + alSourcef(source, AL_GAIN, SfxVolume * volume); +} + +void OpenALSoundRenderer::StopChannel(FISoundChannel *chan) +{ + if(chan == NULL || chan->SysChannel == NULL) + return; + + ALuint source = *((ALuint*)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(); + + std::vector::iterator i; + + i = std::find(PausableSfx.begin(), PausableSfx.end(), source); + if(i != PausableSfx.end()) PausableSfx.erase(i); + i = std::find(ReverbSfx.begin(), ReverbSfx.end(), source); + if(i != ReverbSfx.end()) ReverbSfx.erase(i); + + SfxGroup.erase(std::find(SfxGroup.begin(), SfxGroup.end(), source)); + FreeSfx.push_back(source); +} + +unsigned int OpenALSoundRenderer::GetPosition(FISoundChannel *chan) +{ + if(chan == NULL || chan->SysChannel == NULL) + return 0; + + ALint pos; + alGetSourcei(*((ALuint*)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(EInactiveState) +{ +} + +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). + std::vector toplay = SfxGroup; + if(SFXPaused) + { + std::vector::iterator i = toplay.begin(); + while(i != toplay.end()) + { + if(std::find(PausableSfx.begin(), PausableSfx.end(), *i) != PausableSfx.end()) + i = toplay.erase(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; + + alcSuspendContext(Context); + + ALuint source = *((ALuint*)chan->SysChannel); + alSource3f(source, AL_POSITION, pos[0], pos[1], -pos[2]); + alSource3f(source, AL_VELOCITY, vel[0], vel[1], -vel[2]); + + chan->DistanceSqr = (pos - listener->position).LengthSquared() * + chan->DistanceScale*chan->DistanceScale; + // Not all sources can use the distance models provided by OpenAL. + // For the ones that can't, apply the calculated attenuation as the + // source gain. Positions still handle the panning, + if(chan->ManualGain) + { + float gain = GetRolloff(&chan->Rolloff, sqrt(chan->DistanceSqr)); + alSourcef(source, AL_GAIN, SfxVolume*gain*((FSoundChan*)chan)->Volume); + } + + getALError(); +} + +void OpenALSoundRenderer::UpdateListener(SoundListener *listener) +{ + if(!listener->valid) + return; + + alcSuspendContext(Context); + + 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 (and changing water absorption) will + // undo pitch variations on sounds if either snd_waterreverb or EFX are + // disabled. + if(listener->underwater || env->SoftwareWater) + { + if(LastWaterAbsorb != *snd_waterabsorption) + { + LastWaterAbsorb = *snd_waterabsorption; + + if(EnvSlot != 0 && *snd_waterreverb) + { + // Find the "Underwater" reverb environment + env = Environments; + while(env && env->ID != 0x1600) + env = env->Next; + LoadReverb(env ? env : DefaultEnvironments[0]); + + alFilterf(EnvFilters[0], AL_LOWPASS_GAIN, 0.25f); + alFilterf(EnvFilters[0], AL_LOWPASS_GAINHF, 0.75f); + alFilterf(EnvFilters[1], AL_LOWPASS_GAIN, 1.f); + alFilterf(EnvFilters[1], AL_LOWPASS_GAINHF, 1.f); + + // Apply the updated filters on the sources + foreach(ALuint, i, ReverbSfx) + { + alSourcef(*i, AL_AIR_ABSORPTION_FACTOR, LastWaterAbsorb); + alSourcei(*i, AL_DIRECT_FILTER, EnvFilters[0]); + alSource3i(*i, AL_AUXILIARY_SEND_FILTER, EnvSlot, 0, EnvFilters[1]); + } + } + else + { + foreach(ALuint, i, ReverbSfx) + alSourcef(*i, AL_PITCH, PITCH_MULT); + } + getALError(); + } + } + else + { + if(LastWaterAbsorb > 0.f) + { + LastWaterAbsorb = 0.f; + + 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); + foreach(ALuint, i, ReverbSfx) + { + alSourcef(*i, AL_AIR_ABSORPTION_FACTOR, 0.f); + alSourcei(*i, AL_DIRECT_FILTER, EnvFilters[0]); + alSource3i(*i, AL_AUXILIARY_SEND_FILTER, EnvSlot, 0, EnvFilters[1]); + } + } + else + { + foreach(ALuint, i, ReverbSfx) + alSourcef(*i, AL_PITCH, 1.f); + } + getALError(); + } + } +} + +void OpenALSoundRenderer::UpdateSounds() +{ + alcProcessContext(Context); + + if(DisconnectNotify) + { + 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(); +} + +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 = *((ALuint*)chan->SysChannel); + ALfloat volume = 0.f; + + if(!chan->ManualGain) + volume = SfxVolume * ((FSoundChan*)chan)->Volume * + GetRolloff(&chan->Rolloff, sqrt(chan->DistanceSqr)); + else + { + alGetSourcef(source, AL_GAIN, &volume); + getALError(); + } + 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); + + ALuint total = Sources.size(); + ALuint used = SfxGroup.size()+Streams.size(); + ALuint 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)); + const ALCchar *current = alcGetString(Device, ALC_DEVICE_SPECIFIER); + if(drivers == NULL) + { + Printf(TEXTCOLOR_YELLOW"Failed to retrieve device list: %s\n", alcGetString(NULL, alcGetError(NULL))); + 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 + foreach(ALuint, i, SfxGroup) + { + ALint state = AL_PLAYING; + alGetSourcei(*i, AL_SOURCE_STATE, &state); + if(state == AL_PLAYING || state == AL_PAUSED) + continue; + + FSoundChan *schan = Channels; + while(schan) + { + if(schan->SysChannel != NULL && *i == *((ALuint*)schan->SysChannel)) + { + StopChannel(schan); + break; + } + schan = schan->NextChan; + } + } + getALError(); +} + +void OpenALSoundRenderer::LoadReverb(const ReverbContainer *env) +{ + ALuint &envReverb = EnvEffects[env->ID]; + bool doLoad = (env->Modified || !envReverb); + + if(!envReverb) + { + bool ok = false; + 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, DENSITY, props.Density/100.f); + SETPARAM(envReverb, DIFFUSION, props.Diffusion/100.f); + 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, DENSITY, props.Density/100.f); + SETPARAM(envReverb, DIFFUSION, props.Diffusion/100.f); + 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 index 24450c5b1..9842e6312 100644 --- a/src/sound/oalsound.h +++ b/src/sound/oalsound.h @@ -196,8 +196,9 @@ private: ALuint EnvFilters[2]; float LastWaterAbsorb; - std::vector Streams; - friend class OpenALSoundStream; + std::vector Streams; + friend class OpenALSoundStream; + friend class OpenALCallbackStream; }; #endif // NO_OPENAL