/*************************************************************************/ /* spatial_sound_2d_server_sw.cpp */ /*************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ /* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ /* "Software"), to deal in the Software without restriction, including */ /* without limitation the rights to use, copy, modify, merge, publish, */ /* distribute, sublicense, and/or sell copies of the Software, and to */ /* permit persons to whom the Software is furnished to do so, subject to */ /* the following conditions: */ /* */ /* The above copyright notice and this permission notice shall be */ /* included in all copies or substantial portions of the Software. */ /* */ /* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ /* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ /* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ /* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ /* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ #include "spatial_sound_2d_server_sw.h" #include "os/os.h" #include "servers/audio/audio_filter_sw.h" int SpatialSound2DServerSW::InternalAudioStream::get_channel_count() const { return AudioServer::get_singleton()->get_default_channel_count(); } void SpatialSound2DServerSW::InternalAudioStream::set_mix_rate(int p_rate) { } void SpatialSound2DServerSW::InternalAudioStream::update() { owner->_update_sources(); } bool SpatialSound2DServerSW::InternalAudioStream::mix(int32_t *p_buffer,int p_frames) { return owner->internal_buffer_mix(p_buffer,p_frames); } void SpatialSound2DServerSW::_update_sources() { _THREAD_SAFE_METHOD_ for (Set::Element *E=streaming_sources.front();E;E=E->next()) { Source *s=E->get(); ERR_CONTINUE(!s->stream); s->stream->update(); } } SpatialSound2DServerSW::Room::Room() { // params[ROOM_PARAM_SPEED_OF_SOUND]=343.0; params[ROOM_PARAM_PITCH_SCALE]=1.0; params[ROOM_PARAM_VOLUME_SCALE_DB]=0; params[ROOM_PARAM_REVERB_SEND]=0; params[ROOM_PARAM_CHORUS_SEND]=0; params[ROOM_PARAM_ATTENUATION_SCALE]=1.0; params[ROOM_PARAM_ATTENUATION_HF_CUTOFF]=5000; params[ROOM_PARAM_ATTENUATION_HF_FLOOR_DB]=-24.0; params[ROOM_PARAM_ATTENUATION_HF_RATIO_EXP]=1.0; params[ROOM_PARAM_ATTENUATION_REVERB_SCALE]=0.0; override_other_sources=false; reverb=ROOM_REVERB_HALL; // octree_id=0; level=-1; } SpatialSound2DServerSW::Source::Source() { params[SOURCE_PARAM_VOLUME_DB]=0.0; params[SOURCE_PARAM_PITCH_SCALE]=1.0; params[SOURCE_PARAM_ATTENUATION_MIN_DISTANCE]=1; params[SOURCE_PARAM_ATTENUATION_MAX_DISTANCE]=100; params[SOURCE_PARAM_ATTENUATION_DISTANCE_EXP]=1.0; //linear (and not really good) stream=NULL; voices.resize(1); last_voice=0; } SpatialSound2DServerSW::Source::Voice::Voice() { active=false; restart=false; pitch_scale=1.0; volume_scale=0.0; voice_rid=AudioServer::get_singleton()->voice_create(); } SpatialSound2DServerSW::Source::Voice::~Voice() { AudioServer::get_singleton()->free(voice_rid); } SpatialSound2DServerSW::Listener::Listener() { params[LISTENER_PARAM_VOLUME_SCALE_DB]=0.0; params[LISTENER_PARAM_PITCH_SCALE]=1.0; params[LISTENER_PARAM_ATTENUATION_SCALE]=1.0; params[LISTENER_PARAM_PAN_RANGE]=128; } /* SPACE */ RID SpatialSound2DServerSW::space_create() { Space* space = memnew( Space ); RID space_rid = space_owner.make_rid(space); space->default_room=room_create(); room_set_space(space->default_room,space_rid); return space_rid; } /* ROOM */ RID SpatialSound2DServerSW::room_create() { Room *room = memnew( Room ); return room_owner.make_rid(room); } void SpatialSound2DServerSW::room_set_space(RID p_room,RID p_space) { Room *room = room_owner.get(p_room); ERR_FAIL_COND(!room); if (room->space.is_valid()) { Space *space = space_owner.get(room->space); space->rooms.erase(p_room); // space->octree.erase(room->octree_id); //room->octree_id=0; } room->space=RID(); if (p_space.is_valid()) { Space *space = space_owner.get(p_space); ERR_FAIL_COND(!space); space->rooms.insert(p_room); // room->octree_id=space->octree.create(room,AABB()); //set bounds // AABB aabb = room->bounds.is_empty()?AABB():room->bounds.get_aabb(); // space->octree.move(room->octree_id,room->transform.xform(aabb)); room->space=p_space; } } RID SpatialSound2DServerSW::room_get_space(RID p_room) const { Room *room = room_owner.get(p_room); ERR_FAIL_COND_V(!room,RID()); return room->space; } void SpatialSound2DServerSW::room_set_bounds(RID p_room, const DVector& p_bounds) { Room *room = room_owner.get(p_room); ERR_FAIL_COND(!room); room->bounds=p_bounds; if (!room->space.is_valid()) return; // AABB aabb = room->bounds.is_empty()?AABB():room->bounds.get_aabb(); // Space* space = space_owner.get(room->space); // ERR_FAIL_COND(!space); // space->octree.move(room->octree_id,room->transform.xform(aabb)); } DVector SpatialSound2DServerSW::room_get_bounds(RID p_room) const { Room *room = room_owner.get(p_room); ERR_FAIL_COND_V(!room,DVector()); return room->bounds; } void SpatialSound2DServerSW::room_set_transform(RID p_room, const Matrix32& p_transform) { if (space_owner.owns(p_room)) p_room=space_owner.get(p_room)->default_room; Room *room = room_owner.get(p_room); ERR_FAIL_COND(!room); room->transform=p_transform; room->inverse_transform=p_transform.affine_inverse(); // needs to be done to unscale BSP properly if (!room->space.is_valid()) return; /* if (!room->bounds.is_empty()) { Space* space = space_owner.get(room->space); ERR_FAIL_COND(!space); //space->octree.move(room->octree_id,room->transform.xform(room->bounds.get_aabb())); }*/ } Matrix32 SpatialSound2DServerSW::room_get_transform(RID p_room) const { if (space_owner.owns(p_room)) p_room=space_owner.get(p_room)->default_room; Room *room = room_owner.get(p_room); ERR_FAIL_COND_V(!room,Matrix32()); return room->transform; } void SpatialSound2DServerSW::room_set_param(RID p_room, RoomParam p_param, float p_value) { if (space_owner.owns(p_room)) p_room=space_owner.get(p_room)->default_room; ERR_FAIL_INDEX(p_param,ROOM_PARAM_MAX); Room *room = room_owner.get(p_room); ERR_FAIL_COND(!room); room->params[p_param]=p_value; } float SpatialSound2DServerSW::room_get_param(RID p_room, RoomParam p_param) const { if (space_owner.owns(p_room)) p_room=space_owner.get(p_room)->default_room; ERR_FAIL_INDEX_V(p_param,ROOM_PARAM_MAX,0); Room *room = room_owner.get(p_room); ERR_FAIL_COND_V(!room,0); return room->params[p_param]; } void SpatialSound2DServerSW::room_set_level(RID p_room, int p_level) { Room *room = room_owner.get(p_room); ERR_FAIL_COND(!room); room->level =p_level; } int SpatialSound2DServerSW::room_get_level(RID p_room) const { Room *room = room_owner.get(p_room); ERR_FAIL_COND_V(!room,0); return room->level; } void SpatialSound2DServerSW::room_set_reverb(RID p_room, RoomReverb p_reverb) { if (space_owner.owns(p_room)) p_room=space_owner.get(p_room)->default_room; Room *room = room_owner.get(p_room); ERR_FAIL_COND(!room); room->reverb=p_reverb; } SpatialSound2DServerSW::RoomReverb SpatialSound2DServerSW::room_get_reverb(RID p_room) const { if (space_owner.owns(p_room)) p_room=space_owner.get(p_room)->default_room; Room *room = room_owner.get(p_room); ERR_FAIL_COND_V(!room,ROOM_REVERB_SMALL); return room->reverb; } //useful for underwater or rooms with very strange conditions void SpatialSound2DServerSW::room_set_force_params_to_all_sources(RID p_room, bool p_force) { if (space_owner.owns(p_room)) p_room=space_owner.get(p_room)->default_room; Room *room = room_owner.get(p_room); ERR_FAIL_COND(!room); room->override_other_sources=p_force; } bool SpatialSound2DServerSW::room_is_forcing_params_to_all_sources(RID p_room) const { if (space_owner.owns(p_room)) p_room=space_owner.get(p_room)->default_room; Room *room = room_owner.get(p_room); ERR_FAIL_COND_V(!room,false); return room->override_other_sources; } /* SOURCE */ RID SpatialSound2DServerSW::source_create(RID p_space) { Space *space = space_owner.get(p_space); ERR_FAIL_COND_V(!space,RID()); Source *source = memnew( Source ); source->space=p_space; RID source_rid = source_owner.make_rid(source); space->sources.insert(source_rid); return source_rid; } void SpatialSound2DServerSW::source_set_polyphony(RID p_source,int p_voice_count) { ERR_FAIL_COND(p_voice_count<=0); // more than 32 is too much, change this if you really need more if (p_voice_count>32) { ERR_PRINT("Voices will be clipped to 32"); p_voice_count=32; } Source *source = source_owner.get(p_source); ERR_FAIL_COND(!source); if (p_voice_countvoices.size()) { for(int i=p_voice_count;ivoices.size();i++) { active_voices.erase(ActiveVoice(source,i)); //erase from active voices } } source->voices.resize(p_voice_count); } int SpatialSound2DServerSW::source_get_polyphony(RID p_source) const { Source *source = source_owner.get(p_source); ERR_FAIL_COND_V(!source,-1); return source->voices.size(); } void SpatialSound2DServerSW::source_set_transform(RID p_source, const Matrix32& p_transform) { Source *source = source_owner.get(p_source); ERR_FAIL_COND(!source); source->transform=p_transform; source->transform.orthonormalize(); } Matrix32 SpatialSound2DServerSW::source_get_transform(RID p_source) const { Source *source = source_owner.get(p_source); ERR_FAIL_COND_V(!source,Matrix32()); return source->transform; } void SpatialSound2DServerSW::source_set_param(RID p_source, SourceParam p_param, float p_value) { ERR_FAIL_INDEX(p_param,SOURCE_PARAM_MAX); Source *source = source_owner.get(p_source); ERR_FAIL_COND(!source); source->params[p_param]=p_value; } float SpatialSound2DServerSW::source_get_param(RID p_source, SourceParam p_param) const { ERR_FAIL_INDEX_V(p_param,SOURCE_PARAM_MAX,0); Source *source = source_owner.get(p_source); ERR_FAIL_COND_V(!source,0); return source->params[p_param]; } void SpatialSound2DServerSW::source_set_audio_stream(RID p_source, AudioServer::AudioStream *p_stream) { Source *source = source_owner.get(p_source); ERR_FAIL_COND(!source); AudioServer::get_singleton()->lock(); source->stream=p_stream; _THREAD_SAFE_METHOD_ if (!p_stream) { streaming_sources.erase(source); active_voices.erase(ActiveVoice(source,VOICE_IS_STREAM)); } else { streaming_sources.insert(source); active_voices.insert(ActiveVoice(source,VOICE_IS_STREAM)); zeromem(source->stream_data.filter_state,sizeof(Source::StreamData::FilterState)*4); //reset filter for safetyness p_stream->set_mix_rate(AudioServer::get_singleton()->get_default_mix_rate()); } AudioServer::get_singleton()->unlock(); } //null to unset SpatialSound2DServer::SourceVoiceID SpatialSound2DServerSW::source_play_sample(RID p_source, RID p_sample, int p_mix_rate, int p_voice) { Source *source = source_owner.get(p_source); ERR_FAIL_COND_V(!source,SOURCE_INVALID_VOICE); int to_play=0; if (p_voice==SOURCE_NEXT_VOICE) { to_play=source->last_voice+1; if (to_play>=source->voices.size()) to_play=0; } else to_play=p_voice; ERR_FAIL_INDEX_V(to_play,source->voices.size(),SOURCE_INVALID_VOICE); source->voices[to_play].restart=true; source->voices[to_play].sample_rid=p_sample; source->voices[to_play].sample_mix_rate=p_mix_rate; source->voices[to_play].pitch_scale=1; source->voices[to_play].volume_scale=0; source->last_voice=to_play; active_voices.insert(ActiveVoice(source,to_play)); return to_play; } /* VOICES */ void SpatialSound2DServerSW::source_voice_set_pitch_scale(RID p_source, SourceVoiceID p_voice, float p_pitch_scale) { Source *source = source_owner.get(p_source); ERR_FAIL_COND(!source); ERR_FAIL_INDEX(p_voice,source->voices.size()); source->voices[p_voice].pitch_scale=p_pitch_scale; } void SpatialSound2DServerSW::source_voice_set_volume_scale_db(RID p_source, SourceVoiceID p_voice, float p_db) { Source *source = source_owner.get(p_source); ERR_FAIL_COND(!source); ERR_FAIL_INDEX(p_voice,source->voices.size()); source->voices[p_voice].volume_scale=p_db; } bool SpatialSound2DServerSW::source_is_voice_active(RID p_source, SourceVoiceID p_voice) const { Source *source = source_owner.get(p_source); ERR_FAIL_COND_V(!source,false); ERR_FAIL_INDEX_V(p_voice,source->voices.size(),false); return source->voices[p_voice].active || source->voices[p_voice].restart; } void SpatialSound2DServerSW::source_stop_voice(RID p_source, SourceVoiceID p_voice) { Source *source = source_owner.get(p_source); ERR_FAIL_COND(!source); ERR_FAIL_INDEX(p_voice,source->voices.size()); if (source->voices[p_voice].active) { AudioServer::get_singleton()->voice_stop(source->voices[p_voice].voice_rid); } source->voices[p_voice].active=false; source->voices[p_voice].restart=false; active_voices.erase(ActiveVoice(source,p_voice)); } /* LISTENER */ RID SpatialSound2DServerSW::listener_create() { Listener *listener = memnew( Listener ); RID listener_rid = listener_owner.make_rid(listener); return listener_rid; } void SpatialSound2DServerSW::listener_set_space(RID p_listener,RID p_space) { Listener *listener = listener_owner.get(p_listener); ERR_FAIL_COND(!listener); if (listener->space.is_valid()) { Space *lspace = space_owner.get(listener->space); ERR_FAIL_COND(!lspace); lspace->listeners.erase(p_listener); } listener->space=RID(); if (p_space.is_valid()) { Space *space = space_owner.get(p_space); ERR_FAIL_COND(!space); listener->space=p_space; space->listeners.insert(p_listener); } } void SpatialSound2DServerSW::listener_set_transform(RID p_listener, const Matrix32& p_transform) { Listener *listener = listener_owner.get(p_listener); ERR_FAIL_COND(!listener); listener->transform=p_transform; listener->transform.orthonormalize(); //must be done.. } Matrix32 SpatialSound2DServerSW::listener_get_transform(RID p_listener) const { Listener *listener = listener_owner.get(p_listener); ERR_FAIL_COND_V(!listener,Matrix32()); return listener->transform; } void SpatialSound2DServerSW::listener_set_param(RID p_listener, ListenerParam p_param, float p_value) { ERR_FAIL_INDEX(p_param,LISTENER_PARAM_MAX); Listener *listener = listener_owner.get(p_listener); ERR_FAIL_COND(!listener); listener->params[p_param]=p_value; } float SpatialSound2DServerSW::listener_get_param(RID p_listener, ListenerParam p_param) const { ERR_FAIL_INDEX_V(p_param,LISTENER_PARAM_MAX,0); Listener *listener = listener_owner.get(p_listener); ERR_FAIL_COND_V(!listener,0); return listener->params[p_param]; } /* MISC */ void SpatialSound2DServerSW::free(RID p_id) { if (space_owner.owns(p_id)) { Space *space = space_owner.get(p_id); free(space->default_room); while(space->listeners.size()) { listener_set_space(space->listeners.front()->get(),RID()); } while(space->sources.size()) { free(space->sources.front()->get()); } while(space->rooms.size()) { room_set_space(space->rooms.front()->get(),RID()); } space_owner.free(p_id); memdelete(space); } else if (source_owner.owns(p_id)) { Source *source = source_owner.get(p_id); if (source->stream) source_set_audio_stream(p_id,NULL); Space *space = space_owner.get(source->space); ERR_FAIL_COND(!space); space->sources.erase(p_id); for(int i=0;ivoices.size();i++) { active_voices.erase(ActiveVoice(source,i)); } source_owner.free(p_id); memdelete(source); } else if (listener_owner.owns(p_id)) { Listener *listener = listener_owner.get(p_id); if (listener->space.is_valid()) { Space *space = space_owner.get(listener->space); ERR_FAIL_COND(!space); space->listeners.erase(p_id); } listener_owner.free(p_id); memdelete(listener); } else if (room_owner.owns(p_id)) { Room *room = room_owner.get(p_id); if (room->space.is_valid()) { Space *space = space_owner.get(room->space); ERR_FAIL_COND(!space); // space->octree.erase(room->octree_id); space->rooms.erase(p_id); } room_owner.free(p_id); memdelete(room); } else { ERR_PRINT("Attempt to free invalid ID") ; } } void SpatialSound2DServerSW::_clean_up_owner(RID_OwnerBase *p_owner, const char *p_area) { List rids; p_owner->get_owned_list(&rids); for(List::Element *I=rids.front();I;I=I->next()) { if (OS::get_singleton()->is_stdout_verbose()) { print_line("Leaked RID ("+itos(I->get().get_id())+") of type "+String(p_area)); } free(I->get()); } } void SpatialSound2DServerSW::init() { internal_buffer = memnew_arr(int32_t, INTERNAL_BUFFER_SIZE*INTERNAL_BUFFER_MAX_CHANNELS); internal_buffer_channels=AudioServer::get_singleton()->get_default_channel_count(); internal_audio_stream = memnew( InternalAudioStream ); internal_audio_stream->owner=this; internal_audio_stream_rid = AudioServer::get_singleton()->audio_stream_create(internal_audio_stream); AudioServer::get_singleton()->stream_set_active(internal_audio_stream_rid,true); } bool SpatialSound2DServerSW::internal_buffer_mix(int32_t *p_buffer,int p_frames) { if (streaming_sources.size()==0) return false; //nothing to mix for (Set::Element *E=streaming_sources.front();E;E=E->next()) { Source *s=E->get(); ERR_CONTINUE(!s->stream); int channels = s->stream->get_channel_count(); Source::StreamData &sd=s->stream_data; int todo=p_frames; AudioFilterSW filter; filter.set_sampling_rate(AudioServer::get_singleton()->get_default_mix_rate()); filter.set_cutoff(sd.filter_cutoff); filter.set_gain(sd.filter_gain); filter.set_resonance(1); filter.set_mode(AudioFilterSW::HIGHSHELF); filter.set_stages(1); AudioFilterSW::Coeffs coefs; filter.prepare_coefficients(&coefs); int32_t in[4]; #ifndef SPATIAL_SOUND_SERVER_NO_FILTER #define DO_FILTER(m_c)\ {\ float val = in[m_c];\ float pre=val;\ val = val*coefs.b0 + sd.filter_state[m_c].hb[0]*coefs.b1 + sd.filter_state[m_c].hb[1]*coefs.b2 + sd.filter_state[m_c].ha[0]*coefs.a1 + sd.filter_state[m_c].ha[1]*coefs.a2;\ sd.filter_state[m_c].ha[1]=sd.filter_state[m_c].ha[0];\ sd.filter_state[m_c].hb[1]=sd.filter_state[m_c].hb[0]; \ sd.filter_state[m_c].hb[0]=pre;\ sd.filter_state[m_c].ha[0]=val;\ in[m_c]=Math::fast_ftoi(val);\ } #else #define DO_FILTER(m_c) #endif while(todo) { int to_mix=MIN(todo,INTERNAL_BUFFER_SIZE); s->stream->mix(internal_buffer,to_mix); switch(internal_buffer_channels) { case 2: { float p = sd.panning.x*0.5+0.5; float panf[2]={ (1.0-p),p }; panf[0]*=sd.volume; panf[1]*=sd.volume; int32_t pan[2]={Math::fast_ftoi(panf[0]*(1<<16)),Math::fast_ftoi(panf[1]*(1<<16))}; switch(channels) { case 1: { for(int i=0;i>16)*pan[0]); p_buffer[(i<<1)+1]=((in[1]>>16)*pan[1]); } } break; case 2: { for(int i=0;i>16)*pan[0]); p_buffer[(i<<1)+1]=((in[1]>>16)*pan[1]); } } break; case 4: { for(int i=0;i>1; in[1]=(internal_buffer[(i<<2)+1]+internal_buffer[(i<<2)+3])>>1; DO_FILTER(0); DO_FILTER(1); p_buffer[(i<<1)+0]=((in[0]>>16)*pan[0]); p_buffer[(i<<1)+1]=((in[1]>>16)*pan[1]); } } break; } break; } break; case 4: { float xp = sd.panning.x*0.5+0.5; float yp = sd.panning.y*0.5+0.5; float panf[4]={ (1.0-xp)*(1.0-yp),(xp)*(1.0-yp),(1.0-xp)*(yp),(xp)*(yp) }; panf[0]*=sd.volume; panf[1]*=sd.volume; panf[2]*=sd.volume; panf[3]*=sd.volume; int32_t pan[4]={ Math::fast_ftoi(panf[0]*(1<<16)), Math::fast_ftoi(panf[1]*(1<<16)), Math::fast_ftoi(panf[2]*(1<<16)), Math::fast_ftoi(panf[3]*(1<<16))}; switch(channels) { case 1: { for(int i=0;i>16)*pan[0]); p_buffer[(i<<2)+1]=((in[1]>>16)*pan[1]); p_buffer[(i<<2)+2]=((in[2]>>16)*pan[2]); p_buffer[(i<<2)+3]=((in[3]>>16)*pan[3]); } } break; case 2: { for(int i=0;i>16)*pan[0]); p_buffer[(i<<2)+1]=((in[1]>>16)*pan[1]); p_buffer[(i<<2)+2]=((in[2]>>16)*pan[2]); p_buffer[(i<<2)+3]=((in[3]>>16)*pan[3]); } } break; case 4: { for(int i=0;i>16)*pan[0]); p_buffer[(i<<2)+1]=((in[1]>>16)*pan[1]); p_buffer[(i<<2)+2]=((in[2]>>16)*pan[2]); p_buffer[(i<<2)+3]=((in[3]>>16)*pan[3]); } } break; } break; } break; case 6: { } break; } p_buffer+=to_mix*internal_buffer_channels; todo-=to_mix; } } return true; } void SpatialSound2DServerSW::update(float p_delta) { List to_disable; for(Set::Element *E=active_voices.front();E;E=E->next()) { Source *source = E->get().source; int voice = E->get().voice; if (voice!=VOICE_IS_STREAM) { Source::Voice &v=source->voices[voice]; ERR_CONTINUE(!v.active && !v.restart); // likely a bug... } //this could be optimized at some point... am not sure Space *space=space_owner.get(source->space); Room *room=room_owner.get(space->default_room); int max_level=-0x80000000; /* int rooms_culled = space->octree.cull_point(source->transform.origin,cull_rooms,MAX_CULL_ROOMS); for(int i=0;ibounds.is_empty() ); // how did this happen?? if (r->level<=max_level) //ignore optimization (level too low) continue; Vector2 local_point = r->inverse_transform.xform(source->transform.origin); if (!r->bounds.point_is_inside(local_point)) continue; room=r; max_level=r->level; } */ //compute mixing weights (support for multiple listeners in the same output) float total_distance=0; for(Set::Element *L=space->listeners.front();L;L=L->next()) { Listener *listener=listener_owner.get(L->get()); float d = listener->transform.get_origin().distance_to(source->transform.get_origin()); if (d==0) d=0.1; total_distance+=d; } //compute spatialization variables, weighted according to distance float volume_attenuation = 0.0; float air_absorption_hf_cutoff = 0.0; float air_absorption = 0.0; float pitch_scale=0.0; Vector2 panning; for(Set::Element *L=space->listeners.front();L;L=L->next()) { Listener *listener=listener_owner.get(L->get()); Vector2 rel_vector = -listener->transform.xform_inv(source->transform.get_origin()); //Vector2 source_rel_vector = source->transform.xform_inv(listener->transform.get_origin()).normalized(); float distance=rel_vector.length(); float weight = distance/total_distance; float pscale=1.0; float distance_scale=listener->params[LISTENER_PARAM_ATTENUATION_SCALE]*room->params[ROOM_PARAM_ATTENUATION_SCALE]; float distance_min=source->params[SOURCE_PARAM_ATTENUATION_MIN_DISTANCE]*distance_scale; float distance_max=source->params[SOURCE_PARAM_ATTENUATION_MAX_DISTANCE]*distance_scale; float attenuation_exp=source->params[SOURCE_PARAM_ATTENUATION_DISTANCE_EXP]; float attenuation=1; if (distance_max>0) { distance = CLAMP(distance,distance_min,distance_max); attenuation = Math::pow(1.0 - ((distance - distance_min)/(distance_max-distance_min)),CLAMP(attenuation_exp,0.001,16)); } float hf_attenuation_cutoff = room->params[ROOM_PARAM_ATTENUATION_HF_CUTOFF]; float hf_attenuation_exp = room->params[ROOM_PARAM_ATTENUATION_HF_RATIO_EXP]; float hf_attenuation_floor = room->params[ROOM_PARAM_ATTENUATION_HF_FLOOR_DB]; float absorption=Math::db2linear(Math::lerp(hf_attenuation_floor,0,Math::pow(attenuation,hf_attenuation_exp))); // source emission cone /* only for 3D float emission_deg=source->params[SOURCE_PARAM_EMISSION_CONE_DEGREES]; float emission_attdb=source->params[SOURCE_PARAM_EMISSION_CONE_ATTENUATION_DB]; absorption*=_get_attenuation(source_rel_vector.dot(Vector2(0,0,-1)),emission_deg,emission_attdb); */ Vector2 vpanning=rel_vector.normalized(); if (distance < listener->params[LISTENER_PARAM_PAN_RANGE]) vpanning*=distance/listener->params[LISTENER_PARAM_PAN_RANGE]; //listener stuff { // head cone /* only for 3D float reception_deg=listener->params[LISTENER_PARAM_RECEPTION_CONE_DEGREES]; float reception_attdb=listener->params[LISTENER_PARAM_RECEPTION_CONE_ATTENUATION_DB]; absorption*=_get_attenuation(vpanning.dot(Vector2(0,0,-1)),reception_deg,reception_attdb); */ // scale attenuation*=Math::db2linear(listener->params[LISTENER_PARAM_VOLUME_SCALE_DB]); pscale*=Math::db2linear(listener->params[LISTENER_PARAM_PITCH_SCALE]); } //add values volume_attenuation+=weight*attenuation; // plus other stuff i guess air_absorption+=weight*absorption; air_absorption_hf_cutoff+=weight*hf_attenuation_cutoff; panning+=vpanning*weight; pitch_scale+=pscale*weight; } RoomReverb reverb_room=ROOM_REVERB_HALL; float reverb_send=0; /* APPLY ROOM SETTINGS */ { pitch_scale*=room->params[ROOM_PARAM_PITCH_SCALE]; volume_attenuation*=Math::db2linear(room->params[ROOM_PARAM_VOLUME_SCALE_DB]); reverb_room=room->reverb; reverb_send=Math::lerp(1.0,volume_attenuation,room->params[ROOM_PARAM_ATTENUATION_REVERB_SCALE])*room->params[ROOM_PARAM_REVERB_SEND]; } /* UPDATE VOICE & STREAM */ if (voice==VOICE_IS_STREAM) { //update voice!! source->stream_data.panning=panning; source->stream_data.volume=volume_attenuation*Math::db2linear(source->params[SOURCE_PARAM_VOLUME_DB]); source->stream_data.reverb=reverb_room; source->stream_data.reverb_send=reverb_send; source->stream_data.filter_gain=air_absorption; source->stream_data.filter_cutoff=air_absorption_hf_cutoff; if (!source->stream) //stream is gone bye bye to_disable.push_back(ActiveVoice(source,voice)); // oh well.. } else if (voice>=0) { //update stream!! Source::Voice &v=source->voices[voice]; if (v.restart) AudioServer::get_singleton()->voice_play(v.voice_rid,v.sample_rid); float volume_scale = Math::db2linear(v.volume_scale)*Math::db2linear(source->params[SOURCE_PARAM_VOLUME_DB]); float volume = volume_attenuation*volume_scale; reverb_send*=volume_scale; int mix_rate = v.sample_mix_rate*v.pitch_scale*pitch_scale*source->params[SOURCE_PARAM_PITCH_SCALE]; if (mix_rate<=0) { ERR_PRINT("Invalid mix rate for voice (0) check for invalid pitch_scale param."); to_disable.push_back(ActiveVoice(source,voice)); // oh well.. continue; //invalid mix rate, disabling } if (v.restart || v.last_volume!=volume) AudioServer::get_singleton()->voice_set_volume(v.voice_rid,volume); if (v.restart || v.last_mix_rate!=mix_rate) AudioServer::get_singleton()->voice_set_mix_rate(v.voice_rid,mix_rate); if (v.restart || v.last_filter_gain!=air_absorption || v.last_filter_cutoff!=air_absorption_hf_cutoff) AudioServer::get_singleton()->voice_set_filter(v.voice_rid,AudioServer::FILTER_HIGH_SHELF,air_absorption_hf_cutoff,1.0,air_absorption); if (v.restart || v.last_panning!=panning) { AudioServer::get_singleton()->voice_set_pan(v.voice_rid,-panning.x,panning.y,0); } if (v.restart || v.last_reverb_room!=reverb_room || v.last_reverb_send!=reverb_send) AudioServer::get_singleton()->voice_set_reverb(v.voice_rid,AudioServer::ReverbRoomType(reverb_room),reverb_send); v.last_volume=volume; v.last_mix_rate=mix_rate; v.last_filter_gain=air_absorption; v.last_filter_cutoff=air_absorption_hf_cutoff; v.last_panning=panning; v.last_reverb_room=reverb_room; v.last_reverb_send=reverb_send; v.restart=false; v.active=true; if (!AudioServer::get_singleton()->voice_is_active(v.voice_rid)) to_disable.push_back(ActiveVoice(source,voice)); // oh well.. } } while(to_disable.size()) { ActiveVoice av = to_disable.front()->get(); av.source->voices[av.voice].active=false; av.source->voices[av.voice].restart=false; active_voices.erase(av); to_disable.pop_front(); } } void SpatialSound2DServerSW::finish() { AudioServer::get_singleton()->free(internal_audio_stream_rid); memdelete(internal_audio_stream); _clean_up_owner(&source_owner,"Source"); _clean_up_owner(&listener_owner,"Listener"); _clean_up_owner(&room_owner,"Room"); _clean_up_owner(&space_owner,"Space"); memdelete_arr(internal_buffer); } SpatialSound2DServerSW::SpatialSound2DServerSW() { }