diff options
author | Juan Linietsky <reduzio@gmail.com> | 2014-02-09 22:10:30 -0300 |
---|---|---|
committer | Juan Linietsky <reduzio@gmail.com> | 2014-02-09 22:10:30 -0300 |
commit | 0b806ee0fc9097fa7bda7ac0109191c9c5e0a1ac (patch) | |
tree | 276c4d099e178eb67fbd14f61d77b05e3808e9e3 /servers/spatial_sound | |
parent | 0e49da1687bc8192ed210947da52c9e5c5f301bb (diff) |
GODOT IS OPEN SOURCE
Diffstat (limited to 'servers/spatial_sound')
-rw-r--r-- | servers/spatial_sound/SCsub | 7 | ||||
-rw-r--r-- | servers/spatial_sound/spatial_sound_server_sw.cpp | 1049 | ||||
-rw-r--r-- | servers/spatial_sound/spatial_sound_server_sw.h | 249 |
3 files changed, 1305 insertions, 0 deletions
diff --git a/servers/spatial_sound/SCsub b/servers/spatial_sound/SCsub new file mode 100644 index 0000000000..16fe3a59ac --- /dev/null +++ b/servers/spatial_sound/SCsub @@ -0,0 +1,7 @@ +Import('env') + +env.add_source_files(env.servers_sources,"*.cpp") + +Export('env') + + diff --git a/servers/spatial_sound/spatial_sound_server_sw.cpp b/servers/spatial_sound/spatial_sound_server_sw.cpp new file mode 100644 index 0000000000..7ec29d32cb --- /dev/null +++ b/servers/spatial_sound/spatial_sound_server_sw.cpp @@ -0,0 +1,1049 @@ +/*************************************************/ +/* spatial_sound_server_sw.cpp */ +/*************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/*************************************************/ +/* Source code within this file is: */ +/* (c) 2007-2010 Juan Linietsky, Ariel Manzur */ +/* All Rights Reserved. */ +/*************************************************/ + +#include "spatial_sound_server_sw.h" +#include "os/os.h" +#include "servers/audio/audio_filter_sw.h" + + + +int SpatialSoundServerSW::InternalAudioStream::get_channel_count() const { + + return AudioServer::get_singleton()->get_default_channel_count(); +} + +void SpatialSoundServerSW::InternalAudioStream::set_mix_rate(int p_rate) { + + +} + +void SpatialSoundServerSW::InternalAudioStream::update() { + + owner->_update_sources(); +} + +bool SpatialSoundServerSW::InternalAudioStream::mix(int32_t *p_buffer,int p_frames) { + + return owner->internal_buffer_mix(p_buffer,p_frames); +} + +void SpatialSoundServerSW::_update_sources() { + + _THREAD_SAFE_METHOD_ + for (Set<Source*>::Element *E=streaming_sources.front();E;E=E->next()) { + + Source *s=E->get(); + ERR_CONTINUE(!s->stream); + s->stream->update(); + } +} + + +SpatialSoundServerSW::Room::Room() { + +// params[ROOM_PARAM_SPEED_OF_SOUND]=343.0; + params[ROOM_PARAM_SPEED_OF_SOUND_SCALE]=1; + params[ROOM_PARAM_DOPPLER_FACTOR]=1.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; + + +} + + +SpatialSoundServerSW::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) + params[SOURCE_PARAM_EMISSION_CONE_DEGREES]=180.0; //cone disabled + params[SOURCE_PARAM_EMISSION_CONE_ATTENUATION_DB]=-6.0; //minus 6 db attenuation + stream=NULL; + voices.resize(1); + last_voice=0; +} + +SpatialSoundServerSW::Source::Voice::Voice() { + + active=false; + restart=false; + pitch_scale=1.0; + volume_scale=0.0; + voice_rid=AudioServer::get_singleton()->voice_create(); + +} +SpatialSoundServerSW::Source::Voice::~Voice() { + + AudioServer::get_singleton()->free(voice_rid); +} + + +SpatialSoundServerSW::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_RECEPTION_CONE_DEGREES]=60.0; + params[LISTENER_PARAM_RECEPTION_CONE_ATTENUATION_DB]=-6; // minus six decibels + +} + +/* SPACE */ +RID SpatialSoundServerSW::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 SpatialSoundServerSW::room_create() { + + Room *room = memnew( Room ); + return room_owner.make_rid(room); +} + +void SpatialSoundServerSW::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 SpatialSoundServerSW::room_get_space(RID p_room) const { + + Room *room = room_owner.get(p_room); + ERR_FAIL_COND_V(!room,RID()); + + + return room->space; +} + + + +void SpatialSoundServerSW::room_set_bounds(RID p_room, const BSP_Tree& 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)); + +} +BSP_Tree SpatialSoundServerSW::room_get_bounds(RID p_room) const { + Room *room = room_owner.get(p_room); + ERR_FAIL_COND_V(!room,BSP_Tree()); + + return room->bounds; +} + +void SpatialSoundServerSW::room_set_transform(RID p_room, const Transform& 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())); + } +} + +Transform SpatialSoundServerSW::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,Transform()); + return room->transform; +} + + +void SpatialSoundServerSW::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 SpatialSoundServerSW::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 SpatialSoundServerSW::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 SpatialSoundServerSW::room_get_level(RID p_room) const { + + Room *room = room_owner.get(p_room); + ERR_FAIL_COND_V(!room,0); + return room->level; + +} + + +void SpatialSoundServerSW::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; + +} +SpatialSoundServerSW::RoomReverb SpatialSoundServerSW::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 SpatialSoundServerSW::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 SpatialSoundServerSW::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 SpatialSoundServerSW::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 SpatialSoundServerSW::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_count<source->voices.size()) { + + for(int i=p_voice_count;i<source->voices.size();i++) { + active_voices.erase(ActiveVoice(source,i)); //erase from active voices + } + } + source->voices.resize(p_voice_count); + +} + +int SpatialSoundServerSW::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 SpatialSoundServerSW::source_set_transform(RID p_source, const Transform& p_transform) { + + Source *source = source_owner.get(p_source); + ERR_FAIL_COND(!source); + source->transform=p_transform; + source->transform.orthonormalize(); +} +Transform SpatialSoundServerSW::source_get_transform(RID p_source) const { + + Source *source = source_owner.get(p_source); + ERR_FAIL_COND_V(!source,Transform()); + return source->transform; +} + +void SpatialSoundServerSW::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 SpatialSoundServerSW::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 SpatialSoundServerSW::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 + +SpatialSoundServer::SourceVoiceID SpatialSoundServerSW::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 SpatialSoundServerSW::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 SpatialSoundServerSW::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 SpatialSoundServerSW::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 SpatialSoundServerSW::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 SpatialSoundServerSW::listener_create() { + + Listener *listener = memnew( Listener ); + RID listener_rid = listener_owner.make_rid(listener); + return listener_rid; + +} + +void SpatialSoundServerSW::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 SpatialSoundServerSW::listener_set_transform(RID p_listener, const Transform& p_transform) { + + Listener *listener = listener_owner.get(p_listener); + ERR_FAIL_COND(!listener); + listener->transform=p_transform; + listener->transform.orthonormalize(); //must be done.. +} +Transform SpatialSoundServerSW::listener_get_transform(RID p_listener) const { + + Listener *listener = listener_owner.get(p_listener); + ERR_FAIL_COND_V(!listener,Transform()); + return listener->transform; +} + +void SpatialSoundServerSW::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 SpatialSoundServerSW::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 SpatialSoundServerSW::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;i<source->voices.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 SpatialSoundServerSW::_clean_up_owner(RID_OwnerBase *p_owner, const char *p_area) { + + List<RID> rids; + p_owner->get_owned_list(&rids); + + for(List<RID>::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 SpatialSoundServerSW::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); + +} + + + +static float _get_attenuation(float cosine, float angle, float attenuation) { + + + float listener_ang = Math::rad2deg(Math::acos(cosine))-angle; + + if (listener_ang>0 && angle<180.0) { + listener_ang/=(180.0-angle); + return Math::db2linear(Math::sin(listener_ang*(Math_PI/2.0))*attenuation); + } + return 1.0; +} + + +bool SpatialSoundServerSW::internal_buffer_mix(int32_t *p_buffer,int p_frames) { + + if (streaming_sources.size()==0) + return false; //nothing to mix + + + for (Set<Source*>::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<to_mix;i++) { + + in[0]=internal_buffer[i]; + in[1]=internal_buffer[i]; + 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; + case 2: { + + for(int i=0;i<to_mix;i++) { + + in[0]=internal_buffer[(i<<1)+0]; + in[1]=internal_buffer[(i<<1)+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; + case 4: { + + for(int i=0;i<to_mix;i++) { + + in[0]=(internal_buffer[(i<<2)+0]+internal_buffer[(i<<2)+2])>>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<to_mix;i++) { + + in[0]=internal_buffer[i]; + in[1]=internal_buffer[i]; + in[2]=internal_buffer[i]; + in[3]=internal_buffer[i]; + DO_FILTER(0); + DO_FILTER(1); + DO_FILTER(2); + DO_FILTER(3); + p_buffer[(i<<2)+0]=((in[0]>>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<to_mix;i++) { + + in[0]=internal_buffer[(i<<1)+0]; + in[1]=internal_buffer[(i<<1)+1]; + in[2]=internal_buffer[(i<<1)+0]; + in[3]=internal_buffer[(i<<1)+1]; + DO_FILTER(0); + DO_FILTER(1); + DO_FILTER(2); + DO_FILTER(3); + p_buffer[(i<<2)+0]=((in[0]>>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<to_mix;i++) { + + in[0]=internal_buffer[(i<<2)+0]; + in[1]=internal_buffer[(i<<2)+1]; + in[2]=internal_buffer[(i<<2)+2]; + in[3]=internal_buffer[(i<<2)+3]; + DO_FILTER(0); + DO_FILTER(1); + DO_FILTER(2); + DO_FILTER(3); + p_buffer[(i<<2)+0]=((in[0]>>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 SpatialSoundServerSW::update(float p_delta) { + + List<ActiveVoice> to_disable; + + + for(Set<ActiveVoice>::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;i<rooms_culled;i++) { + + Room *r=cull_rooms[i]; + ERR_CONTINUE( r->bounds.is_empty() ); // how did this happen?? + if (r->level<=max_level) //ignore optimization (level too low) + continue; + Vector3 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<RID>::Element *L=space->listeners.front();L;L=L->next()) { + Listener *listener=listener_owner.get(L->get()); + total_distance+=listener->transform.origin.distance_to(source->transform.origin); + } + + //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; + Vector3 panning; + + + for(Set<RID>::Element *L=space->listeners.front();L;L=L->next()) { + + Listener *listener=listener_owner.get(L->get()); + + Vector3 rel_vector = listener->transform.xform_inv(source->transform.origin); + Vector3 source_rel_vector = source->transform.xform_inv(listener->transform.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),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 + + 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(Vector3(0,0,-1)),emission_deg,emission_attdb); + + Vector3 vpanning=rel_vector.normalized(); + + //listener stuff + + { + + // head cone + + 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(Vector3(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; + float reverb_send; + + /* 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,panning.z); + 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.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 SpatialSoundServerSW::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); +} + +SpatialSoundServerSW::SpatialSoundServerSW() { + +} diff --git a/servers/spatial_sound/spatial_sound_server_sw.h b/servers/spatial_sound/spatial_sound_server_sw.h new file mode 100644 index 0000000000..82b1b5fa8e --- /dev/null +++ b/servers/spatial_sound/spatial_sound_server_sw.h @@ -0,0 +1,249 @@ +/*************************************************/ +/* spatial_sound_server_sw.h */ +/*************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/*************************************************/ +/* Source code within this file is: */ +/* (c) 2007-2010 Juan Linietsky, Ariel Manzur */ +/* All Rights Reserved. */ +/*************************************************/ + +#ifndef SPATIAL_SOUND_SERVER_SW_H +#define SPATIAL_SOUND_SERVER_SW_H + +#include "servers/spatial_sound_server.h" +#include "octree.h" +#include "os/thread_safe.h" + + +class SpatialSoundServerSW : public SpatialSoundServer { + + OBJ_TYPE(SpatialSoundServerSW,SpatialSoundServer); + + _THREAD_SAFE_CLASS_ + + enum { + MAX_CULL_ROOMS=128, + INTERNAL_BUFFER_SIZE=4096, + INTERNAL_BUFFER_MAX_CHANNELS=4, + VOICE_IS_STREAM=-1 + + }; + + + struct InternalAudioStream : public AudioServer::AudioStream { + + ::SpatialSoundServerSW *owner; + virtual int get_channel_count() const; + virtual void set_mix_rate(int p_rate); //notify the stream of the mix rate + virtual bool mix(int32_t *p_buffer,int p_frames); + virtual void update(); + }; + + InternalAudioStream *internal_audio_stream; + RID internal_audio_stream_rid; + int32_t *internal_buffer; + int internal_buffer_channels; + + bool internal_buffer_mix(int32_t *p_buffer,int p_frames); + + struct Room; + + struct Space { + + RID default_room; + Set<RID> rooms; + Set<RID> sources; + Set<RID> listeners; + + Octree<Room> octree; + }; + + mutable RID_Owner<Space> space_owner; + + struct Room { + RID space; + Transform transform; + Transform inverse_transform; + BSP_Tree bounds; + RoomReverb reverb; + float params[ROOM_PARAM_MAX]; + bool override_other_sources; + OctreeElementID octree_id; + int level; + + Room(); + }; + + mutable RID_Owner<Room> room_owner; + + + + struct Source { + + struct Voice { + + RID voice_rid; + RID sample_rid; + bool active; + bool restart; + float pitch_scale; + float volume_scale; + int sample_mix_rate; + + + float last_volume; + float last_filter_gain; + float last_filter_cutoff; + Vector3 last_panning; + int last_mix_rate; + RoomReverb last_reverb_room; + float last_reverb_send; + + Voice(); + ~Voice(); + }; + + struct StreamData { + + + Vector3 panning; + RoomReverb reverb; + float reverb_send; + float volume; + float filter_gain; + float filter_cutoff; + + struct FilterState { + + float ha[2]; + float hb[2]; + } filter_state[4]; + + StreamData() { + + reverb_send=0; + reverb=ROOM_REVERB_HALL; + volume=1.0; + filter_gain=1; + filter_cutoff=5000; + + } + } stream_data; + + RID space; + Transform transform; + float params[SOURCE_PARAM_MAX]; + AudioServer::AudioStream *stream; + Vector<Voice> voices; + int last_voice; + + Source(); + }; + + mutable RID_Owner<Source> source_owner; + + struct Listener { + + RID space; + Transform transform; + float params[LISTENER_PARAM_MAX]; + + Listener(); + }; + + mutable RID_Owner<Listener> listener_owner; + + struct ActiveVoice { + + Source *source; + int voice; + bool operator<(const ActiveVoice& p_voice) const { return (voice==p_voice.voice)?(source<p_voice.source):(voice<p_voice.voice); } + ActiveVoice(Source *p_source=NULL,int p_voice=0) { source=p_source; voice=p_voice; } + }; + + Room *cull_rooms[MAX_CULL_ROOMS]; + + Set<Source*> streaming_sources; + Set<ActiveVoice> active_voices; + + void _clean_up_owner(RID_OwnerBase *p_owner, const char *p_area); + void _update_sources(); + +public: + + /* SPACE */ + virtual RID space_create(); + + /* ROOM */ + + virtual RID room_create(); + virtual void room_set_space(RID p_room,RID p_space); + virtual RID room_get_space(RID p_room) const; + + virtual void room_set_bounds(RID p_room, const BSP_Tree& p_bounds); + virtual BSP_Tree room_get_bounds(RID p_room) const; + virtual void room_set_transform(RID p_room, const Transform& p_transform); + virtual Transform room_get_transform(RID p_room) const; + + + virtual void room_set_param(RID p_room, RoomParam p_param, float p_value); + virtual float room_get_param(RID p_room, RoomParam p_param) const; + + virtual void room_set_level(RID p_room, int p_level); + virtual int room_get_level(RID p_room) const; + + virtual void room_set_reverb(RID p_room, RoomReverb p_reverb); + virtual RoomReverb room_get_reverb(RID p_room) const; + + //useful for underwater or rooms with very strange conditions + virtual void room_set_force_params_to_all_sources(RID p_room, bool p_force); + virtual bool room_is_forcing_params_to_all_sources(RID p_room) const; + + /* SOURCE */ + + virtual RID source_create(RID p_space); + + virtual void source_set_polyphony(RID p_source,int p_voice_count); + virtual int source_get_polyphony(RID p_source) const; + + virtual void source_set_transform(RID p_source, const Transform& p_transform); + virtual Transform source_get_transform(RID p_source) const; + + virtual void source_set_param(RID p_source, SourceParam p_param, float p_value); + virtual float source_get_param(RID p_source, SourceParam p_param) const; + + virtual void source_set_audio_stream(RID p_source, AudioServer::AudioStream *p_stream); //null to unset + virtual SourceVoiceID source_play_sample(RID p_source, RID p_sample, int p_mix_rate, int p_voice=SOURCE_NEXT_VOICE); + /* VOICES */ + virtual void source_voice_set_pitch_scale(RID p_source, SourceVoiceID p_voice, float p_pitch_scale); + virtual void source_voice_set_volume_scale_db(RID p_source, SourceVoiceID p_voice, float p_volume); + + virtual bool source_is_voice_active(RID p_source, SourceVoiceID p_voice) const; + virtual void source_stop_voice(RID p_source, SourceVoiceID p_voice); + + /* LISTENER */ + + virtual RID listener_create(); + virtual void listener_set_space(RID p_listener, RID p_space); + + virtual void listener_set_transform(RID p_listener, const Transform& p_transform); + virtual Transform listener_get_transform(RID p_listener) const; + + virtual void listener_set_param(RID p_listener, ListenerParam p_param, float p_value); + virtual float listener_get_param(RID p_listener, ListenerParam p_param) const; + + + /* MISC */ + + virtual void free(RID p_id); + + virtual void init(); + virtual void update(float p_delta); + virtual void finish(); + + SpatialSoundServerSW(); +}; + +#endif // SPATIAL_SOUND_SERVER_SW_H |