Jelajahi Sumber

Merge branch 'master' of https://github.com/okamstudio/godot

Juan Linietsky 10 tahun lalu
induk
melakukan
3013a83f2f

+ 61 - 67
drivers/mpc/audio_stream_mpc.cpp

@@ -1,7 +1,7 @@
 #include "audio_stream_mpc.h"
 
 
-Error AudioStreamMPC::_open_file() {
+Error AudioStreamPlaybackMPC::_open_file() {
 
 	if (f) {
 		memdelete(f);
@@ -41,7 +41,7 @@ Error AudioStreamMPC::_open_file() {
 	return OK;
 }
 
-void AudioStreamMPC::_close_file() {
+void AudioStreamPlaybackMPC::_close_file() {
 
 	if (f) {
 		memdelete(f);
@@ -52,7 +52,7 @@ void AudioStreamMPC::_close_file() {
 	data_ofs=0;
 }
 
-int AudioStreamMPC::_read_file(void *p_dst,int p_bytes) {
+int AudioStreamPlaybackMPC::_read_file(void *p_dst,int p_bytes) {
 
 	if (f)
 		return f->get_buffer((uint8_t*)p_dst,p_bytes);
@@ -68,7 +68,7 @@ int AudioStreamMPC::_read_file(void *p_dst,int p_bytes) {
 	return p_bytes;
 }
 
-bool AudioStreamMPC::_seek_file(int p_pos){
+bool AudioStreamPlaybackMPC::_seek_file(int p_pos){
 
 	if (p_pos<0 || p_pos>streamlen)
 		return false;
@@ -83,7 +83,7 @@ bool AudioStreamMPC::_seek_file(int p_pos){
 	return true;
 
 }
-int AudioStreamMPC::_tell_file()  const{
+int AudioStreamPlaybackMPC::_tell_file()  const{
 
 	if (f)
 		return f->get_pos();
@@ -93,13 +93,13 @@ int AudioStreamMPC::_tell_file()  const{
 
 }
 
-int AudioStreamMPC::_sizeof_file() const{
+int AudioStreamPlaybackMPC::_sizeof_file() const{
 
 	//print_line("sizeof file, get: "+itos(streamlen));
 	return streamlen;
 }
 
-bool AudioStreamMPC::_canseek_file() const{
+bool AudioStreamPlaybackMPC::_canseek_file() const{
 
 	//print_line("canseek file, get true");
 	return true;
@@ -107,51 +107,46 @@ bool AudioStreamMPC::_canseek_file() const{
 
 /////////////////////
 
-mpc_int32_t AudioStreamMPC::_mpc_read(mpc_reader *p_reader,void *p_dst, mpc_int32_t p_bytes) {
+mpc_int32_t AudioStreamPlaybackMPC::_mpc_read(mpc_reader *p_reader,void *p_dst, mpc_int32_t p_bytes) {
 
-	AudioStreamMPC *smpc=(AudioStreamMPC *)p_reader->data;
+	AudioStreamPlaybackMPC *smpc=(AudioStreamPlaybackMPC *)p_reader->data;
 	return smpc->_read_file(p_dst,p_bytes);
 }
 
-mpc_bool_t AudioStreamMPC::_mpc_seek(mpc_reader *p_reader,mpc_int32_t p_offset) {
+mpc_bool_t AudioStreamPlaybackMPC::_mpc_seek(mpc_reader *p_reader,mpc_int32_t p_offset) {
 
-	AudioStreamMPC *smpc=(AudioStreamMPC *)p_reader->data;
+	AudioStreamPlaybackMPC *smpc=(AudioStreamPlaybackMPC *)p_reader->data;
 	return smpc->_seek_file(p_offset);
 
 }
-mpc_int32_t AudioStreamMPC::_mpc_tell(mpc_reader *p_reader) {
+mpc_int32_t AudioStreamPlaybackMPC::_mpc_tell(mpc_reader *p_reader) {
 
-	AudioStreamMPC *smpc=(AudioStreamMPC *)p_reader->data;
+	AudioStreamPlaybackMPC *smpc=(AudioStreamPlaybackMPC *)p_reader->data;
 	return smpc->_tell_file();
 
 }
-mpc_int32_t AudioStreamMPC::_mpc_get_size(mpc_reader *p_reader) {
+mpc_int32_t AudioStreamPlaybackMPC::_mpc_get_size(mpc_reader *p_reader) {
 
-	AudioStreamMPC *smpc=(AudioStreamMPC *)p_reader->data;
+	AudioStreamPlaybackMPC *smpc=(AudioStreamPlaybackMPC *)p_reader->data;
 	return smpc->_sizeof_file();
 
 
 }
-mpc_bool_t AudioStreamMPC::_mpc_canseek(mpc_reader *p_reader) {
+mpc_bool_t AudioStreamPlaybackMPC::_mpc_canseek(mpc_reader *p_reader) {
 
-	AudioStreamMPC *smpc=(AudioStreamMPC *)p_reader->data;
+	AudioStreamPlaybackMPC *smpc=(AudioStreamPlaybackMPC *)p_reader->data;
 	return smpc->_canseek_file();
 }
 
 
 
-bool AudioStreamMPC::_can_mix() const {
 
-	return /*active &&*/ !paused;
-}
-
-
-void AudioStreamMPC::update() {
+int AudioStreamPlaybackMPC::mix(int16_t* p_bufer,int p_frames) {
 
 	if (!active || paused)
-		return;
+		return 0;
 
-	int todo=get_todo();
+	int todo=p_frames;
 
 	while(todo>MPC_DECODER_BUFFER_LENGTH/si.channels) {
 
@@ -162,7 +157,7 @@ void AudioStreamMPC::update() {
 		mpc_status err = mpc_demux_decode(demux, &frame);
 		if (frame.bits!=-1) {
 
-			int16_t *dst_buff = get_write_buffer();
+			int16_t *dst_buff = p_bufer;
 
 #ifdef MPC_FIXED_POINT
 
@@ -185,21 +180,21 @@ void AudioStreamMPC::update() {
 #endif
 
 			int frames = frame.samples;
-			write(frames);
+			p_bufer+=si.channels*frames;
 			todo-=frames;
 		} else {
 
 			if (err != MPC_STATUS_OK) {
 
 				stop();
-				ERR_EXPLAIN("Error decoding MPC");
-				ERR_FAIL();
+				ERR_PRINT("Error decoding MPC");
+				break;
 			} else {
 
 				//finished
 				if (!loop) {
 					stop();
-					return;
+					break;
 				} else {
 
 
@@ -213,9 +208,11 @@ void AudioStreamMPC::update() {
 			}
 		}
 	}
+
+	return p_frames-todo;
 }
 
-Error AudioStreamMPC::_reload() {
+Error AudioStreamPlaybackMPC::_reload() {
 
 	ERR_FAIL_COND_V(demux!=NULL, ERR_FILE_ALREADY_IN_USE);
 
@@ -224,31 +221,40 @@ Error AudioStreamMPC::_reload() {
 
 	demux = mpc_demux_init(&reader);
 	ERR_FAIL_COND_V(!demux,ERR_CANT_CREATE);
-
 	mpc_demux_get_info(demux,  &si);
-	_setup(si.channels,si.sample_freq,MPC_DECODER_BUFFER_LENGTH*2/si.channels);
 
 	return OK;
 }
 
-void AudioStreamMPC::set_file(const String& p_file) {
+void AudioStreamPlaybackMPC::set_file(const String& p_file) {
 
 	file=p_file;
 
+	Error err = _open_file();
+	ERR_FAIL_COND(err!=OK);
+	demux = mpc_demux_init(&reader);
+	ERR_FAIL_COND(!demux);
+	mpc_demux_get_info(demux,  &si);
+	stream_min_size=MPC_DECODER_BUFFER_LENGTH*2/si.channels;
+	stream_rate=si.sample_freq;
+	stream_channels=si.channels;
+
+	mpc_demux_exit(demux);
+	demux=NULL;
+	_close_file();
+
 }
 
 
-String AudioStreamMPC::get_file() const {
+String AudioStreamPlaybackMPC::get_file() const {
 
 	return file;
 }
 
 
-void AudioStreamMPC::play() {
+void AudioStreamPlaybackMPC::play(float p_offset) {
 
 
-	_THREAD_SAFE_METHOD_
-
 	if (active)
 		stop();
 	active=false;
@@ -262,9 +268,9 @@ void AudioStreamMPC::play() {
 
 }
 
-void AudioStreamMPC::stop()  {
+void AudioStreamPlaybackMPC::stop()  {
+
 
-	_THREAD_SAFE_METHOD_
 	if (!active)
 		return;
 	if (demux) {
@@ -275,70 +281,58 @@ void AudioStreamMPC::stop()  {
 	active=false;
 
 }
-bool AudioStreamMPC::is_playing() const  {
+bool AudioStreamPlaybackMPC::is_playing() const  {
 
-	return active || (get_total() - get_todo() -1 > 0);
+	return active;
 }
 
-void AudioStreamMPC::set_paused(bool p_paused)  {
 
-	paused=p_paused;
-}
-bool AudioStreamMPC::is_paused(bool p_paused) const  {
-
-	return paused;
-}
-
-void AudioStreamMPC::set_loop(bool p_enable)  {
+void AudioStreamPlaybackMPC::set_loop(bool p_enable)  {
 
 	loop=p_enable;
 }
-bool AudioStreamMPC::has_loop() const  {
+bool AudioStreamPlaybackMPC::has_loop() const  {
 
 	return loop;
 }
 
-float AudioStreamMPC::get_length() const {
+float AudioStreamPlaybackMPC::get_length() const {
 
 	return 0;
 }
 
-String AudioStreamMPC::get_stream_name() const {
+String AudioStreamPlaybackMPC::get_stream_name() const {
 
 	return "";
 }
 
-int AudioStreamMPC::get_loop_count() const {
+int AudioStreamPlaybackMPC::get_loop_count() const {
 
 	return 0;
 }
 
-float AudioStreamMPC::get_pos() const {
+float AudioStreamPlaybackMPC::get_pos() const {
 
 	return 0;
 }
-void AudioStreamMPC::seek_pos(float p_time) {
+void AudioStreamPlaybackMPC::seek_pos(float p_time) {
 
 
 }
 
-AudioStream::UpdateMode AudioStreamMPC::get_update_mode() const {
-
-	return UPDATE_THREAD;
-}
 
-void AudioStreamMPC::_bind_methods() {
+void AudioStreamPlaybackMPC::_bind_methods() {
 
-	ObjectTypeDB::bind_method(_MD("set_file","name"),&AudioStreamMPC::set_file);
-	ObjectTypeDB::bind_method(_MD("get_file"),&AudioStreamMPC::get_file);
+	ObjectTypeDB::bind_method(_MD("set_file","name"),&AudioStreamPlaybackMPC::set_file);
+	ObjectTypeDB::bind_method(_MD("get_file"),&AudioStreamPlaybackMPC::get_file);
 
 	ADD_PROPERTYNZ( PropertyInfo(Variant::STRING,"file",PROPERTY_HINT_FILE,"mpc"), _SCS("set_file"), _SCS("get_file"));
 
 }
 
-AudioStreamMPC::AudioStreamMPC() {
+AudioStreamPlaybackMPC::AudioStreamPlaybackMPC() {
 
-	preload=true;
+	preload=false;
 	f=NULL;
 	streamlen=0;
 	data_ofs=0;
@@ -356,7 +350,7 @@ AudioStreamMPC::AudioStreamMPC() {
 
 }
 
-AudioStreamMPC::~AudioStreamMPC() {
+AudioStreamPlaybackMPC::~AudioStreamPlaybackMPC() {
 
 	stop();
 

+ 33 - 12
drivers/mpc/audio_stream_mpc.h

@@ -1,18 +1,17 @@
 #ifndef AUDIO_STREAM_MPC_H
 #define AUDIO_STREAM_MPC_H
 
-#include "scene/resources/audio_stream_resampled.h"
+#include "scene/resources/audio_stream.h"
 #include "os/file_access.h"
 #include "mpc/mpcdec.h"
 #include "os/thread_safe.h"
 #include "io/resource_loader.h"
 //#include "../libmpcdec/decoder.h"
 //#include "../libmpcdec/internal.h"
-class AudioStreamMPC : public AudioStreamResampled {
 
-	OBJ_TYPE( AudioStreamMPC, AudioStreamResampled );
+class AudioStreamPlaybackMPC : public AudioStreamPlayback {
 
-	_THREAD_SAFE_CLASS_
+	OBJ_TYPE( AudioStreamPlaybackMPC, AudioStreamPlayback );
 
 	bool preload;
 	FileAccess *f;
@@ -39,7 +38,9 @@ class AudioStreamMPC : public AudioStreamResampled {
 	static mpc_int32_t _mpc_get_size(mpc_reader *p_reader);
 	static mpc_bool_t _mpc_canseek(mpc_reader *p_reader);
 
-	virtual bool _can_mix() const ;
+	int stream_min_size;
+	int stream_rate;
+	int stream_channels;
 
 protected:
 	Error _open_file();
@@ -59,12 +60,10 @@ public:
 	void set_file(const String& p_file);
 	String get_file() const;
 
-	virtual void play();
+	virtual void play(float p_offset=0);
 	virtual void stop();
 	virtual bool is_playing() const;
 
-	virtual void set_paused(bool p_paused);
-	virtual bool is_paused(bool p_paused) const;
 
 	virtual void set_loop(bool p_enable);
 	virtual bool has_loop() const;
@@ -78,13 +77,35 @@ public:
 	virtual float get_pos() const;
 	virtual void seek_pos(float p_time);
 
-	virtual UpdateMode get_update_mode() const;
-	virtual void update();
+	virtual int get_channels() const { return stream_channels; }
+	virtual int get_mix_rate() const { return stream_rate; }
 
-	AudioStreamMPC();
-	~AudioStreamMPC();
+	virtual int get_minimum_buffer_size() const { return stream_min_size; }
+	virtual int mix(int16_t* p_bufer,int p_frames);
+
+	virtual void set_loop_restart_time(float p_time) {  }
+
+	AudioStreamPlaybackMPC();
+	~AudioStreamPlaybackMPC();
 };
 
+class AudioStreamMPC : public AudioStream {
+
+	OBJ_TYPE( AudioStreamMPC, AudioStream );
+
+	String file;
+public:
+
+	Ref<AudioStreamPlayback> instance_playback() {
+		Ref<AudioStreamPlaybackMPC> pb = memnew( AudioStreamPlaybackMPC );
+		pb->set_file(file);
+		return pb;
+	}
+
+	void set_file(const String& p_file) { file=p_file; }
+
+
+};
 
 class ResourceFormatLoaderAudioStreamMPC : public ResourceFormatLoader {
 public:

+ 88 - 86
drivers/speex/audio_stream_speex.cpp

@@ -15,14 +15,15 @@ static _FORCE_INLINE_ uint16_t le_short(uint16_t s)
 }
 
 
-void AudioStreamSpeex::update() {
+int AudioStreamPlaybackSpeex::mix(int16_t* p_buffer,int p_frames) {
+
+
 
-	_THREAD_SAFE_METHOD_;
 	//printf("update, loops %i, read ofs %i\n", (int)loops, read_ofs);
 	//printf("playing %i, paused %i\n", (int)playing, (int)paused);
 
 	if (!active || !playing || paused || !data.size())
-		return;
+		return 0;
 
 	/*
 	if (read_ofs >= data.size()) {
@@ -35,12 +36,13 @@ void AudioStreamSpeex::update() {
 	};
 	*/
 
-	int todo = get_todo();
+	int todo = p_frames;
 	if (todo < page_size) {
-		return;
+		return 0;
 	};
 
-	int eos = 0;
+	int eos = 0;	
+	bool reloaded=false;
 
 	while (todo > page_size) {
 
@@ -92,7 +94,7 @@ void AudioStreamSpeex::update() {
 				for (int j=0;j!=nframes;j++)
 				{
 
-					int16_t* out = get_write_buffer();
+					int16_t* out = p_buffer;
 
 					int ret;
 					/*Decode frame*/
@@ -120,7 +122,7 @@ void AudioStreamSpeex::update() {
 
 
 					/*Convert to short and save to output file*/
-					for (int i=0;i<frame_size*get_channel_count();i++) {
+					for (int i=0;i<frame_size*stream_channels;i++) {
 						out[i]=le_short(out[i]);
 					}
 
@@ -149,7 +151,7 @@ void AudioStreamSpeex::update() {
 						}
 
 
-						write(new_frame_size);
+						p_buffer+=new_frame_size*stream_channels;
 						todo-=new_frame_size;
 					}
 				}
@@ -175,6 +177,7 @@ void AudioStreamSpeex::update() {
 				if (loops) {					
 					reload();
 					++loop_count;
+					//break;
 				} else {
 					playing=false;
 					unload();
@@ -183,18 +186,22 @@ void AudioStreamSpeex::update() {
 			}
 		};
 	};
+
+	return p_frames-todo;
 };
 
 
-void AudioStreamSpeex::unload() {
+void AudioStreamPlaybackSpeex::unload() {
+
 
-	_THREAD_SAFE_METHOD_
 
 	if (!active) return;
 
 	speex_bits_destroy(&bits);
 	if (st)
 		speex_decoder_destroy(st);
+
+	ogg_sync_clear(&oy);
 	active = false;
 	//data.resize(0);
 	st = NULL;
@@ -204,7 +211,7 @@ void AudioStreamSpeex::unload() {
 	loop_count = 0;
 }
 
-void *AudioStreamSpeex::process_header(ogg_packet *op, int *frame_size, int *rate, int *nframes, int *channels, int *extra_headers) {
+void *AudioStreamPlaybackSpeex::process_header(ogg_packet *op, int *frame_size, int *rate, int *nframes, int *channels, int *extra_headers) {
 
 	void *st;
 	SpeexHeader *header;
@@ -276,9 +283,9 @@ void *AudioStreamSpeex::process_header(ogg_packet *op, int *frame_size, int *rat
 
 
 
-void AudioStreamSpeex::reload() {
+void AudioStreamPlaybackSpeex::reload() {
+
 
-	_THREAD_SAFE_METHOD_
 
 	if (active)
 		unload();
@@ -359,8 +366,10 @@ void AudioStreamSpeex::reload() {
 					};
 
 					page_size = nframes * frame_size;
+					stream_srate=rate;
+					stream_channels=channels;
+					stream_minbuff_size=page_size;
 
-					_setup(channels, rate,page_size);
 
 				} else if (packet_count==1)
 				{
@@ -374,23 +383,23 @@ void AudioStreamSpeex::reload() {
 
 	} while (packet_count <= extra_headers);
 
-	active = true;
+	active=true;
 
 }
 
-void AudioStreamSpeex::_bind_methods() {
+void AudioStreamPlaybackSpeex::_bind_methods() {
 
-	ObjectTypeDB::bind_method(_MD("set_file","file"),&AudioStreamSpeex::set_file);
-	ObjectTypeDB::bind_method(_MD("get_file"),&AudioStreamSpeex::get_file);
+	//ObjectTypeDB::bind_method(_MD("set_file","file"),&AudioStreamPlaybackSpeex::set_file);
+//	ObjectTypeDB::bind_method(_MD("get_file"),&AudioStreamPlaybackSpeex::get_file);
 
-	ObjectTypeDB::bind_method(_MD("_set_bundled"),&AudioStreamSpeex::_set_bundled);
-	ObjectTypeDB::bind_method(_MD("_get_bundled"),&AudioStreamSpeex::_get_bundled);
+	ObjectTypeDB::bind_method(_MD("_set_bundled"),&AudioStreamPlaybackSpeex::_set_bundled);
+	ObjectTypeDB::bind_method(_MD("_get_bundled"),&AudioStreamPlaybackSpeex::_get_bundled);
 
 	ADD_PROPERTY( PropertyInfo(Variant::DICTIONARY,"_bundled",PROPERTY_HINT_NONE,"",PROPERTY_USAGE_BUNDLE),_SCS("_set_bundled"),_SCS("_get_bundled"));
-	ADD_PROPERTY( PropertyInfo(Variant::STRING,"file",PROPERTY_HINT_FILE,"*.spx"),_SCS("set_file"),_SCS("get_file"));
+	//ADD_PROPERTY( PropertyInfo(Variant::STRING,"file",PROPERTY_HINT_FILE,"*.spx"),_SCS("set_file"),_SCS("get_file"));
 };
 
-void AudioStreamSpeex::_set_bundled(const Dictionary& dict) {
+void AudioStreamPlaybackSpeex::_set_bundled(const Dictionary& dict) {
 
 	ERR_FAIL_COND( !dict.has("filename"));
 	ERR_FAIL_COND( !dict.has("data"));
@@ -399,7 +408,7 @@ void AudioStreamSpeex::_set_bundled(const Dictionary& dict) {
 	data = dict["data"];
 };
 
-Dictionary AudioStreamSpeex::_get_bundled() const {
+Dictionary AudioStreamPlaybackSpeex::_get_bundled() const {
 
 	Dictionary d;
 	d["filename"] = filename;
@@ -408,43 +417,17 @@ Dictionary AudioStreamSpeex::_get_bundled() const {
 };
 
 
-String AudioStreamSpeex::get_file() const {
-
-	return filename;
-};
-
-void AudioStreamSpeex::set_file(const String& p_file){
-
-	if (filename == p_file)
-		return;
-
-	if (active) {
-		unload();
-	}
-
-	if (p_file == "") {
-		data.resize(0);
-		return;
-	};
-
-	Error err;
-	FileAccess* file = FileAccess::open(p_file, FileAccess::READ,&err);
-	if (err != OK) {
-		data.resize(0);
-	};
-	ERR_FAIL_COND(err != OK);
 
-	filename = p_file;
-	data.resize(file->get_len());
-	int read = file->get_buffer(&data[0], data.size());
-	memdelete(file);
+void AudioStreamPlaybackSpeex::set_data(const Vector<uint8_t>& p_data) {
 
+	data=p_data;
 	reload();
 }
 
-void AudioStreamSpeex::play() {
 
-	_THREAD_SAFE_METHOD_
+void AudioStreamPlaybackSpeex::play(float p_from_pos) {
+
+
 
 	reload();
 	if (!active)
@@ -452,82 +435,101 @@ void AudioStreamSpeex::play() {
 	playing = true;
 
 }
-void AudioStreamSpeex::stop(){
+void AudioStreamPlaybackSpeex::stop(){
+
 
-	_THREAD_SAFE_METHOD_
 	unload();
 	playing = false;
-	_clear();
-}
-bool AudioStreamSpeex::is_playing() const{
 
-	return _is_ready() && (playing || (get_total() - get_todo() -1 > 0));
 }
+bool AudioStreamPlaybackSpeex::is_playing() const{
 
-void AudioStreamSpeex::set_paused(bool p_paused){
-
-	playing = !p_paused;
-	paused = p_paused;
+	return playing;
 }
-bool AudioStreamSpeex::is_paused(bool p_paused) const{
 
-	return paused;
-}
 
-void AudioStreamSpeex::set_loop(bool p_enable){
+void AudioStreamPlaybackSpeex::set_loop(bool p_enable){
 
 	loops = p_enable;
 }
-bool AudioStreamSpeex::has_loop() const{
+bool AudioStreamPlaybackSpeex::has_loop() const{
 
 	return loops;
 }
 
-float AudioStreamSpeex::get_length() const{
+float AudioStreamPlaybackSpeex::get_length() const{
 
 	return 0;
 }
 
-String AudioStreamSpeex::get_stream_name() const{
+String AudioStreamPlaybackSpeex::get_stream_name() const{
 
 	return "";
 }
 
-int AudioStreamSpeex::get_loop_count() const{
+int AudioStreamPlaybackSpeex::get_loop_count() const{
 
 	return 0;
 }
 
-float AudioStreamSpeex::get_pos() const{
+float AudioStreamPlaybackSpeex::get_pos() const{
 
 	return 0;
 }
-void AudioStreamSpeex::seek_pos(float p_time){
+void AudioStreamPlaybackSpeex::seek_pos(float p_time){
 
 
 };
 
-bool AudioStreamSpeex::_can_mix() const {
 
-	//return playing;
-	return data.size() != 0;
-};
 
+AudioStreamPlaybackSpeex::AudioStreamPlaybackSpeex() {
 
-AudioStream::UpdateMode AudioStreamSpeex::get_update_mode() const {
+	active=false;
+	st = NULL;
+	stream_channels=1;
+	stream_srate=1;
+	stream_minbuff_size=1;
 
-	return UPDATE_THREAD;
 }
 
-AudioStreamSpeex::AudioStreamSpeex() {
+AudioStreamPlaybackSpeex::~AudioStreamPlaybackSpeex() {
 
-	active=false;
-	st = NULL;
+	unload();
 }
 
-AudioStreamSpeex::~AudioStreamSpeex() {
 
-	unload();
+
+
+
+////////////////////////////////////////
+
+
+
+void AudioStreamSpeex::set_file(const String& p_file) {
+
+	if (this->file == p_file)
+		return;
+
+	this->file=p_file;
+
+	if (p_file == "") {
+		data.resize(0);
+		return;
+	};
+
+	Error err;
+	FileAccess* file = FileAccess::open(p_file, FileAccess::READ,&err);
+	if (err != OK) {
+		data.resize(0);
+	};
+	ERR_FAIL_COND(err != OK);
+
+	this->file = p_file;
+	data.resize(file->get_len());
+	int read = file->get_buffer(&data[0], data.size());
+	memdelete(file);
+
 }
 
 RES ResourceFormatLoaderAudioStreamSpeex::load(const String &p_path, const String& p_original_path, Error *r_error) {

+ 40 - 15
drivers/speex/audio_stream_speex.h

@@ -1,7 +1,7 @@
 #ifndef AUDIO_STREAM_SPEEX_H
 #define AUDIO_STREAM_SPEEX_H
 
-#include "scene/resources/audio_stream_resampled.h"
+#include "scene/resources/audio_stream.h"
 #include "speex/speex.h"
 #include "os/file_access.h"
 #include "io/resource_loader.h"
@@ -14,10 +14,10 @@
 
 #include <ogg/ogg.h>
 
-class AudioStreamSpeex : public AudioStreamResampled {
+class AudioStreamPlaybackSpeex : public AudioStreamPlayback {
+
+	OBJ_TYPE(AudioStreamPlaybackSpeex, AudioStreamPlayback);
 
-	OBJ_TYPE(AudioStreamSpeex, AudioStreamResampled);
-	_THREAD_SAFE_CLASS_
 
 	void *st;
 	SpeexBits bits;
@@ -45,6 +45,9 @@ class AudioStreamSpeex : public AudioStreamResampled {
 
 	ogg_int64_t page_granule, last_granule;
 	int skip_samples, page_nb_packets;
+	int stream_channels;
+	int stream_srate;
+	int stream_minbuff_size;
 
 	void* process_header(ogg_packet *op, int *frame_size, int *rate, int *nframes, int *channels, int *extra_headers);
 
@@ -52,7 +55,7 @@ class AudioStreamSpeex : public AudioStreamResampled {
 
 protected:
 
-	virtual bool _can_mix() const;
+	//virtual bool _can_mix() const;
 
 	Dictionary _get_bundled() const;
 	void _set_bundled(const Dictionary& dict);
@@ -60,16 +63,12 @@ protected:
 public:
 
 
-	void set_file(const String& p_file);
-	String get_file() const;
+	void set_data(const Vector<uint8_t>& p_data);
 
-	virtual void play();
+	virtual void play(float p_from_pos=0);
 	virtual void stop();
 	virtual bool is_playing() const;
 
-	virtual void set_paused(bool p_paused);
-	virtual bool is_paused(bool p_paused) const;
-
 	virtual void set_loop(bool p_enable);
 	virtual bool has_loop() const;
 
@@ -82,13 +81,39 @@ public:
 	virtual float get_pos() const;
 	virtual void seek_pos(float p_time);
 
-	virtual UpdateMode get_update_mode() const;
-	virtual void update();
+	virtual int get_channels() const { return stream_channels; }
+	virtual int get_mix_rate() const { return stream_srate; }
+
+	virtual int get_minimum_buffer_size() const { return stream_minbuff_size; }
+	virtual int mix(int16_t* p_bufer,int p_frames);
+
+	virtual void set_loop_restart_time(float p_time) {  } //no loop restart, ignore
+
+	AudioStreamPlaybackSpeex();
+	~AudioStreamPlaybackSpeex();
+};
+
+
+
+class AudioStreamSpeex : public AudioStream {
+
+	OBJ_TYPE(AudioStreamSpeex,AudioStream);
+
+	Vector<uint8_t> data;
+	String file;
+public:
+
+	Ref<AudioStreamPlayback> instance_playback() {
+		Ref<AudioStreamPlaybackSpeex> pb = memnew( AudioStreamPlaybackSpeex );
+		pb->set_data(data);
+		return pb;
+	}
+
+	void set_file(const String& p_file);
 
-	AudioStreamSpeex();
-	~AudioStreamSpeex();
 };
 
+
 class ResourceFormatLoaderAudioStreamSpeex : public ResourceFormatLoader {
 public:
 	virtual RES load(const String &p_path,const String& p_original_path="",Error *r_error=NULL);

+ 2 - 1
drivers/unix/os_unix.cpp

@@ -226,7 +226,8 @@ uint64_t OS_Unix::get_unix_time() const {
 uint64_t OS_Unix::get_system_time_msec() const {
 	struct timeval tv_now;
 	gettimeofday(&tv_now, NULL);
-	localtime(&tv_now.tv_usec);
+	//localtime(&tv_now.tv_usec);
+	localtime((const long *)&tv_now.tv_usec);
 	uint64_t msec = tv_now.tv_usec/1000;
 	return msec;
 }

+ 113 - 93
drivers/vorbis/audio_stream_ogg_vorbis.cpp

@@ -30,7 +30,7 @@
 
 
 
-size_t AudioStreamOGGVorbis::_ov_read_func(void *p_dst,size_t p_data, size_t p_count, void *_f) {
+size_t AudioStreamPlaybackOGGVorbis::_ov_read_func(void *p_dst,size_t p_data, size_t p_count, void *_f) {
 
 	//printf("read to %p, %i bytes, %i nmemb, %p\n",p_dst,p_data,p_count,_f);
 	FileAccess *fa=(FileAccess*)_f;
@@ -46,7 +46,7 @@ size_t AudioStreamOGGVorbis::_ov_read_func(void *p_dst,size_t p_data, size_t p_c
 	return read;
 }
 
-int AudioStreamOGGVorbis::_ov_seek_func(void *_f,ogg_int64_t offs, int whence) {
+int AudioStreamPlaybackOGGVorbis::_ov_seek_func(void *_f,ogg_int64_t offs, int whence) {
 
 	//printf("seek to %p, offs %i, whence %i\n",_f,(int)offs,whence);
 
@@ -76,7 +76,7 @@ int AudioStreamOGGVorbis::_ov_seek_func(void *_f,ogg_int64_t offs, int whence) {
 #endif
 
 }
-int AudioStreamOGGVorbis::_ov_close_func(void *_f) {
+int AudioStreamPlaybackOGGVorbis::_ov_close_func(void *_f) {
 
 //	printf("close %p\n",_f);
 	if (!_f)
@@ -86,7 +86,7 @@ int AudioStreamOGGVorbis::_ov_close_func(void *_f) {
 		fa->close();
 	return 0;
 }
-long AudioStreamOGGVorbis::_ov_tell_func(void *_f) {
+long AudioStreamPlaybackOGGVorbis::_ov_tell_func(void *_f) {
 
 	//printf("close %p\n",_f);
 
@@ -95,38 +95,32 @@ long AudioStreamOGGVorbis::_ov_tell_func(void *_f) {
 }
 
 
-bool AudioStreamOGGVorbis::_can_mix() const {
 
-	return /*playing &&*/ !paused;
-}
-
-
-void AudioStreamOGGVorbis::update() {
+int AudioStreamPlaybackOGGVorbis::mix(int16_t* p_bufer,int p_frames) {
 
-	_THREAD_SAFE_METHOD_
-
-	if (!playing && !setting_up)
-		return;
+	if (!playing)
+		return 0;
 
+	int total=p_frames;
 	while (true) {
 
-		int todo = get_todo();
+		int todo = p_frames;
 
-		if (todo==0 || todo<MIN_MIX)
+		if (todo==0 || todo<MIN_MIX) {
 			break;
+		}
 
 		//printf("to mix %i - mix me %i bytes\n",to_mix,to_mix*stream_channels*sizeof(int16_t));
 
 		#ifdef BIG_ENDIAN_ENABLED
-		long ret=ov_read(&vf,(char*)get_write_buffer(),todo*stream_channels*sizeof(int16_t), 1, 2, 1, &current_section);
+		long ret=ov_read(&vf,(char*)p_bufer,todo*stream_channels*sizeof(int16_t), 1, 2, 1, &current_section);
 		#else
-		long ret=ov_read(&vf,(char*)get_write_buffer(),todo*stream_channels*sizeof(int16_t), 0, 2, 1, &current_section);
+		long ret=ov_read(&vf,(char*)p_bufer,todo*stream_channels*sizeof(int16_t), 0, 2, 1, &current_section);
 		#endif
+
 		if (ret<0) {
 
 			playing = false;
-			setting_up=false;
-
 			ERR_EXPLAIN("Error reading OGG Vorbis File: "+file);
 			ERR_BREAK(ret<0);
 		} else if (ret==0) { // end of song, reload?
@@ -138,9 +132,8 @@ void AudioStreamOGGVorbis::update() {
 			if (!has_loop()) {
 
 				playing=false;
-				setting_up=false;
 				repeats=1;
-				return;
+				break;
 			}
 
 			f=FileAccess::open(file,FileAccess::READ);
@@ -148,11 +141,22 @@ void AudioStreamOGGVorbis::update() {
 			int errv = ov_open_callbacks(f,&vf,NULL,0,_ov_callbacks);
 			if (errv!=0) {
 				playing=false;
-				setting_up=false;
-				return; // :(
+				break;; // :(
 			}
 
-			frames_mixed=0;
+			if (loop_restart_time) {
+				bool ok = ov_time_seek(&vf,loop_restart_time)==0;
+				if (!ok) {
+					playing=false;
+					//ERR_EXPLAIN("loop restart time rejected");
+					ERR_PRINT("loop restart time rejected")
+				}
+
+				frames_mixed=stream_srate*loop_restart_time;
+			} else {
+
+				frames_mixed=0;
+			}
 			repeats++;
 			continue;
 
@@ -162,16 +166,19 @@ void AudioStreamOGGVorbis::update() {
 		ret/=sizeof(int16_t);
 
 		frames_mixed+=ret;
-		write(ret);
+
+		p_bufer+=ret*stream_channels;
+		p_frames-=ret;
+
 	}
 
-}
+	return total-p_frames;
 
+}
 
 
-void AudioStreamOGGVorbis::play() {
 
-	_THREAD_SAFE_METHOD_
+void AudioStreamPlaybackOGGVorbis::play(float p_from) {
 
 	if (playing)
 		stop();
@@ -179,56 +186,46 @@ void AudioStreamOGGVorbis::play() {
 	if (_load_stream()!=OK)
 		return;
 
+
 	frames_mixed=0;
-	playing=false;
-	setting_up=true;
-	update();
-	if (!setting_up)
-		return;
-	setting_up=false;
 	playing=true;
+	if (p_from>0) {
+		seek_pos(p_from);
+	}
 }
 
-void AudioStreamOGGVorbis::_close_file() {
+void AudioStreamPlaybackOGGVorbis::_close_file() {
 
 	if (f) {
+
 		memdelete(f);
 		f=NULL;
 	}
 }
 
-void AudioStreamOGGVorbis::stop() {
-
-	_THREAD_SAFE_METHOD_
+bool AudioStreamPlaybackOGGVorbis::is_playing() const {
+	return playing;
+}
+void AudioStreamPlaybackOGGVorbis::stop() {
 
 	_clear_stream();
 	playing=false;
-	_clear();
-}
-
-AudioStreamOGGVorbis::UpdateMode AudioStreamOGGVorbis::get_update_mode() const {
-
-	return UPDATE_THREAD;
+	//_clear();
 }
 
 
-bool AudioStreamOGGVorbis::is_playing() const {
 
+float AudioStreamPlaybackOGGVorbis::get_pos() const {
 
-	return playing || (get_total() - get_todo() -1 > 0);
-}
-
-float AudioStreamOGGVorbis::get_pos() const {
-
-	int32_t frames = int32_t(frames_mixed) - (int32_t(get_total()) - get_todo());
+	int32_t frames = int32_t(frames_mixed);
 	if (frames < 0)
 		frames=0;
 	return double(frames) / stream_srate;
 }
 
-void AudioStreamOGGVorbis::seek_pos(float p_time) {
+void AudioStreamPlaybackOGGVorbis::seek_pos(float p_time) {
+
 
-	_THREAD_SAFE_METHOD_
 
 	if (!playing)
 		return;
@@ -237,85 +234,107 @@ void AudioStreamOGGVorbis::seek_pos(float p_time) {
 	frames_mixed=stream_srate*p_time;
 }
 
-String AudioStreamOGGVorbis::get_stream_name() const  {
+String AudioStreamPlaybackOGGVorbis::get_stream_name() const  {
 
 	return "";
 }
 
-void AudioStreamOGGVorbis::set_loop(bool p_enable) {
+void AudioStreamPlaybackOGGVorbis::set_loop(bool p_enable) {
 
 	loops=p_enable;
 }
 
-bool AudioStreamOGGVorbis::has_loop() const {
+bool AudioStreamPlaybackOGGVorbis::has_loop() const {
 
 	return loops;
 }
 
-int AudioStreamOGGVorbis::get_loop_count() const {
+int AudioStreamPlaybackOGGVorbis::get_loop_count() const {
 	return repeats;
 }
 
 
-void AudioStreamOGGVorbis::set_file(const String& p_file) {
+Error AudioStreamPlaybackOGGVorbis::set_file(const String& p_file) {
 
 	file=p_file;
-}
-
-Error AudioStreamOGGVorbis::_load_stream()  {
-
-	_clear_stream();
-	if (file=="")
-		return ERR_INVALID_DATA;
-
+	stream_valid=false;
 	Error err;
 	f=FileAccess::open(file,FileAccess::READ,&err);
 
-
 	if (err) {
 		ERR_FAIL_COND_V( err, err );
 	}
 
 	int errv = ov_open_callbacks(f,&vf,NULL,0,_ov_callbacks);
+	switch(errv) {
 
-
+		case OV_EREAD: { // - A read from media returned an error.
+			memdelete(f); f=NULL;
+			ERR_FAIL_V( ERR_FILE_CANT_READ );
+		} break;
+		case OV_EVERSION:  // - Vorbis version mismatch.
+		case OV_ENOTVORBIS: { // - Bitstream is not Vorbis data.
+			memdelete(f); f=NULL;
+			ERR_FAIL_V( ERR_FILE_UNRECOGNIZED );
+		} break;
+		case OV_EBADHEADER: { // - Invalid Vorbis bitstream header.
+			memdelete(f); f=NULL;
+			ERR_FAIL_V( ERR_FILE_CORRUPT );
+		} break;
+		case OV_EFAULT: { // - Internal logic fault; indicates a bug or heap/stack corruption.
+			memdelete(f); f=NULL;
+			ERR_FAIL_V( ERR_BUG );
+		} break;
+	}
 	const vorbis_info *vinfo=ov_info(&vf,-1);
 	stream_channels=vinfo->channels;
 	stream_srate=vinfo->rate;
-	Error serr = _setup(stream_channels,stream_srate);
+	ogg_int64_t len = ov_time_total(&vf,-1);
+	length=len/1000.0;
+	ov_clear(&vf);
+	memdelete(f);
+	f=NULL;
+	stream_valid=true;
+
+
+	return OK;
+}
+
+Error AudioStreamPlaybackOGGVorbis::_load_stream()  {
+
+	ERR_FAIL_COND_V(!stream_valid,ERR_UNCONFIGURED);
 
-	if (serr) {
-		_close_file();
-		ERR_FAIL_V( ERR_INVALID_DATA );
+	_clear_stream();
+	if (file=="")
+		return ERR_INVALID_DATA;
+
+	Error err;
+	f=FileAccess::open(file,FileAccess::READ,&err);
+	if (err) {
+		ERR_FAIL_COND_V( err, err );
 	}
 
+	int errv = ov_open_callbacks(f,&vf,NULL,0,_ov_callbacks);
 	switch(errv) {
 
 		case OV_EREAD: { // - A read from media returned an error.
-			_close_file();
+			memdelete(f); f=NULL;
 			ERR_FAIL_V( ERR_FILE_CANT_READ );
 		} break;
 		case OV_EVERSION:  // - Vorbis version mismatch.
 		case OV_ENOTVORBIS: { // - Bitstream is not Vorbis data.
-			_close_file();
+			memdelete(f); f=NULL;
 			ERR_FAIL_V( ERR_FILE_UNRECOGNIZED );
 		} break;
 		case OV_EBADHEADER: { // - Invalid Vorbis bitstream header.
-			_close_file();
+			memdelete(f); f=NULL;
 			ERR_FAIL_V( ERR_FILE_CORRUPT );
 		} break;
 		case OV_EFAULT: { // - Internal logic fault; indicates a bug or heap/stack corruption.
-
-			_close_file();
+			memdelete(f); f=NULL;
 			ERR_FAIL_V( ERR_BUG );
 		} break;
 	}
-
-
-	ogg_int64_t len = ov_time_total(&vf,-1);
-
-	length=len/1000.0;
-
 	repeats=0;
 	stream_loaded=true;
 
@@ -324,16 +343,16 @@ Error AudioStreamOGGVorbis::_load_stream()  {
 }
 
 
-float AudioStreamOGGVorbis::get_length() const {
+float AudioStreamPlaybackOGGVorbis::get_length() const {
 
 	if (!stream_loaded) {
-		if (const_cast<AudioStreamOGGVorbis*>(this)->_load_stream()!=OK)
+		if (const_cast<AudioStreamPlaybackOGGVorbis*>(this)->_load_stream()!=OK)
 			return 0;
 	}
 	return length;
 }
 
-void AudioStreamOGGVorbis::_clear_stream() {
+void AudioStreamPlaybackOGGVorbis::_clear_stream() {
 
 	if (!stream_loaded)
 		return;
@@ -346,18 +365,18 @@ void AudioStreamOGGVorbis::_clear_stream() {
 	playing=false;
 }
 
-void AudioStreamOGGVorbis::set_paused(bool p_paused) {
+void AudioStreamPlaybackOGGVorbis::set_paused(bool p_paused) {
 
 	paused=p_paused;
 }
 
-bool AudioStreamOGGVorbis::is_paused(bool p_paused) const {
+bool AudioStreamPlaybackOGGVorbis::is_paused(bool p_paused) const {
 
 	return paused;
 }
 
 
-AudioStreamOGGVorbis::AudioStreamOGGVorbis() {
+AudioStreamPlaybackOGGVorbis::AudioStreamPlaybackOGGVorbis() {
 
 	loops=false;
 	playing=false;
@@ -367,17 +386,18 @@ AudioStreamOGGVorbis::AudioStreamOGGVorbis() {
 	_ov_callbacks.tell_func=_ov_tell_func;
 	f = NULL;
 	stream_loaded=false;
-	repeats=0;
-	setting_up=false;
+	stream_valid=false;
+	repeats=0;	
 	paused=true;
 	stream_channels=0;
 	stream_srate=0;
 	current_section=0;
 	length=0;
+	loop_restart_time=0;
 }
 
 
-AudioStreamOGGVorbis::~AudioStreamOGGVorbis() {
+AudioStreamPlaybackOGGVorbis::~AudioStreamPlaybackOGGVorbis() {
 
 	_clear_stream();
 

+ 36 - 15
drivers/vorbis/audio_stream_ogg_vorbis.h

@@ -29,17 +29,16 @@
 #ifndef AUDIO_STREAM_OGG_VORBIS_H
 #define AUDIO_STREAM_OGG_VORBIS_H
 
-#include "scene/resources/audio_stream_resampled.h"
+#include "scene/resources/audio_stream.h"
 #include "vorbis/vorbisfile.h"
 #include "os/file_access.h"
 #include "io/resource_loader.h"
 #include "os/thread_safe.h"
 
-class AudioStreamOGGVorbis : public AudioStreamResampled {
 
-	OBJ_TYPE(AudioStreamOGGVorbis,AudioStreamResampled);
-	_THREAD_SAFE_CLASS_
+class AudioStreamPlaybackOGGVorbis : public AudioStreamPlayback {
 
+	OBJ_TYPE(AudioStreamPlaybackOGGVorbis,AudioStreamPlayback);
 
 	enum {
 		MIN_MIX=1024
@@ -54,9 +53,6 @@ class AudioStreamOGGVorbis : public AudioStreamResampled {
 	static int _ov_close_func(void *_f);
 	static long _ov_tell_func(void *_f);
 
-
-	virtual bool _can_mix() const;
-
 	String file;
 	int64_t frames_mixed;
 
@@ -67,7 +63,7 @@ class AudioStreamOGGVorbis : public AudioStreamResampled {
 	int stream_srate;
 	int current_section;
 
-	volatile bool setting_up;
+
 	bool paused;
 	bool loops;
 	int repeats;
@@ -76,17 +72,21 @@ class AudioStreamOGGVorbis : public AudioStreamResampled {
 	void _clear_stream();
 	void _close_file();
 
+	bool stream_valid;
+	float loop_restart_time;
 
-public:
 
+public:
 
-	void set_file(const String& p_file);
 
+	Error set_file(const String& p_file);
 
-	virtual void play();
+	virtual void play(float p_from=0);
 	virtual void stop();
 	virtual bool is_playing() const;
 
+	virtual void set_loop_restart_time(float p_time) { loop_restart_time=0; }
+
 	virtual void set_paused(bool p_paused);
 	virtual bool is_paused(bool p_paused) const;
 
@@ -102,11 +102,32 @@ public:
 	virtual float get_pos() const;
 	virtual void seek_pos(float p_time);
 
-	virtual UpdateMode get_update_mode() const;
-	virtual void update();
+	virtual int get_channels() const { return stream_channels; }
+	virtual int get_mix_rate() const { return stream_srate; }
+
+	virtual int get_minimum_buffer_size() const { return 0; }
+	virtual int mix(int16_t* p_bufer,int p_frames);
+
+	AudioStreamPlaybackOGGVorbis();
+	~AudioStreamPlaybackOGGVorbis();
+};
+
+
+class AudioStreamOGGVorbis : public AudioStream {
+
+	OBJ_TYPE(AudioStreamOGGVorbis,AudioStream);
+
+	String file;
+public:
+
+	Ref<AudioStreamPlayback> instance_playback() {
+		Ref<AudioStreamPlaybackOGGVorbis> pb = memnew( AudioStreamPlaybackOGGVorbis );
+		pb->set_file(file);
+		return pb;
+	}
+
+	void set_file(const String& p_file) { file=p_file; }
 
-	AudioStreamOGGVorbis();
-	~AudioStreamOGGVorbis();
 };
 
 class ResourceFormatLoaderAudioStreamOGGVorbis : public ResourceFormatLoader {

+ 0 - 1
main/main.cpp

@@ -339,7 +339,6 @@ Error Main::setup(const char *execpath,int argc, char *argv[],bool p_second_phas
 
 				init_custom_pos=Point2(x,y);
 				init_use_custom_pos=true;
-				force_res=true;
 
 				N=I->next()->next();
 			} else {

+ 1 - 1
platform/iphone/SCsub

@@ -34,5 +34,5 @@ obj = env_ios.Object('godot_iphone.cpp')
 
 prog = None
 prog = env_ios.Program('#bin/godot', [obj] + iphone_lib)
-action = "dsymutil "+File(prog)[0].path+" -o " + File(prog)[0].path + ".dSYM"
+action = "$IPHONEPATH/usr/bin/dsymutil "+File(prog)[0].path+" -o " + File(prog)[0].path + ".dSYM"
 env.AddPostAction(prog, action)

+ 7 - 4
platform/iphone/detect.py

@@ -11,7 +11,8 @@ def get_name():
 def can_build():
 
 	import sys
-	if sys.platform == 'darwin':
+	import os
+	if sys.platform == 'darwin' or os.environ.has_key("OSXCROSS_IOS"):
 		return True
 
 	return False
@@ -28,6 +29,7 @@ def get_opts():
 		('ios_gles22_override', 'Force GLES2.0 on iOS', 'yes'),
 		('ios_appirater', 'Enable Appirater', 'no'),
 		('ios_exceptions', 'Use exceptions when compiling on playbook', 'yes'),
+		('ios_triple', 'Triple for ios toolchain', ''),
 	]
 
 def get_flags():
@@ -48,9 +50,10 @@ def configure(env):
 
 #	env['CC'] = '$IPHONEPATH/Developer/usr/bin/gcc'
 #	env['CXX'] = '$IPHONEPATH/Developer/usr/bin/g++'
-	env['CC'] = '$IPHONEPATH/usr/bin/clang'
-	env['CXX'] = '$IPHONEPATH/usr/bin/clang++'
-	env['AR'] = 'ar'
+	env['CC'] = '$IPHONEPATH/usr/bin/${ios_triple}clang'
+	env['CXX'] = '$IPHONEPATH/usr/bin/${ios_triple}clang++'
+	env['AR'] = '$IPHONEPATH/usr/bin/${ios_triple}ar'
+	env['RANLIB'] = '$IPHONEPATH/usr/bin/${ios_triple}ranlib'
 
 	import string
 	if (env["bits"]=="64"):

+ 250 - 38
scene/3d/spatial_stream_player.cpp

@@ -30,21 +30,79 @@
 
 
 
-void SpatialStreamPlayer::_notification(int p_what) {
+int SpatialStreamPlayer::InternalStream::get_channel_count() const {
 
-	switch(p_what) {
+	return player->sp_get_channel_count();
+}
+void SpatialStreamPlayer::InternalStream::set_mix_rate(int p_rate){
 
-		case NOTIFICATION_ENTER_WORLD: {
+	return player->sp_set_mix_rate(p_rate);
+}
+bool SpatialStreamPlayer::InternalStream::mix(int32_t *p_buffer,int p_frames){
+
+	return player->sp_mix(p_buffer,p_frames);
+}
+void SpatialStreamPlayer::InternalStream::update(){
+
+	player->sp_update();
+}
+
+
+int SpatialStreamPlayer::sp_get_channel_count() const {
+
+	return playback->get_channels();
+}
+
+void SpatialStreamPlayer::sp_set_mix_rate(int p_rate){
+
+	server_mix_rate=p_rate;
+}
+
+bool SpatialStreamPlayer::sp_mix(int32_t *p_buffer,int p_frames) {
+
+	if (resampler.is_ready()) {
+		return resampler.mix(p_buffer,p_frames);
+	}
+
+	return false;
+}
+
+void SpatialStreamPlayer::sp_update() {
+
+	_THREAD_SAFE_METHOD_
+	if (!paused && resampler.is_ready() && playback.is_valid()) {
+
+		if (!playback->is_playing()) {
+			//stream depleted data, but there's still audio in the ringbuffer
+			//check that all this audio has been flushed before stopping the stream
+			int to_mix = resampler.get_total() - resampler.get_todo();
+			if (to_mix==0) {
+				stop();
+				return;
+			}
+
+			return;
+		}
+
+		int todo =resampler.get_todo();
+		int wrote = playback->mix(resampler.get_write_buffer(),todo);
+		resampler.write(wrote);
+	}
+}
 
-//			set_idle_process(false); //don't annoy
-		} break;
-		case NOTIFICATION_PROCESS: {
 
-//			if (!stream.is_null())
-//				stream->update();
 
+void SpatialStreamPlayer::_notification(int p_what) {
+
+	switch(p_what) {
+
+		case NOTIFICATION_ENTER_TREE: {
+
+			//set_idle_process(false); //don't annoy
+			if (stream.is_valid() && autoplay && !get_tree()->is_editor_hint())
+				play();
 		} break;
-		case NOTIFICATION_EXIT_WORLD: {
+		case NOTIFICATION_EXIT_TREE: {
 
 			stop(); //wathever it may be doing, stop
 		} break;
@@ -58,12 +116,20 @@ void SpatialStreamPlayer::set_stream(const Ref<AudioStream> &p_stream) {
 	stop();
 
 	stream=p_stream;
-	if (!stream.is_null()) {
 
-		stream->set_loop(loops);
+	if (!stream.is_null()) {
+		playback=stream->instance_playback();
+		playback->set_loop(loops);
+		playback->set_loop_restart_time(loop_point);
+		AudioServer::get_singleton()->lock();
+		resampler.setup(playback->get_channels(),playback->get_mix_rate(),server_mix_rate,buffering_ms,playback->get_minimum_buffer_size());
+		AudioServer::get_singleton()->unlock();
+	} else {
+		AudioServer::get_singleton()->lock();
+		resampler.clear();
+		playback.unref();
+		AudioServer::get_singleton()->unlock();
 	}
-
-
 }
 
 Ref<AudioStream> SpatialStreamPlayer::get_stream() const {
@@ -72,18 +138,25 @@ Ref<AudioStream> SpatialStreamPlayer::get_stream() const {
 }
 
 
-void SpatialStreamPlayer::play() {
+void SpatialStreamPlayer::play(float p_from_offset) {
 
-	if (!is_inside_tree())
+	ERR_FAIL_COND(!is_inside_tree());
+	if (playback.is_null())
 		return;
-	if (stream.is_null())
-		return;
-	if (stream->is_playing())
+	if (playback->is_playing())
 		stop();
-	stream->play();
-	SpatialSoundServer::get_singleton()->source_set_audio_stream(get_source_rid(),stream->get_audio_stream());
-	//if (stream->get_update_mode()!=AudioStream::UPDATE_NONE)
-	//	set_idle_process(true);
+
+	_THREAD_SAFE_METHOD_
+	playback->play(p_from_offset);
+	//feed the ringbuffer as long as no update callback is going on
+	sp_update();
+
+	SpatialSoundServer::get_singleton()->source_set_audio_stream(get_source_rid(),&internal_stream);
+
+//	AudioServer::get_singleton()->stream_set_active(stream_rid,true);
+//	AudioServer::get_singleton()->stream_set_volume_scale(stream_rid,volume);
+//	if (stream->get_update_mode()!=AudioStream::UPDATE_NONE)
+//		set_idle_process(true);
 
 }
 
@@ -91,28 +164,30 @@ void SpatialStreamPlayer::stop() {
 
 	if (!is_inside_tree())
 		return;
-	if (stream.is_null())
+	if (playback.is_null())
 		return;
 
+	_THREAD_SAFE_METHOD_
+	//AudioServer::get_singleton()->stream_set_active(stream_rid,false);
 	SpatialSoundServer::get_singleton()->source_set_audio_stream(get_source_rid(),NULL);
-	stream->stop();
+	playback->stop();
 	//set_idle_process(false);
 }
 
 bool SpatialStreamPlayer::is_playing() const {
 
-	if (stream.is_null())
+	if (playback.is_null())
 		return false;
 
-	return stream->is_playing();
+	return playback->is_playing();
 }
 
 void SpatialStreamPlayer::set_loop(bool p_enable) {
 
 	loops=p_enable;
-	if (stream.is_null())
+	if (playback.is_null())
 		return;
-	stream->set_loop(loops);
+	playback->set_loop(loops);
 
 }
 bool SpatialStreamPlayer::has_loop() const {
@@ -120,6 +195,46 @@ bool SpatialStreamPlayer::has_loop() const {
 	return loops;
 }
 
+void SpatialStreamPlayer::set_volume(float p_vol) {
+
+	volume=p_vol;
+	if (stream_rid.is_valid())
+		AudioServer::get_singleton()->stream_set_volume_scale(stream_rid,volume);
+}
+
+float SpatialStreamPlayer::get_volume() const {
+
+	return volume;
+}
+
+void SpatialStreamPlayer::set_loop_restart_time(float p_secs) {
+
+	loop_point=p_secs;
+	if (playback.is_valid())
+		playback->set_loop_restart_time(p_secs);
+}
+
+float SpatialStreamPlayer::get_loop_restart_time() const {
+
+	return loop_point;
+}
+
+
+void SpatialStreamPlayer::set_volume_db(float p_db) {
+
+	if (p_db<-79)
+		set_volume(0);
+	else
+		set_volume(Math::db2linear(p_db));
+}
+
+float SpatialStreamPlayer::get_volume_db() const {
+
+	if (volume==0)
+		return -80;
+	else
+		return Math::linear2db(volume);
+}
 
 
 String SpatialStreamPlayer::get_stream_name() const  {
@@ -132,27 +247,85 @@ String SpatialStreamPlayer::get_stream_name() const  {
 
 int SpatialStreamPlayer::get_loop_count() const  {
 
-	if (stream.is_null())
+	if (playback.is_null())
 		return 0;
-	return stream->get_loop_count();
+	return playback->get_loop_count();
 
 }
 
 float SpatialStreamPlayer::get_pos() const  {
 
-	if (stream.is_null())
+	if (playback.is_null())
 		return 0;
-	return stream->get_pos();
+	return playback->get_pos();
+
+}
+
+float SpatialStreamPlayer::get_length() const {
 
+	if (playback.is_null())
+		return 0;
+	return playback->get_length();
 }
 void SpatialStreamPlayer::seek_pos(float p_time) {
 
-	if (stream.is_null())
+	if (playback.is_null())
 		return;
-	return stream->seek_pos(p_time);
+	return playback->seek_pos(p_time);
+
+}
+
+void SpatialStreamPlayer::set_autoplay(bool p_enable) {
+
+	autoplay=p_enable;
+}
+
+bool SpatialStreamPlayer::has_autoplay() const {
+
+	return autoplay;
+}
+
+void SpatialStreamPlayer::set_paused(bool p_paused) {
+
+	paused=p_paused;
+	//if (stream.is_valid())
+	//	stream->set_paused(p_paused);
+}
+
+bool SpatialStreamPlayer::is_paused() const {
 
+	return paused;
 }
 
+void SpatialStreamPlayer::_set_play(bool p_play) {
+
+	_play=p_play;
+	if (is_inside_tree()) {
+		if(_play)
+			play();
+		else
+			stop();
+	}
+
+}
+
+bool SpatialStreamPlayer::_get_play() const{
+
+	return _play;
+}
+
+void SpatialStreamPlayer::set_buffering_msec(int p_msec) {
+
+	buffering_ms=p_msec;
+}
+
+int SpatialStreamPlayer::get_buffering_msec() const{
+
+	return buffering_ms;
+}
+
+
+
 void SpatialStreamPlayer::_bind_methods() {
 
 	ObjectTypeDB::bind_method(_MD("set_stream","stream:Stream"),&SpatialStreamPlayer::set_stream);
@@ -163,28 +336,67 @@ void SpatialStreamPlayer::_bind_methods() {
 
 	ObjectTypeDB::bind_method(_MD("is_playing"),&SpatialStreamPlayer::is_playing);
 
+	ObjectTypeDB::bind_method(_MD("set_paused","paused"),&SpatialStreamPlayer::set_paused);
+	ObjectTypeDB::bind_method(_MD("is_paused"),&SpatialStreamPlayer::is_paused);
+
 	ObjectTypeDB::bind_method(_MD("set_loop","enabled"),&SpatialStreamPlayer::set_loop);
 	ObjectTypeDB::bind_method(_MD("has_loop"),&SpatialStreamPlayer::has_loop);
 
+	ObjectTypeDB::bind_method(_MD("set_volume","volume"),&SpatialStreamPlayer::set_volume);
+	ObjectTypeDB::bind_method(_MD("get_volume"),&SpatialStreamPlayer::get_volume);
+
+	ObjectTypeDB::bind_method(_MD("set_volume_db","db"),&SpatialStreamPlayer::set_volume_db);
+	ObjectTypeDB::bind_method(_MD("get_volume_db"),&SpatialStreamPlayer::get_volume_db);
+
+	ObjectTypeDB::bind_method(_MD("set_buffering_msec","msec"),&SpatialStreamPlayer::set_buffering_msec);
+	ObjectTypeDB::bind_method(_MD("get_buffering_msec"),&SpatialStreamPlayer::get_buffering_msec);
+
+	ObjectTypeDB::bind_method(_MD("set_loop_restart_time","secs"),&SpatialStreamPlayer::set_loop_restart_time);
+	ObjectTypeDB::bind_method(_MD("get_loop_restart_time"),&SpatialStreamPlayer::get_loop_restart_time);
+
 	ObjectTypeDB::bind_method(_MD("get_stream_name"),&SpatialStreamPlayer::get_stream_name);
 	ObjectTypeDB::bind_method(_MD("get_loop_count"),&SpatialStreamPlayer::get_loop_count);
 
 	ObjectTypeDB::bind_method(_MD("get_pos"),&SpatialStreamPlayer::get_pos);
 	ObjectTypeDB::bind_method(_MD("seek_pos","time"),&SpatialStreamPlayer::seek_pos);
 
-	ADD_PROPERTY( PropertyInfo(Variant::OBJECT, "stream/stream", PROPERTY_HINT_RESOURCE_TYPE,"AudioStream"), _SCS("set_stream"),_SCS("get_stream") );
-	ADD_PROPERTY( PropertyInfo(Variant::BOOL, "stream/loop"), _SCS("set_loop"),_SCS("has_loop") );
+	ObjectTypeDB::bind_method(_MD("set_autoplay","enabled"),&SpatialStreamPlayer::set_autoplay);
+	ObjectTypeDB::bind_method(_MD("has_autoplay"),&SpatialStreamPlayer::has_autoplay);
 
+	ObjectTypeDB::bind_method(_MD("get_length"),&SpatialStreamPlayer::get_length);
+
+	ObjectTypeDB::bind_method(_MD("_set_play","play"),&SpatialStreamPlayer::_set_play);
+	ObjectTypeDB::bind_method(_MD("_get_play"),&SpatialStreamPlayer::_get_play);
+
+	ADD_PROPERTY( PropertyInfo(Variant::OBJECT, "stream/stream", PROPERTY_HINT_RESOURCE_TYPE,"AudioStream"), _SCS("set_stream"), _SCS("get_stream") );
+	ADD_PROPERTY( PropertyInfo(Variant::BOOL, "stream/play"), _SCS("_set_play"), _SCS("_get_play") );
+	ADD_PROPERTY( PropertyInfo(Variant::BOOL, "stream/loop"), _SCS("set_loop"), _SCS("has_loop") );
+	ADD_PROPERTY( PropertyInfo(Variant::REAL, "stream/volume_db", PROPERTY_HINT_RANGE,"-80,24,0.01"), _SCS("set_volume_db"), _SCS("get_volume_db") );
+	ADD_PROPERTY( PropertyInfo(Variant::BOOL, "stream/autoplay"), _SCS("set_autoplay"), _SCS("has_autoplay") );
+	ADD_PROPERTY( PropertyInfo(Variant::BOOL, "stream/paused"), _SCS("set_paused"), _SCS("is_paused") );
+	ADD_PROPERTY( PropertyInfo(Variant::INT, "stream/loop_restart_time"), _SCS("set_loop_restart_time"), _SCS("get_loop_restart_time") );
+	ADD_PROPERTY( PropertyInfo(Variant::INT, "stream/buffering_ms"), _SCS("set_buffering_msec"), _SCS("get_buffering_msec") );
 }
 
 
 SpatialStreamPlayer::SpatialStreamPlayer() {
 
+	volume=1;
 	loops=false;
+	paused=false;
+	autoplay=false;
+	_play=false;
+	server_mix_rate=1;
+	internal_stream.player=this;
+	stream_rid=AudioServer::get_singleton()->audio_stream_create(&internal_stream);
+	buffering_ms=500;
+	loop_point=0;
+
 }
 
 SpatialStreamPlayer::~SpatialStreamPlayer() {
-
-}
+	AudioServer::get_singleton()->free(stream_rid);
+	resampler.clear();
 
 
+}

+ 52 - 4
scene/3d/spatial_stream_player.h

@@ -31,16 +31,48 @@
 
 #include "scene/resources/audio_stream.h"
 #include "scene/3d/spatial_player.h"
-
+#include "servers/audio/audio_rb_resampler.h"
 
 class SpatialStreamPlayer : public SpatialPlayer {
 
 	OBJ_TYPE(SpatialStreamPlayer,SpatialPlayer);
 
+	_THREAD_SAFE_CLASS_
+
+	struct InternalStream : public AudioServer::AudioStream {
+		SpatialStreamPlayer *player;
+		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();
+	};
+
+
+	InternalStream internal_stream;
+	Ref<AudioStreamPlayback> playback;
 	Ref<AudioStream> stream;
+
+	int sp_get_channel_count() const;
+	void sp_set_mix_rate(int p_rate); //notify the stream of the mix rate
+	bool sp_mix(int32_t *p_buffer,int p_frames);
+	void sp_update();
+
+	int server_mix_rate;
+
+	RID stream_rid;
+	bool paused;
+	bool autoplay;
 	bool loops;
-protected:
+	float volume;
+	float loop_point;
+	int buffering_ms;
 
+	AudioRBResampler resampler;
+
+	bool _play;
+	void _set_play(bool p_play);
+	bool _get_play() const;
+protected:
 	void _notification(int p_what);
 
 	static void _bind_methods();
@@ -49,21 +81,37 @@ public:
 	void set_stream(const Ref<AudioStream> &p_stream);
 	Ref<AudioStream> get_stream() const;
 
-	void play();
+	void play(float p_from_offset=0);
 	void stop();
 	bool is_playing() const;
 
+	void set_paused(bool p_paused);
+	bool is_paused() const;
+
 	void set_loop(bool p_enable);
 	bool has_loop() const;
 
+	void set_volume(float p_vol);
+	float get_volume() const;
+
+	void set_loop_restart_time(float p_secs);
+	float get_loop_restart_time() const;
+
+	void set_volume_db(float p_db);
+	float get_volume_db() const;
 
 	String get_stream_name() const;
-	int get_loop_count() const;
 
+	int get_loop_count() const;
 
 	float get_pos() const;
 	void seek_pos(float p_time);
+	float get_length() const;
+	void set_autoplay(bool p_vol);
+	bool has_autoplay() const;
 
+	void set_buffering_msec(int p_msec);
+	int get_buffering_msec() const;
 
 	SpatialStreamPlayer();
 	~SpatialStreamPlayer();

+ 138 - 32
scene/audio/stream_player.cpp

@@ -28,6 +28,67 @@
 /*************************************************************************/
 #include "stream_player.h"
 
+int StreamPlayer::InternalStream::get_channel_count() const {
+
+	return player->sp_get_channel_count();
+}
+void StreamPlayer::InternalStream::set_mix_rate(int p_rate){
+
+	return player->sp_set_mix_rate(p_rate);
+}
+bool StreamPlayer::InternalStream::mix(int32_t *p_buffer,int p_frames){
+
+	return player->sp_mix(p_buffer,p_frames);
+}
+void StreamPlayer::InternalStream::update(){
+
+	player->sp_update();
+}
+
+
+int StreamPlayer::sp_get_channel_count() const {
+
+	return playback->get_channels();
+}
+
+void StreamPlayer::sp_set_mix_rate(int p_rate){
+
+	server_mix_rate=p_rate;
+}
+
+bool StreamPlayer::sp_mix(int32_t *p_buffer,int p_frames) {
+
+	if (resampler.is_ready()) {
+		return resampler.mix(p_buffer,p_frames);
+	}
+
+	return false;
+}
+
+void StreamPlayer::sp_update() {
+
+	_THREAD_SAFE_METHOD_
+	if (!paused && resampler.is_ready() && playback.is_valid()) {
+
+		if (!playback->is_playing()) {
+			//stream depleted data, but there's still audio in the ringbuffer
+			//check that all this audio has been flushed before stopping the stream
+			int to_mix = resampler.get_total() - resampler.get_todo();
+			if (to_mix==0) {
+				stop();
+				return;
+			}
+
+			return;
+		}
+
+		int todo =resampler.get_todo();
+		int wrote = playback->mix(resampler.get_write_buffer(),todo);
+		resampler.write(wrote);
+	}
+}
+
+
 
 void StreamPlayer::_notification(int p_what) {
 
@@ -52,19 +113,21 @@ void StreamPlayer::set_stream(const Ref<AudioStream> &p_stream) {
 
 	stop();
 
-	if (stream_rid.is_valid())
-		AudioServer::get_singleton()->free(stream_rid);
-	stream_rid=RID();
-
 	stream=p_stream;
-	if (!stream.is_null()) {
 
-		stream->set_loop(loops);
-		stream->set_paused(paused);
-		stream_rid=AudioServer::get_singleton()->audio_stream_create(stream->get_audio_stream());
+	if (!stream.is_null()) {
+		playback=stream->instance_playback();
+		playback->set_loop(loops);
+		playback->set_loop_restart_time(loop_point);
+		AudioServer::get_singleton()->lock();
+		resampler.setup(playback->get_channels(),playback->get_mix_rate(),server_mix_rate,buffering_ms,playback->get_minimum_buffer_size());
+		AudioServer::get_singleton()->unlock();
+	} else {
+		AudioServer::get_singleton()->lock();
+		resampler.clear();
+		playback.unref();
+		AudioServer::get_singleton()->unlock();
 	}
-
-
 }
 
 Ref<AudioStream> StreamPlayer::get_stream() const {
@@ -73,15 +136,18 @@ Ref<AudioStream> StreamPlayer::get_stream() const {
 }
 
 
-void StreamPlayer::play() {
+void StreamPlayer::play(float p_from_offset) {
 
 	ERR_FAIL_COND(!is_inside_tree());
-	if (stream.is_null())
+	if (playback.is_null())
 		return;
-	if (stream->is_playing())
+	if (playback->is_playing())
 		stop();
 
-	stream->play();
+	_THREAD_SAFE_METHOD_
+	playback->play(p_from_offset);
+	//feed the ringbuffer as long as no update callback is going on
+	sp_update();
 	AudioServer::get_singleton()->stream_set_active(stream_rid,true);
 	AudioServer::get_singleton()->stream_set_volume_scale(stream_rid,volume);
 //	if (stream->get_update_mode()!=AudioStream::UPDATE_NONE)
@@ -93,28 +159,29 @@ void StreamPlayer::stop() {
 
 	if (!is_inside_tree())
 		return;
-	if (stream.is_null())
+	if (playback.is_null())
 		return;
 
+	_THREAD_SAFE_METHOD_
 	AudioServer::get_singleton()->stream_set_active(stream_rid,false);
-	stream->stop();
+	playback->stop();
 	//set_idle_process(false);
 }
 
 bool StreamPlayer::is_playing() const {
 
-	if (stream.is_null())
+	if (playback.is_null())
 		return false;
 
-	return stream->is_playing();
+	return playback->is_playing();
 }
 
 void StreamPlayer::set_loop(bool p_enable) {
 
 	loops=p_enable;
-	if (stream.is_null())
+	if (playback.is_null())
 		return;
-	stream->set_loop(loops);
+	playback->set_loop(loops);
 
 }
 bool StreamPlayer::has_loop() const {
@@ -134,6 +201,19 @@ float StreamPlayer::get_volume() const {
 	return volume;
 }
 
+void StreamPlayer::set_loop_restart_time(float p_secs) {
+
+	loop_point=p_secs;
+	if (playback.is_valid())
+		playback->set_loop_restart_time(p_secs);
+}
+
+float StreamPlayer::get_loop_restart_time() const {
+
+	return loop_point;
+}
+
+
 void StreamPlayer::set_volume_db(float p_db) {
 
 	if (p_db<-79)
@@ -161,31 +241,31 @@ String StreamPlayer::get_stream_name() const  {
 
 int StreamPlayer::get_loop_count() const  {
 
-	if (stream.is_null())
+	if (playback.is_null())
 		return 0;
-	return stream->get_loop_count();
+	return playback->get_loop_count();
 
 }
 
 float StreamPlayer::get_pos() const  {
 
-	if (stream.is_null())
+	if (playback.is_null())
 		return 0;
-	return stream->get_pos();
+	return playback->get_pos();
 
 }
 
 float StreamPlayer::get_length() const {
 
-	if (stream.is_null())
+	if (playback.is_null())
 		return 0;
-	return stream->get_length();
+	return playback->get_length();
 }
 void StreamPlayer::seek_pos(float p_time) {
 
-	if (stream.is_null())
+	if (playback.is_null())
 		return;
-	return stream->seek_pos(p_time);
+	return playback->seek_pos(p_time);
 
 }
 
@@ -202,8 +282,8 @@ bool StreamPlayer::has_autoplay() const {
 void StreamPlayer::set_paused(bool p_paused) {
 
 	paused=p_paused;
-	if (stream.is_valid())
-		stream->set_paused(p_paused);
+	//if (stream.is_valid())
+	//	stream->set_paused(p_paused);
 }
 
 bool StreamPlayer::is_paused() const {
@@ -228,6 +308,17 @@ bool StreamPlayer::_get_play() const{
 	return _play;
 }
 
+void StreamPlayer::set_buffering_msec(int p_msec) {
+
+	buffering_ms=p_msec;
+}
+
+int StreamPlayer::get_buffering_msec() const{
+
+	return buffering_ms;
+}
+
+
 
 void StreamPlayer::_bind_methods() {
 
@@ -251,6 +342,12 @@ void StreamPlayer::_bind_methods() {
 	ObjectTypeDB::bind_method(_MD("set_volume_db","db"),&StreamPlayer::set_volume_db);
 	ObjectTypeDB::bind_method(_MD("get_volume_db"),&StreamPlayer::get_volume_db);
 
+	ObjectTypeDB::bind_method(_MD("set_buffering_msec","msec"),&StreamPlayer::set_buffering_msec);
+	ObjectTypeDB::bind_method(_MD("get_buffering_msec"),&StreamPlayer::get_buffering_msec);
+
+	ObjectTypeDB::bind_method(_MD("set_loop_restart_time","secs"),&StreamPlayer::set_loop_restart_time);
+	ObjectTypeDB::bind_method(_MD("get_loop_restart_time"),&StreamPlayer::get_loop_restart_time);
+
 	ObjectTypeDB::bind_method(_MD("get_stream_name"),&StreamPlayer::get_stream_name);
 	ObjectTypeDB::bind_method(_MD("get_loop_count"),&StreamPlayer::get_loop_count);
 
@@ -271,6 +368,8 @@ void StreamPlayer::_bind_methods() {
 	ADD_PROPERTY( PropertyInfo(Variant::REAL, "stream/volume_db", PROPERTY_HINT_RANGE,"-80,24,0.01"), _SCS("set_volume_db"), _SCS("get_volume_db") );
 	ADD_PROPERTY( PropertyInfo(Variant::BOOL, "stream/autoplay"), _SCS("set_autoplay"), _SCS("has_autoplay") );
 	ADD_PROPERTY( PropertyInfo(Variant::BOOL, "stream/paused"), _SCS("set_paused"), _SCS("is_paused") );
+	ADD_PROPERTY( PropertyInfo(Variant::INT, "stream/loop_restart_time"), _SCS("set_loop_restart_time"), _SCS("get_loop_restart_time") );
+	ADD_PROPERTY( PropertyInfo(Variant::INT, "stream/buffering_ms"), _SCS("set_buffering_msec"), _SCS("get_buffering_msec") );
 }
 
 
@@ -281,10 +380,17 @@ StreamPlayer::StreamPlayer() {
 	paused=false;
 	autoplay=false;
 	_play=false;
+	server_mix_rate=1;
+	internal_stream.player=this;
+	stream_rid=AudioServer::get_singleton()->audio_stream_create(&internal_stream);
+	buffering_ms=500;
+	loop_point=0;
+
 }
 
 StreamPlayer::~StreamPlayer() {
-	if (stream_rid.is_valid())
-		AudioServer::get_singleton()->free(stream_rid);
+	AudioServer::get_singleton()->free(stream_rid);
+	resampler.clear();
+
 
 }

+ 32 - 1
scene/audio/stream_player.h

@@ -31,17 +31,43 @@
 
 #include "scene/resources/audio_stream.h"
 #include "scene/main/node.h"
+#include "servers/audio/audio_rb_resampler.h"
 
 class StreamPlayer : public Node {
 
 	OBJ_TYPE(StreamPlayer,Node);
 
+	_THREAD_SAFE_CLASS_
+
+	struct InternalStream : public AudioServer::AudioStream {
+		StreamPlayer *player;
+		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();
+	};
+
+
+	InternalStream internal_stream;
+	Ref<AudioStreamPlayback> playback;
 	Ref<AudioStream> stream;
+
+	int sp_get_channel_count() const;
+	void sp_set_mix_rate(int p_rate); //notify the stream of the mix rate
+	bool sp_mix(int32_t *p_buffer,int p_frames);
+	void sp_update();
+
+	int server_mix_rate;
+
 	RID stream_rid;
 	bool paused;
 	bool autoplay;
 	bool loops;
 	float volume;
+	float loop_point;
+	int buffering_ms;
+
+	AudioRBResampler resampler;
 
 	bool _play;
 	void _set_play(bool p_play);
@@ -55,7 +81,7 @@ public:
 	void set_stream(const Ref<AudioStream> &p_stream);
 	Ref<AudioStream> get_stream() const;
 
-	void play();
+	void play(float p_from_offset=0);
 	void stop();
 	bool is_playing() const;
 
@@ -68,6 +94,9 @@ public:
 	void set_volume(float p_vol);
 	float get_volume() const;
 
+	void set_loop_restart_time(float p_secs);
+	float get_loop_restart_time() const;
+
 	void set_volume_db(float p_db);
 	float get_volume_db() const;
 
@@ -81,6 +110,8 @@ public:
 	void set_autoplay(bool p_vol);
 	bool has_autoplay() const;
 
+	void set_buffering_msec(int p_msec);
+	int get_buffering_msec() const;
 
 	StreamPlayer();
 	~StreamPlayer();

+ 2 - 1
scene/register_scene_types.cpp

@@ -578,7 +578,8 @@ void register_scene_types() {
 	ObjectTypeDB::register_type<Sample>();
 	ObjectTypeDB::register_type<SampleLibrary>();
 	ObjectTypeDB::register_virtual_type<AudioStream>();
-	ObjectTypeDB::register_type<AudioStreamGibberish>();
+	ObjectTypeDB::register_virtual_type<AudioStreamPlayback>();
+//	ObjectTypeDB::register_type<AudioStreamGibberish>();
 	ObjectTypeDB::register_virtual_type<VideoStream>();
 
 	OS::get_singleton()->yield(); //may take time to init

+ 14 - 56
scene/resources/audio_stream.cpp

@@ -28,76 +28,34 @@
 /*************************************************************************/
 #include "audio_stream.h"
 
+//////////////////////////////
 
 
-int AudioStream::InternalAudioStream::get_channel_count() const {
+void AudioStreamPlayback::_bind_methods() {
 
-	return owner->get_channel_count();
+	ObjectTypeDB::bind_method(_MD("play","from_pos_sec"),&AudioStreamPlayback::play,DEFVAL(0));
+	ObjectTypeDB::bind_method(_MD("stop"),&AudioStreamPlayback::stop);
+	ObjectTypeDB::bind_method(_MD("is_playing"),&AudioStreamPlayback::is_playing);
 
-}
-
-void AudioStream::InternalAudioStream::set_mix_rate(int p_rate) {
-
-	owner->_mix_rate=p_rate;
-}
-
-bool AudioStream::InternalAudioStream::mix(int32_t *p_buffer,int p_frames) {
-
-	return owner->mix(p_buffer,p_frames);
-}
-
-bool AudioStream::InternalAudioStream::can_update_mt() const {
+	ObjectTypeDB::bind_method(_MD("set_loop","enabled"),&AudioStreamPlayback::set_loop);
+	ObjectTypeDB::bind_method(_MD("has_loop"),&AudioStreamPlayback::has_loop);
 
-	return owner->get_update_mode()==UPDATE_THREAD;
-}
+	ObjectTypeDB::bind_method(_MD("get_loop_count"),&AudioStreamPlayback::get_loop_count);
 
-void AudioStream::InternalAudioStream::update() {
+	ObjectTypeDB::bind_method(_MD("seek_pos","pos"),&AudioStreamPlayback::seek_pos);
+	ObjectTypeDB::bind_method(_MD("get_pos"),&AudioStreamPlayback::get_pos);
 
-	owner->update();
-}
+	ObjectTypeDB::bind_method(_MD("get_length"),&AudioStreamPlayback::get_length);
+	ObjectTypeDB::bind_method(_MD("get_channels"),&AudioStreamPlayback::get_channels);
+	ObjectTypeDB::bind_method(_MD("get_mix_rate"),&AudioStreamPlayback::get_mix_rate);
+	ObjectTypeDB::bind_method(_MD("get_minimum_buffer_size"),&AudioStreamPlayback::get_minimum_buffer_size);
 
-AudioServer::AudioStream *AudioStream::get_audio_stream() {
 
-	return internal_audio_stream;
 }
 
 
 void AudioStream::_bind_methods() {
 
-	ObjectTypeDB::bind_method(_MD("play"),&AudioStream::play);
-	ObjectTypeDB::bind_method(_MD("stop"),&AudioStream::stop);
-	ObjectTypeDB::bind_method(_MD("is_playing"),&AudioStream::is_playing);
-
-	ObjectTypeDB::bind_method(_MD("set_loop","enabled"),&AudioStream::set_loop);
-	ObjectTypeDB::bind_method(_MD("has_loop"),&AudioStream::has_loop);
-
-	ObjectTypeDB::bind_method(_MD("get_stream_name"),&AudioStream::get_stream_name);
-	ObjectTypeDB::bind_method(_MD("get_loop_count"),&AudioStream::get_loop_count);
-
-	ObjectTypeDB::bind_method(_MD("seek_pos","pos"),&AudioStream::seek_pos);
-	ObjectTypeDB::bind_method(_MD("get_pos"),&AudioStream::get_pos);
-
-	ObjectTypeDB::bind_method(_MD("get_length"),&AudioStream::get_length);
-
-	ObjectTypeDB::bind_method(_MD("get_update_mode"),&AudioStream::get_update_mode);
-
-	ObjectTypeDB::bind_method(_MD("update"),&AudioStream::update);
-
-	BIND_CONSTANT( UPDATE_NONE );
-	BIND_CONSTANT( UPDATE_IDLE );
-	BIND_CONSTANT( UPDATE_THREAD );
 
 }
 
-AudioStream::AudioStream() {
-
-	_mix_rate=44100;
-	internal_audio_stream = memnew( InternalAudioStream );
-	internal_audio_stream->owner=this;
-}
-
-
-AudioStream::~AudioStream()  {
-
-	memdelete(internal_audio_stream);
-}

+ 25 - 44
scene/resources/audio_stream.h

@@ -31,72 +31,53 @@
 
 #include "resource.h"
 #include "servers/audio_server.h"
-#include "scene/resources/audio_stream.h"
-
-class AudioStream : public Resource {
-
-	OBJ_TYPE( AudioStream, Resource );
-	OBJ_SAVE_TYPE( AudioStream ); //children are all saved as AudioStream, so they can be exchanged
-
-	friend class InternalAudioStream;
-
-	struct InternalAudioStream : public AudioServer::AudioStream {
-
-		::AudioStream *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 bool can_update_mt() const;
-		virtual void update();
-	};
 
+class AudioStreamPlayback : public Reference {
 
-	int _mix_rate;
-	InternalAudioStream *internal_audio_stream;
+	OBJ_TYPE( AudioStreamPlayback, Reference );
 protected:
-
-	_FORCE_INLINE_ int get_mix_rate() const { return _mix_rate; }
-	virtual int get_channel_count() const=0;
-	virtual bool mix(int32_t *p_buffer, int p_frames)=0;
-
 	static void _bind_methods();
 public:
 
-	enum UpdateMode {
-		UPDATE_NONE,
-		UPDATE_IDLE,
-		UPDATE_THREAD
-	};
 
-	AudioServer::AudioStream *get_audio_stream();
-
-	virtual void play()=0;
+	virtual void play(float p_from_pos=0)=0;
 	virtual void stop()=0;
 	virtual bool is_playing() const=0;
 
-	virtual void set_paused(bool p_paused)=0;
-	virtual bool is_paused(bool p_paused) const=0;
-
 	virtual void set_loop(bool p_enable)=0;
 	virtual bool has_loop() const=0;
 
-	virtual float get_length() const=0;
-
-	virtual String get_stream_name() const=0;
+	virtual void set_loop_restart_time(float p_time)=0;
 
 	virtual int get_loop_count() const=0;
 
 	virtual float get_pos() const=0;
 	virtual void seek_pos(float p_time)=0;
 
-	virtual UpdateMode get_update_mode() const=0;
-	virtual void update()=0;
+	virtual int mix(int16_t* p_bufer,int p_frames)=0;
+
+	virtual float get_length() const=0;
+	virtual String get_stream_name() const=0;
+
+	virtual int get_channels() const=0;
+	virtual int get_mix_rate() const=0;
+	virtual int get_minimum_buffer_size() const=0;
 
-	AudioStream();
-	~AudioStream();
 };
 
+class AudioStream : public Resource {
+
+	OBJ_TYPE( AudioStream, Resource );
+	OBJ_SAVE_TYPE( AudioStream ); //children are all saved as AudioStream, so they can be exchanged
+
+protected:
+	static void _bind_methods();
+public:
+
+	virtual Ref<AudioStreamPlayback> instance_playback()=0;
+
+
+};
 
-VARIANT_ENUM_CAST( AudioStream::UpdateMode );
 
 #endif // AUDIO_STREAM_H

+ 4 - 0
scene/resources/audio_stream_resampled.cpp

@@ -28,6 +28,9 @@
 /*************************************************************************/
 #include "audio_stream_resampled.h"
 #include "globals.h"
+
+
+#if 0
 int AudioStreamResampled::get_channel_count() const {
 
 	if (!rb)
@@ -382,3 +385,4 @@ AudioStreamResampled::~AudioStreamResampled() {
 
 }
 
+#endif

+ 2 - 1
scene/resources/audio_stream_resampled.h

@@ -31,6 +31,7 @@
 
 #include "scene/resources/audio_stream.h"
 
+#if 0
 
 class AudioStreamResampled : public AudioStream {
 	OBJ_TYPE(AudioStreamResampled,AudioStream);
@@ -160,5 +161,5 @@ public:
 	AudioStreamResampled();
 	~AudioStreamResampled();
 };
-
+#endif
 #endif // AUDIO_STREAM_RESAMPLED_H

+ 3 - 0
scene/resources/gibberish_stream.cpp

@@ -29,6 +29,8 @@
 #include "gibberish_stream.h"
 #include "servers/audio_server.h"
 
+#if 0
+
 int AudioStreamGibberish::get_channel_count() const {
 
 	return 1;
@@ -328,3 +330,4 @@ AudioStreamGibberish::AudioStreamGibberish() {
 	paused=false;
 	active_voices=0;
 }
+#endif

+ 3 - 1
scene/resources/gibberish_stream.h

@@ -29,7 +29,7 @@
 #ifndef GIBBERISH_STREAM_H
 #define GIBBERISH_STREAM_H
 
-
+#if 0
 #include "scene/resources/audio_stream.h"
 #include "scene/resources/sample_library.h"
 class AudioStreamGibberish : public AudioStream {
@@ -109,4 +109,6 @@ public:
 	AudioStreamGibberish();
 };
 
+#endif
+
 #endif // GIBBERISH_STREAM_H

+ 356 - 0
servers/audio/audio_rb_resampler.cpp

@@ -0,0 +1,356 @@
+#include "audio_rb_resampler.h"
+
+
+int AudioRBResampler::get_channel_count() const {
+
+	if (!rb)
+		return 0;
+
+	return channels;
+}
+
+
+template<int C>
+uint32_t AudioRBResampler::_resample(int32_t *p_dest,int p_todo,int32_t p_increment) {
+
+	uint32_t read=offset&MIX_FRAC_MASK;
+
+	for (int i=0;i<p_todo;i++) {
+
+		offset = (offset + p_increment)&(((1<<(rb_bits+MIX_FRAC_BITS))-1));
+		read+=p_increment;
+		uint32_t pos = offset >> MIX_FRAC_BITS;
+		uint32_t frac = offset & MIX_FRAC_MASK;
+#ifndef FAST_AUDIO
+		ERR_FAIL_COND_V(pos>=rb_len,0);
+#endif
+		uint32_t pos_next = (pos+1)&rb_mask;
+		//printf("rb pos %i\n",pos);
+
+		// since this is a template with a known compile time value (C), conditionals go away when compiling.
+		if (C==1) {
+
+			int32_t v0 = rb[pos];
+			int32_t v0n=rb[pos_next];
+#ifndef FAST_AUDIO
+			v0+=(v0n-v0)*(int32_t)frac >> MIX_FRAC_BITS;
+#endif
+			v0<<=16;
+			p_dest[i]=v0;
+
+		}
+		if (C==2) {
+
+			int32_t v0 = rb[(pos<<1)+0];
+			int32_t v1 = rb[(pos<<1)+1];
+			int32_t v0n=rb[(pos_next<<1)+0];
+			int32_t v1n=rb[(pos_next<<1)+1];
+
+#ifndef FAST_AUDIO
+			v0+=(v0n-v0)*(int32_t)frac >> MIX_FRAC_BITS;
+			v1+=(v1n-v1)*(int32_t)frac >> MIX_FRAC_BITS;
+#endif
+			v0<<=16;
+			v1<<=16;
+			p_dest[(i<<1)+0]=v0;
+			p_dest[(i<<1)+1]=v1;
+
+		}
+
+		if (C==4) {
+
+			int32_t v0 = rb[(pos<<2)+0];
+			int32_t v1 = rb[(pos<<2)+1];
+			int32_t v2 = rb[(pos<<2)+2];
+			int32_t v3 = rb[(pos<<2)+3];
+			int32_t v0n = rb[(pos_next<<2)+0];
+			int32_t v1n=rb[(pos_next<<2)+1];
+			int32_t v2n=rb[(pos_next<<2)+2];
+			int32_t v3n=rb[(pos_next<<2)+3];
+
+#ifndef FAST_AUDIO
+			v0+=(v0n-v0)*(int32_t)frac >> MIX_FRAC_BITS;
+			v1+=(v1n-v1)*(int32_t)frac >> MIX_FRAC_BITS;
+			v2+=(v2n-v2)*(int32_t)frac >> MIX_FRAC_BITS;
+			v3+=(v3n-v3)*(int32_t)frac >> MIX_FRAC_BITS;
+#endif
+			v0<<=16;
+			v1<<=16;
+			v2<<=16;
+			v3<<=16;
+			p_dest[(i<<2)+0]=v0;
+			p_dest[(i<<2)+1]=v1;
+			p_dest[(i<<2)+2]=v2;
+			p_dest[(i<<2)+3]=v3;
+
+		}
+
+		if (C==6) {
+
+			int32_t v0 = rb[(pos*6)+0];
+			int32_t v1 = rb[(pos*6)+1];
+			int32_t v2 = rb[(pos*6)+2];
+			int32_t v3 = rb[(pos*6)+3];
+			int32_t v4 = rb[(pos*6)+4];
+			int32_t v5 = rb[(pos*6)+5];
+			int32_t v0n = rb[(pos_next*6)+0];
+			int32_t v1n=rb[(pos_next*6)+1];
+			int32_t v2n=rb[(pos_next*6)+2];
+			int32_t v3n=rb[(pos_next*6)+3];
+			int32_t v4n=rb[(pos_next*6)+4];
+			int32_t v5n=rb[(pos_next*6)+5];
+
+#ifndef FAST_AUDIO
+			v0+=(v0n-v0)*(int32_t)frac >> MIX_FRAC_BITS;
+			v1+=(v1n-v1)*(int32_t)frac >> MIX_FRAC_BITS;
+			v2+=(v2n-v2)*(int32_t)frac >> MIX_FRAC_BITS;
+			v3+=(v3n-v3)*(int32_t)frac >> MIX_FRAC_BITS;
+			v4+=(v4n-v4)*(int32_t)frac >> MIX_FRAC_BITS;
+			v5+=(v5n-v5)*(int32_t)frac >> MIX_FRAC_BITS;
+#endif
+			v0<<=16;
+			v1<<=16;
+			v2<<=16;
+			v3<<=16;
+			v4<<=16;
+			v5<<=16;
+			p_dest[(i*6)+0]=v0;
+			p_dest[(i*6)+1]=v1;
+			p_dest[(i*6)+2]=v2;
+			p_dest[(i*6)+3]=v3;
+			p_dest[(i*6)+4]=v4;
+			p_dest[(i*6)+5]=v5;
+
+		}
+
+
+	}
+
+
+	return read>>MIX_FRAC_BITS;//rb_read_pos=offset>>MIX_FRAC_BITS;
+
+}
+
+
+bool AudioRBResampler::mix(int32_t *p_dest, int p_frames) {
+
+
+	if (!rb)
+		return false;
+
+	int write_pos_cache=rb_write_pos;
+
+	int32_t increment=(src_mix_rate*MIX_FRAC_LEN)/target_mix_rate;
+
+	int rb_todo;
+
+	if (write_pos_cache==rb_read_pos) {
+		return false; //out of buffer
+
+	} else if (rb_read_pos<write_pos_cache) {
+
+		rb_todo=write_pos_cache-rb_read_pos; //-1?
+	} else {
+
+		rb_todo=(rb_len-rb_read_pos)+write_pos_cache; //-1?
+	}
+
+	int todo = MIN( ((int64_t(rb_todo)<<MIX_FRAC_BITS)/increment)+1, p_frames );
+#if 0
+	if (int(src_mix_rate)==target_mix_rate) {
+
+
+		if (channels==6) {
+
+			for(int i=0;i<p_frames;i++) {
+
+				int from = ((rb_read_pos+i)&rb_mask)*6;
+				int to = i*6;
+
+				p_dest[from+0]=int32_t(rb[to+0])<<16;
+				p_dest[from+1]=int32_t(rb[to+1])<<16;
+				p_dest[from+2]=int32_t(rb[to+2])<<16;
+				p_dest[from+3]=int32_t(rb[to+3])<<16;
+				p_dest[from+4]=int32_t(rb[to+4])<<16;
+				p_dest[from+5]=int32_t(rb[to+5])<<16;
+			}
+
+		} else {
+			int len=p_frames*channels;
+			int from=rb_read_pos*channels;
+			int mask=0;
+			switch(channels) {
+				case 1: mask=rb_len-1; break;
+				case 2: mask=(rb_len*2)-1; break;
+				case 4: mask=(rb_len*4)-1; break;
+			}
+
+			for(int i=0;i<len;i++) {
+
+				p_dest[i]=int32_t(rb[(from+i)&mask])<<16;
+			}
+		}
+
+		rb_read_pos = (rb_read_pos+p_frames)&rb_mask;
+	} else
+#endif
+	{
+
+		uint32_t read=0;
+		switch(channels) {
+			case 1: read=_resample<1>(p_dest,todo,increment); break;
+			case 2: read=_resample<2>(p_dest,todo,increment); break;
+			case 4: read=_resample<4>(p_dest,todo,increment); break;
+			case 6: read=_resample<6>(p_dest,todo,increment); break;
+		}
+#if 1
+		//end of stream, fadeout
+		int remaining = p_frames-todo;
+		if (remaining && todo>0) {
+
+			//print_line("fadeout");
+			for(int c=0;c<channels;c++) {
+
+				for(int i=0;i<todo;i++) {
+
+					int32_t samp = p_dest[i*channels+c]>>8;
+					uint32_t mul = (todo-i) * 256 /todo;
+					//print_line("mul: "+itos(i)+" "+itos(mul));
+					p_dest[i*channels+c]=samp*mul;
+				}
+
+			}
+
+		}
+
+#else
+		int remaining = p_frames-todo;
+		if (remaining && todo>0) {
+
+
+			for(int c=0;c<channels;c++) {
+
+				int32_t from = p_dest[(todo-1)*channels+c]>>8;
+
+				for(int i=0;i<remaining;i++) {
+
+					uint32_t mul = (remaining-i) * 256 /remaining;
+					p_dest[(todo+i)*channels+c]=from*mul;
+				}
+
+			}
+
+		}
+#endif
+
+		//zero out what remains there to avoid glitches
+		for(int i=todo*channels;i<int(p_frames)*channels;i++) {
+
+			p_dest[i]=0;
+		}
+
+		if (read>rb_todo)
+			read=rb_todo;
+
+		rb_read_pos = (rb_read_pos+read)&rb_mask;
+
+
+
+
+	}
+
+	return true;
+}
+
+
+Error AudioRBResampler::setup(int p_channels,int p_src_mix_rate,int p_target_mix_rate,int p_buffer_msec,int p_minbuff_needed) {
+
+	ERR_FAIL_COND_V(p_channels!=1 && p_channels!=2 && p_channels!=4 && p_channels!=6,ERR_INVALID_PARAMETER);
+
+
+	//float buffering_sec = int(GLOBAL_DEF("audio/stream_buffering_ms",500))/1000.0;
+	int desired_rb_bits =nearest_shift(MAX((p_buffer_msec/1000.0)*p_src_mix_rate,p_minbuff_needed));
+
+	bool recreate=!rb;
+
+	if (rb && (uint32_t(desired_rb_bits)!=rb_bits || channels!=uint32_t(p_channels))) {
+		//recreate
+
+		memdelete_arr(rb);
+		memdelete_arr(read_buf);
+		recreate=true;
+
+	}
+
+	if (recreate) {
+
+		channels=p_channels;
+		rb_bits=desired_rb_bits;
+		rb_len=(1<<rb_bits);
+		rb_mask=rb_len-1;
+		rb = memnew_arr( int16_t, rb_len * p_channels );
+		read_buf = memnew_arr( int16_t, rb_len * p_channels );
+
+	}
+
+	src_mix_rate=p_src_mix_rate;
+	target_mix_rate=p_target_mix_rate;
+	offset=0;
+	rb_read_pos=0;
+	rb_write_pos=0;
+
+	//avoid maybe strange noises upon load
+	for (int i=0;i<(rb_len*channels);i++) {
+
+		rb[i]=0;
+		read_buf[i]=0;
+	}
+
+	return OK;
+
+}
+
+void AudioRBResampler::clear() {
+
+	if (!rb)
+		return;
+
+	//should be stopped at this point but just in case
+	if (rb) {
+		memdelete_arr(rb);
+		memdelete_arr(read_buf);
+	}
+	rb=NULL;
+	offset=0;
+	rb_read_pos=0;
+	rb_write_pos=0;
+	read_buf=NULL;
+}
+
+AudioRBResampler::AudioRBResampler() {
+
+	rb=NULL;
+	offset=0;
+	read_buf=NULL;
+	rb_read_pos=0;
+	rb_write_pos=0;
+
+	rb_bits=0;
+	rb_len=0;
+	rb_mask=0;
+	read_buff_len=0;
+	channels=0;
+	src_mix_rate=0;
+	target_mix_rate=0;
+
+}
+
+AudioRBResampler::~AudioRBResampler() {
+
+	if (rb) {
+		memdelete_arr(rb);
+		memdelete_arr(read_buf);
+	}
+
+}
+

+ 133 - 0
servers/audio/audio_rb_resampler.h

@@ -0,0 +1,133 @@
+#ifndef AUDIO_RB_RESAMPLER_H
+#define AUDIO_RB_RESAMPLER_H
+
+#include "typedefs.h"
+#include "os/memory.h"
+
+struct AudioRBResampler {
+
+	uint32_t rb_bits;
+	uint32_t rb_len;
+	uint32_t rb_mask;
+	uint32_t read_buff_len;
+	uint32_t channels;
+	uint32_t src_mix_rate;
+	uint32_t target_mix_rate;
+
+	volatile int rb_read_pos;
+	volatile int rb_write_pos;
+
+	int32_t offset; //contains the fractional remainder of the resampler
+	enum {
+		MIX_FRAC_BITS=13,
+		MIX_FRAC_LEN=(1<<MIX_FRAC_BITS),
+		MIX_FRAC_MASK=MIX_FRAC_LEN-1,
+	};
+
+	int16_t *read_buf;
+	int16_t *rb;
+
+
+	template<int C>
+	uint32_t _resample(int32_t *p_dest,int p_todo,int32_t p_increment);
+
+
+public:
+
+	_FORCE_INLINE_ void flush() {
+		rb_read_pos=0;
+		rb_write_pos=0;
+	}
+
+	_FORCE_INLINE_ bool is_ready() const{
+		return rb!=NULL;
+	}
+
+
+	_FORCE_INLINE_ int get_total() const {
+
+		return rb_len-1;
+	}
+
+	_FORCE_INLINE_ int get_todo() const { //return amount of frames to mix
+
+		int todo;
+		int read_pos_cache=rb_read_pos;
+
+		if (read_pos_cache==rb_write_pos) {
+			todo=rb_len-1;
+		} else if (read_pos_cache>rb_write_pos) {
+
+			todo=read_pos_cache-rb_write_pos-1;
+		} else {
+
+			todo=(rb_len-rb_write_pos)+read_pos_cache-1;
+		}
+
+		return todo;
+	}
+
+	_FORCE_INLINE_ int16_t *get_write_buffer() { return read_buf; }
+	_FORCE_INLINE_ void write(uint32_t p_frames) {
+
+		ERR_FAIL_COND(p_frames >= rb_len);
+
+		switch(channels) {
+			case 1: {
+
+				for(uint32_t i=0;i<p_frames;i++) {
+
+					rb[ rb_write_pos ] = read_buf[i];
+					rb_write_pos=(rb_write_pos+1)&rb_mask;
+				}
+			} break;
+			case 2: {
+
+				for(uint32_t i=0;i<p_frames;i++) {
+
+					rb[ (rb_write_pos<<1)+0 ] = read_buf[(i<<1)+0];
+					rb[ (rb_write_pos<<1)+1 ] = read_buf[(i<<1)+1];
+					rb_write_pos=(rb_write_pos+1)&rb_mask;
+				}
+			} break;
+			case 4: {
+
+				for(uint32_t i=0;i<p_frames;i++) {
+
+					rb[ (rb_write_pos<<2)+0 ] = read_buf[(i<<2)+0];
+					rb[ (rb_write_pos<<2)+1 ] = read_buf[(i<<2)+1];
+					rb[ (rb_write_pos<<2)+2 ] = read_buf[(i<<2)+2];
+					rb[ (rb_write_pos<<2)+3 ] = read_buf[(i<<2)+3];
+					rb_write_pos=(rb_write_pos+1)&rb_mask;
+				}
+			} break;
+			case 6: {
+
+				for(uint32_t i=0;i<p_frames;i++) {
+
+					rb[ (rb_write_pos*6)+0 ] = read_buf[(i*6)+0];
+					rb[ (rb_write_pos*6)+1 ] = read_buf[(i*6)+1];
+					rb[ (rb_write_pos*6)+2 ] = read_buf[(i*6)+2];
+					rb[ (rb_write_pos*6)+3 ] = read_buf[(i*6)+3];
+					rb[ (rb_write_pos*6)+4 ] = read_buf[(i*6)+4];
+					rb[ (rb_write_pos*6)+5 ] = read_buf[(i*6)+5];
+					rb_write_pos=(rb_write_pos+1)&rb_mask;
+				}
+			} break;
+
+
+		}
+
+	}
+
+	int get_channel_count() const;
+
+	Error setup(int p_channels, int p_src_mix_rate, int p_target_mix_rate, int p_buffer_msec, int p_minbuff_needed=-1);
+	void clear();
+	bool mix(int32_t *p_dest, int p_frames);
+
+	AudioRBResampler();
+	~AudioRBResampler();
+};
+
+#endif // AUDIO_RB_RESAMPLER_H

+ 5 - 1
servers/audio/audio_server_sw.cpp

@@ -830,10 +830,14 @@ void AudioServerSW::finish() {
 void AudioServerSW::_update_streams(bool p_thread) {
 
 	_THREAD_SAFE_METHOD_
-	for(List<Stream*>::Element *E=active_audio_streams.front();E;E=E->next()) {
+	for(List<Stream*>::Element *E=active_audio_streams.front();E;) { //stream might be removed durnig this callback
+
+		List<Stream*>::Element *N=E->next();
 
 		if (E->get()->audio_stream && p_thread == E->get()->audio_stream->can_update_mt())
 			E->get()->audio_stream->update();
+
+		E=N;
 	}
 
 }

+ 4 - 0
tools/editor/spatial_editor_gizmos.h

@@ -201,6 +201,8 @@ public:
 };
 
 
+
+
 class SpatialPlayerSpatialGizmo  : public SpatialGizmoTool {
 
 	OBJ_TYPE(SpatialPlayerSpatialGizmo,SpatialGizmoTool);
@@ -214,6 +216,8 @@ public:
 
 };
 
+
+
 class TestCubeSpatialGizmo  : public SpatialGizmoTool {
 
 	OBJ_TYPE(TestCubeSpatialGizmo,SpatialGizmoTool);