diff options
Diffstat (limited to 'Source/Sound/sound.cpp')
-rw-r--r-- | Source/Sound/sound.cpp | 436 |
1 files changed, 436 insertions, 0 deletions
diff --git a/Source/Sound/sound.cpp b/Source/Sound/sound.cpp new file mode 100644 index 00000000..45114fb3 --- /dev/null +++ b/Source/Sound/sound.cpp @@ -0,0 +1,436 @@ +//----------------------------------------------------------------------------- +// Name: sound.cpp +// Developer: Wolfire Games LLC +// Author: David Rosen +// Description: The sound class loads, manages, and plays sound effects +// Not that this class might be run in a separate thread from the main +// application, meaning thay any modifications have to be made with case, ensuring +// that all calls to the outside application or third part libraries are thread-safe. +// +// Additionally, this class, and system takes ownership of OpenAL. Meaning that no other +// Subsystem is allowed to directly communicate with the OpenAL API as it's not thread-safe. +// License: Read below +//----------------------------------------------------------------------------- +// +// Copyright 2022 Wolfire Games LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//----------------------------------------------------------------------------- + +#include <Math/enginemath.h> +#include <Math/vec3math.h> + +#include <Internal/datemodified.h> +#include <Internal/timer.h> +#include <Internal/error.h> +#include <Internal/filesystem.h> +#include <Internal/common.h> + +#include <Sound/sound.h> +#include <Asset/Asset/soundgroup.h> +#include <Physics/bulletworld.h> +#include <GUI/gui.h> +#include <Logging/logdata.h> +#include <Threading/sdl_wrapper.h> + +#ifdef _WIN32 +#define NOMINMAX +#include <windows.h> +#endif + +#include <sstream> + +const bool kSoundOcclusionEnabled = false; // Disabled for now because of a problem with ambient triangles getting occluded (e.g. crowd on stucco arena) + +bool g_sound_enable_layered_soundtrack_limiter = false; + +//----------------------------------------------------------------------------- +//Functions +//----------------------------------------------------------------------------- + +#define REF_DISTANCE 1.0f +//#define REF_DISTANCE 8.0f + +Sound::Sound(const char* preferred_device) : + m_last_game_time_scale(1.0f), + m_last_game_timestep(0.0f), + m_accum_game_timestep(0.0f), + m_audio(preferred_device, 1.0f, REF_DISTANCE), + m_soundtrack(0.5f), + whoosh_active_countdown(0) +{ + m_audio.set_distance_model(AL_INVERSE_DISTANCE_CLAMPED); + m_audio.subscribe(m_soundtrack); + pcg32_srandom_r(&play_group_rand_state, 0xDEADBEEF, 0xFACEFEED); +} + +void Sound::Dispose() { + m_audio.Dispose(); + m_soundtrack.Dispose(); +} + +void Sound::Update() { + for(unsigned i=0; i<ambient_triangles.size(); ++i){ + AmbientTriangle& a_t = ambient_triangles[i]; + for(unsigned j=0; j<3; ++j){ + a_t.vol[j] = min(1.0f, a_t.vol[j] + m_accum_game_timestep); + SetVolume(a_t.handles[j], a_t.vol[j]); + } + } + + if(m_last_game_time_scale == 1.0f){ + m_audio.set_pitch(1.0f); + } else { + m_audio.set_pitch(m_last_game_time_scale); + } + + m_audio.update(m_accum_game_timestep); + + m_accum_game_timestep = 0.0f; + + if( whoosh_active_countdown > 0 ) { + whoosh_active_countdown--; + if( whoosh_active_countdown == 0 ) { + setAirWhoosh(0); + } + } +} + +void Sound::UpdateGameTimestep( float timestep ) +{ + m_last_game_timestep = timestep; + m_accum_game_timestep += timestep; +} + +void Sound::UpdateGameTimescale( float time_scale ) +{ + m_last_game_time_scale = time_scale; +} + +void Sound::updateListener(vec3 pos, vec3 vel, vec3 facing, vec3 up) { + m_audio.set_listener_position(pos); + m_audio.set_listener_velocity(vel); + m_audio.set_listener_orientation(facing, up); + + //std::ostringstream oss; + //oss << "Ambient triangles: " << ambient_triangles.size(); + //GUI::Instance()->AddDebugText("ambient_triangles", oss.str(), 0.5f); + + for(unsigned i=0; i<ambient_triangles.size(); ++i){ + AmbientTriangle& a_t = ambient_triangles[i]; + for(unsigned j=0; j<3; ++j){ + //vec3 new_dir(dot(a_t.rel_dir[j], cross(facing, up)), + // dot(a_t.rel_dir[j], up), + // dot(a_t.rel_dir[j], facing)); + //SetPosition(a_t.handles[j], + // new_dir); + //printf("%f %f %f\n", new_dir[0], new_dir[1], new_dir[2]); + SetPosition(a_t.handles[j], + pos+a_t.rel_dir[j]); + SetVelocity(a_t.handles[j], + vel); + //DebugDraw::Instance()->AddWireSphere(pos+a_t.rel_dir[j], 0.3f, vec3(1.0f), _delete_on_update); + } + } +} + +unsigned long Sound::CreateHandle() +{ + return m_audio.get_next_handle(); +} + +void Sound::Play(const unsigned long& handle, SoundPlayInfo spi) { + char abs_path[kPathSize]; + if(FindFilePath(spi.path.c_str(), abs_path, kPathSize, kDataPaths | kModPaths)==-1) { + std::string error_text = "Could not find sound file: "+std::string(spi.path); + DisplayError("Error",error_text.c_str()); + } + + /* + * Disabled until we're done threading and can move this out of this class. + if(kSoundOcclusionEnabled){ + if(!(spi.flags & SoundFlags::kRelative)){ + const vec3 &listener_pos = m_audio.listener_pos; + bool occluded = false; + if(bullet_world){ + occluded = bullet_world->CheckRayCollision(listener_pos, spi.occlusion_position); + } + if(occluded){ + spi.volume_mult *= _occluded_sound_mult; + } + } + } + */ + + if(spi.volume >= 0.0){ + m_audio.play(handle,spi); + } else { + const int kBufSize = 256; + char buf[kBufSize]; + FormatString(buf, kBufSize, "Playing sound with negative volume: %s", spi.path.c_str()); + DisplayError("Error",buf); + } +} + +void Sound::PlayGroup(const unsigned long& handle, SoundGroupPlayInfo& sgpi) { + //DebugDraw::Instance()->AddWireSphere(pos, 0.1f, vec4(1.0f), _persistent); + //SoundGroupRef sgr = SoundGroups::Instance()->ReturnRef(sgpi.path); + SoundPlayInfo sound_info; + if(sgpi.specific_path.empty()){ + sound_info.path = sgpi.GetARandomOrderedSoundPath(&play_group_rand_state); + } else { + sound_info.path = sgpi.specific_path; + } + sound_info.position = sgpi.position; + sound_info.occlusion_position = sgpi.occlusion_position; + sound_info.volume = sgpi.volume*sgpi.gain; + sound_info.pitch = 1.0f*sgpi.pitch_shift; + sound_info.created_by_id = sgpi.created_by_id; + sound_info.filter_info = sgpi.filter_info; + sound_info.max_gain = sgpi.max_gain; + sound_info.priority = sgpi.priority; + sound_info.flags = sgpi.flags; + sound_info.max_distance = sgpi.max_distance; + sound_info.play_past_tick = sgpi.play_past_tick; + return Play(handle, sound_info); +} + +void Sound::setAirWhoosh(float amount, float pitch) { + whoosh_active_countdown = 60; + amount = max(0.0f,min(1.0f,amount)); + + if (m_air_whoosh_sound.get() == NULL) { + m_air_whoosh_sound = ambient_ptr(new ambientSound(amount)); + SoundPlayInfo spi; + spi.path = "Data/Sounds/fall_whoosh.wav"; + spi.looping = true; + unsigned long handle = m_audio.get_next_handle(); + m_audio.play(handle, spi, m_air_whoosh_sound.get()); + } else { + m_air_whoosh_sound->SetVolume(amount); + m_air_whoosh_sound->SetPitch(pitch); + } +} + +std::vector<AudioEmitter*> Sound::GetActiveSounds() +{ + return m_audio.GetActiveSounds(); +} + +/* +std::string Sound::GetGroupPath(const std::string &path){ + SoundGroupRef sgr = SoundGroups::Instance()->ReturnRef(path); + return sgr->GetSoundPath(); +} +*/ + + +bool Sound::IsHandleValid( const unsigned long &handle ) { + return m_audio.IsHandleValid(handle); +} + +void Sound::SetPosition( const unsigned long &handle, const vec3 &new_pos ) { + m_audio.SetPosition(handle, new_pos); +} + +void Sound::TranslatePosition( const unsigned long &handle, const vec3 &trans ) { + m_audio.TranslatePosition(handle, trans); +} + +void Sound::SetOcclusionPosition( const unsigned long &handle, const vec3 &new_pos ) { + m_audio.SetOcclusionPosition(handle, new_pos); +} + +void Sound::SetVelocity( const unsigned long &handle, const vec3 &new_vel ) { + m_audio.SetVelocity(handle, new_vel); +} + +const vec3 Sound::GetPosition( const unsigned long &handle ) { + return m_audio.GetPosition(handle); +} + +void Sound::SetPitch( const unsigned long &handle, float pitch ) { + m_audio.SetPitch(handle, pitch); +} + +void Sound::SetVolume( const unsigned long &handle, float volume ) { + m_audio.SetVolume(handle, volume); +} + +void Sound::Stop( const unsigned long &handle ) { + m_audio.Stop(handle); +} + +void Sound::AddMusic( const Path &path ) { + m_soundtrack.AddMusic( path ); +} + +void Sound::RemoveMusic( const Path &path ) { + m_soundtrack.RemoveMusic( path ); +} + +void Sound::QueueSegment( const std::string& string ) { + m_soundtrack.QueueSegment( string ); +} + +void Sound::TransitionToSegment( const std::string& string ) { + m_soundtrack.TransitionIntoSegment( string ); +} + +void Sound::SetSegment( const std::string& string ) { + m_soundtrack.SetSegment( string ); +} + +void Sound::TransitionToSong( const std::string& string ) { + m_soundtrack.TransitionToSong( string ); +} + +void Sound::SetSong( const std::string& string ) { + m_soundtrack.SetSong( string ); +} + +void Sound::SetLayerGain(const std::string& layer, float v) { + m_soundtrack.SetLayerGain(layer,v); +} + +const std::string Sound::GetSegment() const +{ + return m_soundtrack.GetSegment(); +} + +const std::map<std::string,float> Sound::GetLayerGains() { + return m_soundtrack.GetLayerGains(); +} + +const std::vector<std::string> Sound::GetLayerNames() const { + return m_soundtrack.GetLayerNames(); +} + +const std::string Sound::GetSongName() const +{ + return m_soundtrack.GetSongName(); +} + +const std::string Sound::GetSongType() const +{ + return m_soundtrack.GetSongType(); +} + +const std::string Sound::GetNextSongName() const +{ + return m_soundtrack.GetNextSongName(); +} + +const std::string Sound::GetNextSongType() const +{ + return m_soundtrack.GetNextSongType(); +} + +void Sound::SetMusicVolume( float val ) { + m_soundtrack.SetVolume(val); +} + +void Sound::SetMasterVolume( float val ) { + m_audio.set_master_volume(val); +} + +void Sound::AddAmbientTriangle( const std::string &path ) { + char paths[3][kPathSize]; + for(unsigned i=0; i<3; ++i){ + char abs_path[kPathSize]; + FormatString(paths[i], kPathSize, "%s_%d.wav", path.c_str(), i+1); + if(FindFilePath(paths[i], abs_path, kPathSize, kDataPaths | kModPaths) == -1){ + FatalError("Error","Could not find sound: %s", paths[i]); + } + } + ambient_triangles.resize(ambient_triangles.size()+1); + AmbientTriangle &ambient_triangle = ambient_triangles.back(); + ambient_triangle.path = path; + ambient_triangle.rel_dir[0] = vec3(1.0f,0.0f,0.0f); + ambient_triangle.rel_dir[1] = vec3(-0.5f,0.0f,0.866f); + ambient_triangle.rel_dir[2] = vec3(-0.5f,0.0f,-0.866f); + SoundPlayInfo spi; + spi.looping = true; + spi.volume = 0.0f; + spi.flags = spi.flags | SoundFlags::kNoOcclusion; + for(unsigned i=0; i<3; ++i){ + spi.path = paths[i]; + spi.position = ambient_triangle.rel_dir[i]; + spi.occlusion_position = spi.position; + unsigned long handle = CreateHandle(); + Play(handle,spi); + ambient_triangle.handles[i] = handle; + ambient_triangle.vol[i] = 0.0f; + } +} + +const std::vector<AmbientTriangle> & Sound::GetAmbientTriangles() { + return ambient_triangles; +} + +void Sound::ClearTransient() { + m_audio.StopAllTransient(); + ambient_triangles.clear(); +} + +void Sound::Clear() { + m_audio.StopAll(); + ambient_triangles.clear(); +} + +/* + * For this function to work correctly in the future it'll have to use the bullet_world reference indirectly + * (as the sound system is run in a separate thread). + * +void Sound::UpdateSoundOcclusion(void (OcclusionCheck*) (int sound_uid, vec3 from, vec3 to)) { + if(kSoundOcclusionEnabled){ + const vec3 &listener_pos = m_audio.listener_pos; + + std::vector<AudioEmitter*> sounds = m_audio.GetActiveSounds(); + + for(unsigned i=0; i<sounds.size(); ++i){ + if(!(sounds[i]->flags() & SoundFlags::kNoOcclusion)){ + const vec3& sound_pos = sounds[i]->GetOcclusionPosition(); + bool occluded = bullet_world->CheckRayCollision(listener_pos, sound_pos); + if(occluded){ + sounds[i]->SetVolumeMult(_occluded_sound_mult); + } else { + sounds[i]->SetVolumeMult(1.0f); + } + } + } + } +} +*/ + +void Sound::LoadSoundFile( const std::string & path ) { + FilterInfo filter_info; + m_audio.load(path, filter_info); +} + +std::string Sound::GetPreferredDevice() { + return m_audio.GetPreferredDevice(); +} + +std::string Sound::GetUsedDevice() { + return m_audio.GetUsedDevice(); +} + +std::vector<std::string> Sound::GetAvailableDevices() { + return m_audio.GetAvailableDevices(); +} + +void Sound::EnableLayeredSoundtrackLimiter(bool val) { + g_sound_enable_layered_soundtrack_limiter = val; +} |