/*************************************************************************/
/*  spatial_sound_2d_server_sw.cpp                                       */
/*************************************************************************/
/*                       This file is part of:                           */
/*                           GODOT ENGINE                                */
/*                    http://www.godotengine.org                         */
/*************************************************************************/
/* Copyright (c) 2007-2016 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<Source*>::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<Point2>& 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<Point2> SpatialSound2DServerSW::room_get_bounds(RID p_room) const {
	Room *room = room_owner.get(p_room);
	ERR_FAIL_COND_V(!room,DVector<Point2>());

	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_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 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;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 SpatialSound2DServerSW::_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 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<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 SpatialSound2DServerSW::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;
			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<RID>::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<RID>::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() {

}
