aboutsummaryrefslogtreecommitdiff
path: root/servers/audio
diff options
context:
space:
mode:
authorJuan Linietsky2014-02-09 22:10:30 -0300
committerJuan Linietsky2014-02-09 22:10:30 -0300
commit0b806ee0fc9097fa7bda7ac0109191c9c5e0a1ac (patch)
tree276c4d099e178eb67fbd14f61d77b05e3808e9e3 /servers/audio
parent0e49da1687bc8192ed210947da52c9e5c5f301bb (diff)
downloadgodot-0b806ee.tar.gz
godot-0b806ee.tar.zst
godot-0b806ee.zip
GODOT IS OPEN SOURCE
Diffstat (limited to '')
-rw-r--r--servers/audio/SCsub7
-rw-r--r--servers/audio/audio_driver_dummy.cpp146
-rw-r--r--servers/audio/audio_driver_dummy.h76
-rw-r--r--servers/audio/audio_filter_sw.cpp286
-rw-r--r--servers/audio/audio_filter_sw.h119
-rw-r--r--servers/audio/audio_mixer_sw.cpp1085
-rw-r--r--servers/audio/audio_mixer_sw.h248
-rw-r--r--servers/audio/audio_server_sw.cpp1012
-rw-r--r--servers/audio/audio_server_sw.h279
-rw-r--r--servers/audio/reverb_buffers_sw.cpp33
-rw-r--r--servers/audio/reverb_buffers_sw.h38
-rw-r--r--servers/audio/reverb_sw.cpp569
-rw-r--r--servers/audio/reverb_sw.h84
-rw-r--r--servers/audio/sample_manager_sw.cpp280
-rw-r--r--servers/audio/sample_manager_sw.h129
-rw-r--r--servers/audio/voice_rb_sw.cpp34
-rw-r--r--servers/audio/voice_rb_sw.h146
-rw-r--r--servers/audio_server.cpp178
-rw-r--r--servers/audio_server.h289
19 files changed, 5038 insertions, 0 deletions
diff --git a/servers/audio/SCsub b/servers/audio/SCsub
new file mode 100644
index 000000000..16fe3a59a
--- /dev/null
+++ b/servers/audio/SCsub
@@ -0,0 +1,7 @@
+Import('env')
+
+env.add_source_files(env.servers_sources,"*.cpp")
+
+Export('env')
+
+
diff --git a/servers/audio/audio_driver_dummy.cpp b/servers/audio/audio_driver_dummy.cpp
new file mode 100644
index 000000000..14d89cc21
--- /dev/null
+++ b/servers/audio/audio_driver_dummy.cpp
@@ -0,0 +1,146 @@
+/*************************************************************************/
+/* audio_driver_dummy.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#include "audio_driver_dummy.h"
+
+#include "globals.h"
+#include "os/os.h"
+
+
+
+Error AudioDriverDummy::init() {
+
+ active=false;
+ thread_exited=false;
+ exit_thread=false;
+ pcm_open = false;
+ samples_in = NULL;
+
+
+ mix_rate = 44100;
+ output_format = OUTPUT_STEREO;
+ channels = 2;
+
+ int latency = GLOBAL_DEF("audio/output_latency",25);
+ buffer_size = nearest_power_of_2( latency * mix_rate / 1000 );
+
+ samples_in = memnew_arr(int32_t, buffer_size*channels);
+
+ mutex=Mutex::create();
+ thread = Thread::create(AudioDriverDummy::thread_func, this);
+
+ return OK;
+};
+
+void AudioDriverDummy::thread_func(void* p_udata) {
+
+ AudioDriverDummy* ad = (AudioDriverDummy*)p_udata;
+
+ uint64_t usdelay = (ad->buffer_size / float(ad->mix_rate))*1000000;
+
+
+ while (!ad->exit_thread) {
+
+
+ if (!ad->active) {
+
+ } else {
+
+ ad->lock();
+
+ ad->audio_server_process(ad->buffer_size, ad->samples_in);
+
+ ad->unlock();
+
+ };
+
+ OS::get_singleton()->delay_usec(usdelay);
+
+ };
+
+ ad->thread_exited=true;
+
+};
+
+void AudioDriverDummy::start() {
+
+ active = true;
+};
+
+int AudioDriverDummy::get_mix_rate() const {
+
+ return mix_rate;
+};
+
+AudioDriverSW::OutputFormat AudioDriverDummy::get_output_format() const {
+
+ return output_format;
+};
+void AudioDriverDummy::lock() {
+
+ if (!thread || !mutex)
+ return;
+ mutex->lock();
+};
+void AudioDriverDummy::unlock() {
+
+ if (!thread || !mutex)
+ return;
+ mutex->unlock();
+};
+
+void AudioDriverDummy::finish() {
+
+ if (!thread)
+ return;
+
+ exit_thread = true;
+ Thread::wait_to_finish(thread);
+
+ if (samples_in) {
+ memdelete_arr(samples_in);
+ };
+
+ memdelete(thread);
+ if (mutex)
+ memdelete(mutex);
+ thread = NULL;
+};
+
+AudioDriverDummy::AudioDriverDummy() {
+
+ mutex = NULL;
+ thread=NULL;
+
+};
+
+AudioDriverDummy::~AudioDriverDummy() {
+
+};
+
+
diff --git a/servers/audio/audio_driver_dummy.h b/servers/audio/audio_driver_dummy.h
new file mode 100644
index 000000000..6784edf59
--- /dev/null
+++ b/servers/audio/audio_driver_dummy.h
@@ -0,0 +1,76 @@
+/*************************************************************************/
+/* audio_driver_dummy.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#ifndef AUDIO_DRIVER_DUMMY_H
+#define AUDIO_DRIVER_DUMMY_H
+
+#include "servers/audio/audio_server_sw.h"
+
+#include "core/os/thread.h"
+#include "core/os/mutex.h"
+
+
+class AudioDriverDummy : public AudioDriverSW {
+
+ Thread* thread;
+ Mutex* mutex;
+
+ int32_t* samples_in;
+
+ static void thread_func(void* p_udata);
+ int buffer_size;
+
+ unsigned int mix_rate;
+ OutputFormat output_format;
+
+ int channels;
+
+ bool active;
+ bool thread_exited;
+ mutable bool exit_thread;
+ bool pcm_open;
+
+public:
+
+ const char* get_name() const {
+ return "Dummy";
+ };
+
+ virtual Error init();
+ virtual void start();
+ virtual int get_mix_rate() const;
+ virtual OutputFormat get_output_format() const;
+ virtual void lock();
+ virtual void unlock();
+ virtual void finish();
+
+ AudioDriverDummy();
+ ~AudioDriverDummy();
+};
+
+#endif
diff --git a/servers/audio/audio_filter_sw.cpp b/servers/audio/audio_filter_sw.cpp
new file mode 100644
index 000000000..5d8750ffd
--- /dev/null
+++ b/servers/audio/audio_filter_sw.cpp
@@ -0,0 +1,286 @@
+/*************************************************************************/
+/* audio_filter_sw.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#include "audio_filter_sw.h"
+
+void AudioFilterSW::set_mode(Mode p_mode) {
+
+ mode = p_mode;
+}
+void AudioFilterSW::set_cutoff(float p_cutoff) {
+
+ cutoff=p_cutoff;
+}
+void AudioFilterSW::set_resonance(float p_resonance) {
+
+ resonance=p_resonance;
+}
+
+void AudioFilterSW::set_gain(float p_gain) {
+
+ gain=p_gain;
+}
+
+void AudioFilterSW::set_sampling_rate(float p_srate) {
+
+ sampling_rate=p_srate;
+}
+
+
+void AudioFilterSW::prepare_coefficients(Coeffs *p_coeffs) {
+
+ int sr_limit = (sampling_rate/2)+512;
+
+
+ double final_cutoff=(cutoff>sr_limit)?sr_limit:cutoff;
+ if (final_cutoff<1) //avoid crapness
+ final_cutoff=1; //dont allow less than this
+
+
+
+ double omega=2.0*Math_PI*final_cutoff/sampling_rate;
+
+ double sin_v=Math::sin(omega);
+ double cos_v=Math::cos(omega);
+
+ double Q=resonance;
+ if (Q<=0.0) {
+ Q=0.0001;
+ }
+
+
+ if (mode==BANDPASS)
+ Q*=2.0;
+ else if (mode==PEAK)
+ Q*=3.0;
+
+ double tmpgain=gain;
+
+ if (tmpgain<0.001)
+ tmpgain=0.001;
+
+ if (stages>1) {
+
+ Q=(Q>1.0 ? Math::pow(Q,1.0/stages) : Q);
+ tmpgain = Math::pow(tmpgain,1.0/(stages+1));
+ }
+ double alpha = sin_v/(2*Q);
+
+ double a0 = 1.0 + alpha;
+
+ switch (mode) {
+
+ case LOWPASS: {
+
+ p_coeffs->b0= (1.0 - cos_v)/2.0 ;
+ p_coeffs->b1= 1.0 - cos_v ;
+ p_coeffs->b2= (1.0 - cos_v)/2.0 ;
+ p_coeffs->a1= -2.0*cos_v;
+ p_coeffs->a2= 1.0 - alpha ;
+ } break;
+
+
+ case HIGHPASS: {
+
+ p_coeffs->b0 = (1.0 + cos_v)/2.0;
+ p_coeffs->b1 = -(1.0 + cos_v);
+ p_coeffs->b2 = (1.0 + cos_v)/2.0;
+ p_coeffs->a1 = -2.0*cos_v;
+ p_coeffs->a2 = 1.0 - alpha;
+ } break;
+
+ case BANDPASS: {
+
+ p_coeffs->b0 = alpha*sqrt(Q+1);
+ p_coeffs->b1 = 0.0 ;
+ p_coeffs->b2 = -alpha*sqrt(Q+1);
+ p_coeffs->a1 = -2.0*cos_v;
+ p_coeffs->a2 = 1.0 - alpha;
+ } break;
+
+ case NOTCH: {
+
+ p_coeffs->b0 = 1.0;
+ p_coeffs->b1 = -2.0*cos_v;
+ p_coeffs->b2 = 1.0;
+ p_coeffs->a1 = -2.0*cos_v;
+ p_coeffs->a2 = 1.0 - alpha;
+ } break;
+ case PEAK: {
+ p_coeffs->b0 = (1.0+alpha*tmpgain);
+ p_coeffs->b1 = (-2.0*cos_v);
+ p_coeffs->b2 = (1.0-alpha*tmpgain);
+ p_coeffs->a1 = -2*cos_v;
+ p_coeffs->a2 = (1-alpha/tmpgain);
+ } break;
+ case BANDLIMIT: {
+ //this one is extra tricky
+ double hicutoff=resonance;
+ double centercutoff = (cutoff+resonance)/2.0;
+ double bandwidth=(Math::log(centercutoff)-Math::log(hicutoff))/Math::log(2);
+ omega=2.0*Math_PI*centercutoff/sampling_rate;
+ alpha = Math::sin(omega)*Math::sinh( Math::log(2)/2 * bandwidth * omega/Math::sin(omega) );
+ a0=1+alpha;
+
+ p_coeffs->b0 = alpha;
+ p_coeffs->b1 = 0;
+ p_coeffs->b2 = -alpha;
+ p_coeffs->a1 = -2*Math::cos(omega);
+ p_coeffs->a2 = 1-alpha;
+
+ } break;
+ case LOWSHELF: {
+
+ double tmpq = Math::sqrt(Q);
+ if (tmpq<=0)
+ tmpq=0.001;
+ alpha = sin_v / (2 * tmpq);
+ double beta = Math::sqrt(tmpgain) / tmpq;
+
+ a0=(tmpgain+1.0)+(tmpgain-1.0)*cos_v+beta*sin_v;
+ p_coeffs->b0=tmpgain*((tmpgain+1.0)-(tmpgain-1.0)*cos_v+beta*sin_v);
+ p_coeffs->b1=2.0*tmpgain*((tmpgain-1.0)-(tmpgain+1.0)*cos_v);
+ p_coeffs->b2=tmpgain*((tmpgain+1.0)-(tmpgain-1.0)*cos_v-beta*sin_v);
+ p_coeffs->a1=-2.0*((tmpgain-1.0)+(tmpgain+1.0)*cos_v);
+ p_coeffs->a2=((tmpgain+1.0)+(tmpgain-1.0)*cos_v-beta*sin_v);
+
+ } break;
+ case HIGHSHELF: {
+ double tmpq= Math::sqrt(Q);
+ if (tmpq<=0)
+ tmpq=0.001;
+ alpha = sin_v / (2 * tmpq);
+ double beta = Math::sqrt(tmpgain) / tmpq;
+
+ a0=(tmpgain+1.0)-(tmpgain-1.0)*cos_v+beta*sin_v;
+ p_coeffs->b0=tmpgain*((tmpgain+1.0)+(tmpgain-1.0)*cos_v+beta*sin_v);
+ p_coeffs->b1=-2.0*tmpgain*((tmpgain-1.0)+(tmpgain+1.0)*cos_v);
+ p_coeffs->b2=tmpgain*((tmpgain+1.0)+(tmpgain-1.0)*cos_v-beta*sin_v);
+ p_coeffs->a1=2.0*((tmpgain-1.0)-(tmpgain+1.0)*cos_v);
+ p_coeffs->a2=((tmpgain+1.0)-(tmpgain-1.0)*cos_v-beta*sin_v);
+
+
+ } break;
+
+ };
+
+ p_coeffs->b0/=a0;
+ p_coeffs->b1/=a0;
+ p_coeffs->b2/=a0;
+ p_coeffs->a1/=0.0-a0;
+ p_coeffs->a2/=0.0-a0;
+
+ //undenormalise
+/* p_coeffs->b0=undenormalise(p_coeffs->b0);
+ p_coeffs->b1=undenormalise(p_coeffs->b1);
+ p_coeffs->b2=undenormalise(p_coeffs->b2);
+ p_coeffs->a1=undenormalise(p_coeffs->a1);
+ p_coeffs->a2=undenormalise(p_coeffs->a2);*/
+
+}
+
+void AudioFilterSW::set_stages(int p_stages) { //adjust for multiple stages
+
+ stages=p_stages;
+}
+
+/* Fouriertransform kernel to obtain response */
+
+float AudioFilterSW::get_response(float p_freq,Coeffs *p_coeffs) {
+
+ float freq=p_freq / sampling_rate * Math_PI * 2.0f;
+
+ float cx=p_coeffs->b0,cy=0.0;
+
+ cx += cos(freq) * p_coeffs->b1;
+ cy -= sin(freq) * p_coeffs->b1;
+ cx += cos(2*freq) * p_coeffs->b2;
+ cy -= sin(2*freq) * p_coeffs->b2;
+
+
+ float H=cx*cx+cy*cy;
+ cx=1.0;
+ cy=0.0;
+
+
+ cx -= cos(freq) * p_coeffs->a1;
+ cy += sin(freq) * p_coeffs->a1;
+ cx -= cos(2*freq) * p_coeffs->a2;
+ cy += sin(2*freq) * p_coeffs->a2;
+
+
+ H=H/(cx*cx+cy*cy);
+ return H;
+}
+
+
+AudioFilterSW::AudioFilterSW() {
+
+
+ sampling_rate=44100;
+ resonance=0.5;
+ cutoff=5000;
+ gain=1.0;
+ mode=LOWPASS;
+ stages=1;
+}
+
+AudioFilterSW::Processor::Processor() {
+
+ set_filter(NULL);
+
+}
+
+void AudioFilterSW::Processor::set_filter(AudioFilterSW * p_filter) {
+
+ ha1=ha2=hb1=hb2=0;
+ filter=p_filter;
+}
+
+void AudioFilterSW::Processor::update_coeffs() {
+
+ if (!filter)
+ return;
+
+ filter->prepare_coefficients(&coeffs);
+
+}
+
+void AudioFilterSW::Processor::process(float *p_samples,int p_amount, int p_stride) {
+
+ if (!filter)
+ return;
+
+ for (int i=0;i<p_amount;i++) {
+
+ process_one(*p_samples);
+ p_samples+=p_stride;
+ }
+
+
+}
diff --git a/servers/audio/audio_filter_sw.h b/servers/audio/audio_filter_sw.h
new file mode 100644
index 000000000..52315af68
--- /dev/null
+++ b/servers/audio/audio_filter_sw.h
@@ -0,0 +1,119 @@
+/*************************************************************************/
+/* audio_filter_sw.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#ifndef AUDIO_FILTER_SW_H
+#define AUDIO_FILTER_SW_H
+
+
+#include "math_funcs.h"
+
+class AudioFilterSW {
+public:
+
+ struct Coeffs {
+
+ float a1,a2;
+ float b0,b1,b2;
+
+ //bool operator==(const Coeffs &p_rv) { return (FLOATS_EQ(a1,p_rv.a1) && FLOATS_EQ(a2,p_rv.a2) && FLOATS_EQ(b1,p_rv.b1) && FLOATS_EQ(b2,p_rv.b2) && FLOATS_EQ(b0,p_rv.b0) ); }
+ Coeffs() { a1=a2=b0=b1=b2=0.0; }
+ };
+
+ enum Mode {
+ BANDPASS,
+ HIGHPASS,
+ LOWPASS,
+ NOTCH,
+ PEAK,
+ BANDLIMIT,
+ LOWSHELF,
+ HIGHSHELF
+
+ };
+
+ class Processor { // simple filter processor
+
+ AudioFilterSW * filter;
+ Coeffs coeffs;
+ float ha1,ha2,hb1,hb2; //history
+ public:
+ void set_filter(AudioFilterSW * p_filter);
+ void process(float *p_samples,int p_amount, int p_stride=1);
+ void update_coeffs();
+ inline void process_one(float& p_sample);
+
+ Processor();
+ };
+
+private:
+
+
+ float cutoff;
+ float resonance;
+ float gain;
+ float sampling_rate;
+ int stages;
+ Mode mode;
+
+
+
+public:
+
+ float get_response(float p_freq,Coeffs *p_coeffs);
+
+ void set_mode(Mode p_mode);
+ void set_cutoff(float p_cutoff);
+ void set_resonance(float p_resonance);
+ void set_gain(float p_gain);
+ void set_sampling_rate(float p_srate);
+ void set_stages(int p_stages); //adjust for multiple stages
+
+ void prepare_coefficients(Coeffs *p_coeffs);
+
+ AudioFilterSW();
+
+};
+
+
+
+
+/* inline methods */
+
+
+void AudioFilterSW::Processor::process_one(float &p_val) {
+
+ float pre=p_val;
+ p_val = (p_val * coeffs.b0 + hb1 * coeffs.b1 + hb2 * coeffs.b2 + ha1 * coeffs.a1 + ha2 * coeffs.a2);
+ ha2=ha1;
+ hb2=hb1;
+ hb1=pre;
+ ha1=p_val;
+}
+
+
+#endif // AUDIO_FILTER_SW_H
diff --git a/servers/audio/audio_mixer_sw.cpp b/servers/audio/audio_mixer_sw.cpp
new file mode 100644
index 000000000..2ca0c5e93
--- /dev/null
+++ b/servers/audio/audio_mixer_sw.cpp
@@ -0,0 +1,1085 @@
+/*************************************************************************/
+/* audio_mixer_sw.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#include "audio_mixer_sw.h"
+#include "print_string.h"
+#include "os/os.h"
+//TODO implement FAST_AUDIO macro
+
+#ifdef FAST_AUDIO
+#define NO_REVERB
+#endif
+
+template<class Depth,bool is_stereo,bool use_filter,bool use_fx,AudioMixerSW::InterpolationType type,AudioMixerSW::MixChannels mix_mode>
+void AudioMixerSW::do_resample(const Depth* p_src, int32_t *p_dst, ResamplerState *p_state) {
+
+ // this function will be compiled branchless by any decent compiler
+
+ int32_t final,final_r,next,next_r;
+ int32_t *reverb_dst = p_state->reverb_buffer;
+ while (p_state->amount--) {
+
+ int32_t pos=p_state->pos >> MIX_FRAC_BITS;
+ if (is_stereo)
+ pos<<=1;
+
+ final=p_src[pos];
+ if (is_stereo)
+ final_r=p_src[pos+1];
+
+ if (sizeof(Depth)==1) { /* conditions will not exist anymore when compiled! */
+ final<<=8;
+ if (is_stereo)
+ final_r<<=8;
+ }
+
+ if (type==INTERPOLATION_LINEAR) {
+
+ if (is_stereo) {
+
+ next=p_src[pos+2];
+ next_r=p_src[pos+3];
+ } else {
+ next=p_src[pos+1];
+ }
+
+ if (sizeof(Depth)==1) {
+ next<<=8;
+ if (is_stereo)
+ next_r<<=8;
+ }
+
+ int32_t frac=int32_t(p_state->pos&MIX_FRAC_MASK);
+
+ final=final+((next-final)*frac >> MIX_FRAC_BITS);
+ if (is_stereo)
+ final_r=final_r+((next_r-final_r)*frac >> MIX_FRAC_BITS);
+ }
+
+ if (use_filter) {
+
+ Channel::Mix::Filter *f = p_state->filter_l;
+ float finalf=final;
+ float pre = finalf;
+ finalf = ((finalf*p_state->coefs.b0) + (f->hb[0]*p_state->coefs.b1) + (f->hb[1]*p_state->coefs.b2) + (f->ha[0]*p_state->coefs.a1) + (f->ha[1]*p_state->coefs.a2)
+ );
+
+ f->ha[1]=f->ha[0];
+ f->hb[1]=f->hb[0];
+ f->hb[0]=pre;
+ f->ha[0]=finalf;
+
+ final=Math::fast_ftoi(finalf);
+
+ if (is_stereo) {
+
+ f = p_state->filter_r;
+ finalf=final_r;
+ pre = finalf;
+ finalf = ((finalf*p_state->coefs.b0) + (f->hb[0]*p_state->coefs.b1) + (f->hb[1]*p_state->coefs.b2) + (f->ha[0]*p_state->coefs.a1) + (f->ha[1]*p_state->coefs.a2)
+ );
+ f->ha[1]=f->ha[0];
+ f->hb[1]=f->hb[0];
+ f->hb[0]=pre;
+ f->ha[0]=finalf;
+
+ final_r=Math::fast_ftoi(finalf);
+
+ }
+
+ p_state->coefs.b0+=p_state->coefs_inc.b0;
+ p_state->coefs.b1+=p_state->coefs_inc.b1;
+ p_state->coefs.b2+=p_state->coefs_inc.b2;
+ p_state->coefs.a1+=p_state->coefs_inc.a1;
+ p_state->coefs.a2+=p_state->coefs_inc.a2;
+ }
+
+ if (!is_stereo) {
+ final_r=final; //copy to right channel if stereo
+ }
+
+ //convert back to 24 bits and mix to buffers
+
+ if (mix_mode==MIX_STEREO) {
+ *p_dst++ +=(final*(p_state->vol[0]>>MIX_VOLRAMP_FRAC_BITS))>>MIX_VOL_MOVE_TO_24;
+ *p_dst++ +=(final_r*(p_state->vol[1]>>MIX_VOLRAMP_FRAC_BITS))>>MIX_VOL_MOVE_TO_24;
+
+ p_state->vol[0]+=p_state->vol_inc[0];
+ p_state->vol[1]+=p_state->vol_inc[1];
+
+ if (use_fx) {
+ *reverb_dst++ +=(final*(p_state->reverb_vol[0]>>MIX_VOLRAMP_FRAC_BITS))>>MIX_VOL_MOVE_TO_24;
+ *reverb_dst++ +=(final_r*(p_state->reverb_vol[1]>>MIX_VOLRAMP_FRAC_BITS))>>MIX_VOL_MOVE_TO_24;
+ p_state->reverb_vol[0]+=p_state->reverb_vol_inc[0];
+ p_state->reverb_vol[1]+=p_state->reverb_vol_inc[1];
+ }
+
+
+ } else if (mix_mode==MIX_QUAD) {
+
+ *p_dst++ +=(final*(p_state->vol[0]>>MIX_VOLRAMP_FRAC_BITS))>>MIX_VOL_MOVE_TO_24;
+ *p_dst++ +=(final_r*(p_state->vol[1]>>MIX_VOLRAMP_FRAC_BITS))>>MIX_VOL_MOVE_TO_24;
+
+ *p_dst++ +=(final*(p_state->vol[2]>>MIX_VOLRAMP_FRAC_BITS))>>MIX_VOL_MOVE_TO_24;
+ *p_dst++ +=(final_r*(p_state->vol[3]>>MIX_VOLRAMP_FRAC_BITS))>>MIX_VOL_MOVE_TO_24;
+
+ p_state->vol[0]+=p_state->vol_inc[0];
+ p_state->vol[1]+=p_state->vol_inc[1];
+ p_state->vol[2]+=p_state->vol_inc[2];
+ p_state->vol[3]+=p_state->vol_inc[3];
+
+ if (use_fx) {
+ *reverb_dst++ +=(final*(p_state->reverb_vol[0]>>MIX_VOLRAMP_FRAC_BITS))>>MIX_VOL_MOVE_TO_24;
+ *reverb_dst++ +=(final_r*(p_state->reverb_vol[1]>>MIX_VOLRAMP_FRAC_BITS))>>MIX_VOL_MOVE_TO_24;
+ *reverb_dst++ +=(final*(p_state->reverb_vol[2]>>MIX_VOLRAMP_FRAC_BITS))>>MIX_VOL_MOVE_TO_24;
+ *reverb_dst++ +=(final_r*(p_state->reverb_vol[3]>>MIX_VOLRAMP_FRAC_BITS))>>MIX_VOL_MOVE_TO_24;
+ p_state->reverb_vol[0]+=p_state->reverb_vol_inc[0];
+ p_state->reverb_vol[1]+=p_state->reverb_vol_inc[1];
+ p_state->reverb_vol[2]+=p_state->reverb_vol_inc[2];
+ p_state->reverb_vol[3]+=p_state->reverb_vol_inc[3];
+ }
+ }
+
+ p_state->pos+=p_state->increment;
+ }
+}
+
+
+void AudioMixerSW::mix_channel(Channel& c) {
+
+
+ if (!sample_manager->is_sample(c.sample)) {
+ // sample is gone!
+ c.active=false;
+ return;
+ }
+
+
+ /* some 64-bit fixed point precaches */
+
+ int64_t loop_begin_fp=((int64_t)sample_manager->sample_get_loop_begin(c.sample) << MIX_FRAC_BITS);
+ int64_t loop_end_fp=((int64_t)sample_manager->sample_get_loop_end(c.sample) << MIX_FRAC_BITS);
+ int64_t length_fp=((int64_t)sample_manager->sample_get_length(c.sample) << MIX_FRAC_BITS);
+ int64_t begin_limit=(sample_manager->sample_get_loop_format(c.sample)!=AS::SAMPLE_LOOP_NONE)?loop_begin_fp:0;
+ int64_t end_limit=(sample_manager->sample_get_loop_format(c.sample)!=AS::SAMPLE_LOOP_NONE)?loop_end_fp:length_fp;
+ bool is_stereo=sample_manager->sample_is_stereo(c.sample);
+
+ int32_t todo=mix_chunk_size;
+// int mixed=0;
+ bool use_filter=false;
+
+ ResamplerState rstate;
+
+ /* compute voume ramps, increment, etc */
+
+
+
+ for(int i=0;i<mix_channels;i++) {
+ c.mix.old_vol[i]=c.mix.vol[i];
+ c.mix.old_reverb_vol[i]=c.mix.reverb_vol[i];
+ c.mix.old_chorus_vol[i]=c.mix.chorus_vol[i];
+ }
+
+ float vol = c.vol*channel_nrg;
+
+ float reverb_vol = c.reverb_send*channel_nrg;
+ float chorus_vol = c.chorus_send*channel_nrg;
+
+ if (mix_channels==2) {
+ //stereo pan
+ float pan = c.pan * 0.5 + 0.5;
+ float panv[2]={
+ (1.0 - pan)*(1<<MIX_VOL_FRAC_BITS),
+ (pan)*(1<<MIX_VOL_FRAC_BITS)
+ };
+
+ for(int i=0;i<2;i++) {
+
+ c.mix.vol[i]=Math::fast_ftoi(vol*panv[i]);
+ c.mix.reverb_vol[i]=Math::fast_ftoi(reverb_vol*panv[i]);
+ c.mix.chorus_vol[i]=Math::fast_ftoi(chorus_vol*panv[i]);
+ }
+
+ } else {
+ //qudra pan
+ float panx = c.pan * 0.5 + 0.5;
+ float pany = c.depth * 0.5 + 0.5;
+ // with this model every speaker plays at 0.25 energy at the center.. i'm not sure if it's correct but it seems to be balanced
+ float panv[4]={
+ (1.0-pany)*(1.0-panx)*(1<<MIX_VOL_FRAC_BITS),
+ (1.0-pany)*( panx)*(1<<MIX_VOL_FRAC_BITS),
+ ( pany)*(1.0-panx)*(1<<MIX_VOL_FRAC_BITS),
+ ( pany)*( panx)*(1<<MIX_VOL_FRAC_BITS)
+ };
+
+ for(int i=0;i<4;i++) {
+
+ c.mix.vol[i]=Math::fast_ftoi(vol*panv[i]);
+ c.mix.reverb_vol[i]=Math::fast_ftoi(reverb_vol*panv[i]);
+ c.mix.chorus_vol[i]=Math::fast_ftoi(chorus_vol*panv[i]);
+ }
+
+ }
+
+ if (c.first_mix) { // avoid ramp up
+
+ for(int i=0;i<mix_channels;i++) {
+ c.mix.old_vol[i]=c.mix.vol[i];
+ c.mix.old_reverb_vol[i]=c.mix.reverb_vol[i];
+ c.mix.old_chorus_vol[i]=c.mix.chorus_vol[i];
+ }
+
+ c.first_mix=false;
+ }
+
+
+
+ Channel::Filter::Coefs filter_coefs;
+ Channel::Filter::Coefs filter_inc;
+
+ if (c.filter.type!=AudioMixer::FILTER_NONE) {
+
+ filter_coefs=c.filter.old_coefs;
+ filter_inc.b0=(c.filter.coefs.b0-filter_coefs.b0)/(1<<mix_chunk_bits);
+ filter_inc.b1=(c.filter.coefs.b1-filter_coefs.b1)/(1<<mix_chunk_bits);
+ filter_inc.b2=(c.filter.coefs.b2-filter_coefs.b2)/(1<<mix_chunk_bits);
+ filter_inc.a1=(c.filter.coefs.a1-filter_coefs.a1)/(1<<mix_chunk_bits);
+ filter_inc.a2=(c.filter.coefs.a2-filter_coefs.a2)/(1<<mix_chunk_bits);
+ use_filter=true;
+ }
+
+ if (c.mix.increment>0)
+ c.mix.increment=((int64_t)c.speed<<MIX_FRAC_BITS)/mix_rate;
+ else
+ c.mix.increment=-((int64_t)c.speed<<MIX_FRAC_BITS)/mix_rate;
+
+ //volume ramp
+
+
+ for(int i=0;i<mix_channels;i++) {
+ rstate.vol_inc[i]=((c.mix.vol[i]-c.mix.old_vol[i])<<MIX_VOLRAMP_FRAC_BITS)>>mix_chunk_bits;
+ rstate.vol[i]=c.mix.old_vol[i]<<MIX_VOLRAMP_FRAC_BITS;
+ rstate.reverb_vol_inc[i]=((c.mix.reverb_vol[i]-c.mix.old_reverb_vol[i])<<MIX_VOLRAMP_FRAC_BITS)>>mix_chunk_bits;
+ rstate.reverb_vol[i]=c.mix.old_reverb_vol[i]<<MIX_VOLRAMP_FRAC_BITS;
+ rstate.chorus_vol_inc[i]=((c.mix.chorus_vol[i]-c.mix.old_chorus_vol[i])<<MIX_VOLRAMP_FRAC_BITS)>>mix_chunk_bits;
+ rstate.chorus_vol[i]=c.mix.old_chorus_vol[i]<<MIX_VOLRAMP_FRAC_BITS;
+ }
+
+
+ //looping
+
+ AS::SampleLoopFormat loop_format=sample_manager->sample_get_loop_format(c.sample);
+ AS::SampleFormat format=sample_manager->sample_get_format(c.sample);
+
+ bool use_fx=fx_enabled && (c.mix.old_reverb_vol || c.mix.reverb_vol || c.mix.old_chorus_vol || c.mix.chorus_vol );
+
+ /* audio data */
+
+ const void *data=sample_manager->sample_get_data_ptr(c.sample);
+ int32_t *dst_buff=mix_buffer;
+
+#ifndef NO_REVERB
+ rstate.reverb_buffer=reverb_state[c.reverb_room].buffer;
+#endif
+
+ /* @TODO validar loops al registrar? */
+
+ rstate.coefs=filter_coefs;
+ rstate.coefs_inc=filter_inc;
+ rstate.filter_l=&c.mix.filter_l;
+ rstate.filter_r=&c.mix.filter_r;
+
+ while (todo>0) {
+
+ int64_t limit=0;
+ int32_t target=0,aux=0;
+
+ /** LOOP CHECKING **/
+
+ if ( c.mix.increment < 0 ) {
+ /* going backwards */
+
+ if ( loop_format!=AS::SAMPLE_LOOP_NONE && c.mix.offset < loop_begin_fp ) {
+ /* loopstart reached */
+ if ( loop_format==AS::SAMPLE_LOOP_PING_PONG ) {
+ /* bounce ping pong */
+ c.mix.offset= loop_begin_fp + ( loop_begin_fp-c.mix.offset );
+ c.mix.increment=-c.mix.increment;
+ } else {
+ /* go to loop-end */
+ c.mix.offset=loop_end_fp-(loop_begin_fp-c.mix.offset);
+ }
+ } else {
+ /* check for sample not reaching begining */
+ if(c.mix.offset < 0) {
+
+ c.active=false;
+ break;
+ }
+ }
+ } else {
+ /* going forward */
+ if( loop_format!=AS::SAMPLE_LOOP_NONE && c.mix.offset >= loop_end_fp ) {
+ /* loopend reached */
+
+ if ( loop_format==AS::SAMPLE_LOOP_PING_PONG ) {
+ /* bounce ping pong */
+ c.mix.offset=loop_end_fp-(c.mix.offset-loop_end_fp);
+ c.mix.increment=-c.mix.increment;
+ } else {
+ /* go to loop-begin */
+
+ c.mix.offset=loop_begin_fp+(c.mix.offset-loop_end_fp);
+
+ }
+ } else {
+ /* no loop, check for end of sample */
+ if(c.mix.offset >= length_fp) {
+
+ c.active=false;
+ break;
+ }
+ }
+ }
+
+ /** MIXCOUNT COMPUTING **/
+
+ /* next possible limit (looppoints or sample begin/end */
+ limit=(c.mix.increment < 0) ?begin_limit:end_limit;
+
+ /* compute what is shorter, the todo or the limit? */
+ aux=(limit-c.mix.offset)/c.mix.increment+1;
+ target=(aux<todo)?aux:todo; /* mix target is the shorter buffer */
+
+ /* check just in case */
+ if ( target<=0 ) {
+ c.active=false;
+ break;
+ }
+
+ todo-=target;
+
+ int32_t offset=c.mix.offset&mix_chunk_mask; /* strip integer */
+ c.mix.offset-=offset;
+
+ rstate.increment=c.mix.increment;
+ rstate.amount=target;
+ rstate.pos=offset;
+
+/* Macros to call the resample function for all possibilities, creating a dedicated-non branchy function call for each thanks to template magic*/
+
+#define CALL_RESAMPLE_FUNC( m_depth, m_stereo, m_use_filter, m_use_fx, m_interp, m_mode)\
+ do_resample<m_depth,m_stereo,m_use_filter,m_use_fx,m_interp, m_mode>(\
+ src_ptr,\
+ dst_buff,&rstate);
+
+
+#define CALL_RESAMPLE_INTERP( m_depth, m_stereo, m_use_filter, m_use_fx, m_interp, m_mode)\
+ if(m_interp==INTERPOLATION_RAW) {\
+ CALL_RESAMPLE_FUNC(m_depth,m_stereo,m_use_filter,m_use_fx,INTERPOLATION_RAW,m_mode);\
+ } else if(m_interp==INTERPOLATION_LINEAR) {\
+ CALL_RESAMPLE_FUNC(m_depth,m_stereo,m_use_filter,m_use_fx,INTERPOLATION_LINEAR,m_mode);\
+ } else if(m_interp==INTERPOLATION_CUBIC) {\
+ CALL_RESAMPLE_FUNC(m_depth,m_stereo,m_use_filter,m_use_fx,INTERPOLATION_CUBIC,m_mode);\
+ }\
+
+#define CALL_RESAMPLE_FX( m_depth, m_stereo, m_use_filter, m_use_fx, m_interp, m_mode)\
+ if(m_use_fx) {\
+ CALL_RESAMPLE_INTERP(m_depth,m_stereo,m_use_filter,true,m_interp, m_mode);\
+ } else {\
+ CALL_RESAMPLE_INTERP(m_depth,m_stereo,m_use_filter,false,m_interp, m_mode);\
+ }\
+
+
+#define CALL_RESAMPLE_FILTER( m_depth, m_stereo, m_use_filter, m_use_fx, m_interp, m_mode)\
+ if(m_use_filter) {\
+ CALL_RESAMPLE_FX(m_depth,m_stereo,true,m_use_fx,m_interp, m_mode);\
+ } else {\
+ CALL_RESAMPLE_FX(m_depth,m_stereo,false,m_use_fx,m_interp, m_mode);\
+ }\
+
+#define CALL_RESAMPLE_STEREO( m_depth, m_stereo, m_use_filter, m_use_fx, m_interp, m_mode)\
+ if(m_stereo) {\
+ CALL_RESAMPLE_FILTER(m_depth,true,m_use_filter,m_use_fx,m_interp, m_mode);\
+ } else {\
+ CALL_RESAMPLE_FILTER(m_depth,false,m_use_filter,m_use_fx,m_interp, m_mode);\
+ }\
+
+#define CALL_RESAMPLE_MODE( m_depth, m_stereo, m_use_filter, m_use_fx, m_interp, m_mode)\
+ if(m_mode==MIX_STEREO) {\
+ CALL_RESAMPLE_STEREO(m_depth,m_stereo,m_use_filter,m_use_fx,m_interp, MIX_STEREO);\
+ } else {\
+ CALL_RESAMPLE_STEREO(m_depth,m_stereo,m_use_filter,m_use_fx,m_interp, MIX_QUAD);\
+ }\
+
+
+
+
+ if (format==AS::SAMPLE_FORMAT_PCM8) {
+
+ int8_t *src_ptr = &((int8_t*)data)[(c.mix.offset >> MIX_FRAC_BITS)<<(is_stereo?1:0) ];
+ CALL_RESAMPLE_MODE(int8_t,is_stereo,use_filter,use_fx,interpolation_type,mix_channels);
+
+ } else if (format==AS::SAMPLE_FORMAT_PCM16) {
+ int16_t *src_ptr = &((int16_t*)data)[(c.mix.offset >> MIX_FRAC_BITS)<<(is_stereo?1:0) ];
+ CALL_RESAMPLE_MODE(int16_t,is_stereo,use_filter,use_fx,interpolation_type,mix_channels);
+
+ }
+
+ c.mix.offset+=rstate.pos;
+ dst_buff+=target*2;
+
+ }
+
+ c.filter.old_coefs=c.filter.coefs;
+}
+
+void AudioMixerSW::mix_chunk() {
+
+ ERR_FAIL_COND(mix_chunk_left);
+
+ inside_mix=true;
+
+ // emit tick in usecs
+ for (int i=0;i<mix_chunk_size*mix_channels;i++) {
+
+ mix_buffer[i]=0;
+ }
+#ifndef NO_REVERB
+ for(int i=0;i<max_reverbs;i++)
+ reverb_state[i].used_in_chunk=false;
+#endif
+
+
+ audio_mixer_chunk_call(mix_chunk_size);
+
+ int ac=0;
+ for (int i=0;i<MAX_CHANNELS;i++) {
+
+ if (!channels[i].active)
+ continue;
+ ac++;
+
+ /* process volume */
+ Channel&c=channels[i];
+#ifndef NO_REVERB
+ bool has_reverb = c.reverb_send>CMP_EPSILON && fx_enabled;
+ if (has_reverb || c.had_prev_reverb) {
+
+ if (!reverb_state[c.reverb_room].used_in_chunk) {
+ //zero the room
+ int32_t *buff = reverb_state[c.reverb_room].buffer;
+ int len = mix_chunk_size*mix_channels;
+ for (int j=0;j<len;j++) {
+
+ buff[j]=0; // buffer in use, clear it for appending
+ }
+ reverb_state[c.reverb_room].used_in_chunk=true;
+ }
+ }
+#else
+ bool has_reverb = false;
+#endif
+ bool has_chorus = c.chorus_send>CMP_EPSILON && fx_enabled;
+
+
+ mix_channel(c);
+
+ c.had_prev_reverb=has_reverb;
+ c.had_prev_chorus=has_chorus;
+
+ }
+
+ //process reverb
+#ifndef NO_REVERB
+ if (fx_enabled) {
+
+
+ for(int i=0;i<max_reverbs;i++) {
+
+ if (!reverb_state[i].enabled && !reverb_state[i].used_in_chunk)
+ continue; //this reverb is not in use
+
+ int32_t *src=NULL;
+
+ if (reverb_state[i].used_in_chunk)
+ src=reverb_state[i].buffer;
+ else
+ src=zero_buffer;
+
+ bool in_use=false;
+
+ int passes=mix_channels/2;
+
+ for(int j=0;j<passes;j++) {
+
+ if (reverb_state[i].reverb[j].process((int*)&src[j*2],(int*)&mix_buffer[j*2],mix_chunk_size,passes))
+ in_use=true;
+ }
+
+ if (in_use) {
+ reverb_state[i].enabled=true;
+ reverb_state[i].frames_idle=0;
+ //copy data over
+
+ } else {
+ reverb_state[i].frames_idle+=mix_chunk_size;
+ if (false) { // go idle because too many frames passed
+ //disable this reverb, as nothing important happened on it
+ reverb_state[i].enabled=false;
+ reverb_state[i].frames_idle=0;
+ }
+ }
+
+ }
+ }
+#endif
+ mix_chunk_left=mix_chunk_size;
+ inside_mix=false;
+}
+
+int AudioMixerSW::mix(int32_t *p_buffer,int p_frames) {
+
+ int todo=p_frames;
+ int mixes=0;
+
+ while(todo) {
+
+
+ if (!mix_chunk_left) {
+
+ if (step_callback)
+ step_callback(step_udata);
+ mix_chunk();
+ mixes++;
+ }
+
+ int to_mix=MIN(mix_chunk_left,todo);
+ int from=mix_chunk_size-mix_chunk_left;
+
+ for (int i=0;i<to_mix*2;i++) {
+
+ (*p_buffer++)=mix_buffer[from*2+i];
+ }
+
+ mix_chunk_left-=to_mix;
+ todo-=to_mix;
+ }
+
+ return mixes;
+}
+
+uint64_t AudioMixerSW::get_step_usecs() const {
+
+ double mct = (1<<mix_chunk_bits)/double(mix_rate);
+ return mct*1000000.0;
+}
+
+int AudioMixerSW::_get_channel(ChannelID p_channel) const {
+
+ if (p_channel<0) {
+ return -1;
+ }
+
+ int idx=p_channel%MAX_CHANNELS;
+ int check=p_channel/MAX_CHANNELS;
+ ERR_FAIL_INDEX_V(idx,MAX_CHANNELS,-1);
+ if (channels[idx].check!=check) {
+ return -1;
+ }
+ if (!channels[idx].active) {
+ return -1;
+ }
+
+ return idx;
+}
+
+AudioMixer::ChannelID AudioMixerSW::channel_alloc(RID p_sample) {
+
+ ERR_FAIL_COND_V( !sample_manager->is_sample(p_sample), INVALID_CHANNEL );
+
+
+ int index=-1;
+ for (int i=0;i<MAX_CHANNELS;i++) {
+
+ if (!channels[i].active) {
+ index=i;
+ break;
+ }
+ }
+
+ if (index==-1)
+ return INVALID_CHANNEL;
+
+ Channel &c=channels[index];
+
+ // init variables
+ c.sample=p_sample;
+ c.vol=1;
+ c.pan=0;
+ c.depth=0;
+ c.height=0;
+ c.chorus_send=0;
+ c.reverb_send=0;
+ c.reverb_room=REVERB_HALL;
+ c.positional=false;
+ c.filter.type=FILTER_NONE;
+ c.speed=sample_manager->sample_get_mix_rate(p_sample);
+ c.active=true;
+ c.check=channel_id_count++;
+ c.first_mix=true;
+
+ // init mix variables
+
+ c.mix.offset=0;
+ c.mix.increment=1;
+ //zero everything when this errors
+ for(int i=0;i<4;i++) {
+ c.mix.old_vol[i]=0;
+ c.mix.old_reverb_vol[i]=0;
+ c.mix.old_chorus_vol[i]=0;
+ }
+
+ c.had_prev_chorus=false;
+ c.had_prev_reverb=false;
+ c.had_prev_vol=false;
+
+ ChannelID ret_id = index+c.check*MAX_CHANNELS;
+
+ return ret_id;
+
+}
+
+void AudioMixerSW::channel_set_volume(ChannelID p_channel, float p_gain) {
+
+ if (p_gain>3) // avoid gain going too high
+ p_gain=3;
+ if (p_gain<0)
+ p_gain=0;
+
+ int chan = _get_channel(p_channel);
+ if (chan<0 || chan >=MAX_CHANNELS)
+ return;
+ Channel &c = channels[chan];
+
+ //Math::exp( p_db * 0.11512925464970228420089957273422 );
+ c.vol=p_gain;
+
+}
+
+void AudioMixerSW::channel_set_pan(ChannelID p_channel, float p_pan, float p_depth,float p_height) {
+
+ int chan = _get_channel(p_channel);
+ if (chan<0 || chan >=MAX_CHANNELS)
+ return;
+ Channel &c = channels[chan];
+
+ c.pan=p_pan;
+ c.depth=p_depth;
+ c.height=p_height;
+
+}
+void AudioMixerSW::channel_set_filter(ChannelID p_channel, FilterType p_type, float p_cutoff, float p_resonance, float p_gain) {
+
+ int chan = _get_channel(p_channel);
+ if (chan<0 || chan >=MAX_CHANNELS)
+ return;
+
+ Channel &c = channels[chan];
+
+ if (c.filter.type==p_type && c.filter.cutoff==p_cutoff && c.filter.resonance==p_resonance && c.filter.gain==p_gain)
+ return; //bye
+
+
+ bool type_changed = p_type!=c.filter.type;
+
+ c.filter.type=p_type;
+ c.filter.cutoff=p_cutoff;
+ c.filter.resonance=p_resonance;
+ c.filter.gain=p_gain;
+
+
+ AudioFilterSW filter;
+ switch(p_type) {
+ case FILTER_NONE: {
+
+ return; //do nothing else
+ } break;
+ case FILTER_LOWPASS: {
+ filter.set_mode(AudioFilterSW::LOWPASS);
+ } break;
+ case FILTER_BANDPASS: {
+ filter.set_mode(AudioFilterSW::BANDPASS);
+ } break;
+ case FILTER_HIPASS: {
+ filter.set_mode(AudioFilterSW::HIGHPASS);
+ } break;
+ case FILTER_NOTCH: {
+ filter.set_mode(AudioFilterSW::NOTCH);
+ } break;
+ case FILTER_PEAK: {
+ filter.set_mode(AudioFilterSW::PEAK);
+ } break;
+ case FILTER_BANDLIMIT: {
+ filter.set_mode(AudioFilterSW::BANDLIMIT);
+ } break;
+ case FILTER_LOW_SHELF: {
+ filter.set_mode(AudioFilterSW::LOWSHELF);
+ } break;
+ case FILTER_HIGH_SHELF: {
+ filter.set_mode(AudioFilterSW::HIGHSHELF);
+ } break;
+ }
+
+ filter.set_cutoff(p_cutoff);
+ filter.set_resonance(p_resonance);
+ filter.set_gain(p_gain);
+ filter.set_sampling_rate(mix_rate);
+ filter.set_stages(1);
+
+ AudioFilterSW::Coeffs coefs;
+ filter.prepare_coefficients(&coefs);
+
+ if (!type_changed)
+ c.filter.old_coefs=c.filter.coefs;
+
+ c.filter.coefs.b0=coefs.b0;
+ c.filter.coefs.b1=coefs.b1;
+ c.filter.coefs.b2=coefs.b2;
+ c.filter.coefs.a1=coefs.a1;
+ c.filter.coefs.a2=coefs.a2;
+
+
+ if (type_changed) {
+ //type changed reset filter
+ c.filter.old_coefs=c.filter.coefs;
+ c.mix.filter_l.ha[0]=0;
+ c.mix.filter_l.ha[1]=0;
+ c.mix.filter_l.hb[0]=0;
+ c.mix.filter_l.hb[1]=0;
+ c.mix.filter_r.ha[0]=0;
+ c.mix.filter_r.ha[1]=0;
+ c.mix.filter_r.hb[0]=0;
+ c.mix.filter_r.hb[1]=0;
+ }
+
+
+}
+void AudioMixerSW::channel_set_chorus(ChannelID p_channel, float p_chorus ) {
+
+ int chan = _get_channel(p_channel);
+ if (chan<0 || chan >=MAX_CHANNELS)
+ return;
+
+ Channel &c = channels[chan];
+ c.chorus_send=p_chorus;
+
+}
+void AudioMixerSW::channel_set_reverb(ChannelID p_channel, ReverbRoomType p_room_type, float p_reverb) {
+
+ ERR_FAIL_INDEX(p_room_type,MAX_REVERBS);
+ int chan = _get_channel(p_channel);
+ if (chan<0 || chan >=MAX_CHANNELS)
+ return;
+
+ Channel &c = channels[chan];
+ c.reverb_room=p_room_type;
+ c.reverb_send=p_reverb;
+
+}
+
+void AudioMixerSW::channel_set_mix_rate(ChannelID p_channel, int p_mix_rate) {
+
+ int chan = _get_channel(p_channel);
+ if (chan<0 || chan >=MAX_CHANNELS)
+ return;
+
+ Channel &c = channels[chan];
+ c.speed=p_mix_rate;
+
+}
+void AudioMixerSW::channel_set_positional(ChannelID p_channel, bool p_positional) {
+
+ int chan = _get_channel(p_channel);
+ if (chan<0 || chan >=MAX_CHANNELS)
+ return;
+
+ Channel &c = channels[chan];
+ c.positional=p_positional;
+}
+
+float AudioMixerSW::channel_get_volume(ChannelID p_channel) const {
+
+ int chan = _get_channel(p_channel);
+ if (chan<0 || chan >=MAX_CHANNELS)
+ return 0;
+
+ const Channel &c = channels[chan];
+ //Math::log( c.vol ) * 8.6858896380650365530225783783321;
+ return c.vol;
+}
+
+float AudioMixerSW::channel_get_pan(ChannelID p_channel) const {
+
+ int chan = _get_channel(p_channel);
+ if (chan<0 || chan >=MAX_CHANNELS)
+ return 0;
+
+ const Channel &c = channels[chan];
+ return c.pan;
+}
+float AudioMixerSW::channel_get_pan_depth(ChannelID p_channel) const {
+
+ int chan = _get_channel(p_channel);
+ if (chan<0 || chan >=MAX_CHANNELS)
+ return 0;
+
+ const Channel &c = channels[chan];
+ return c.depth;
+}
+float AudioMixerSW::channel_get_pan_height(ChannelID p_channel) const {
+
+ int chan = _get_channel(p_channel);
+ if (chan<0 || chan >=MAX_CHANNELS)
+ return 0;
+
+ const Channel &c = channels[chan];
+ return c.height;
+
+}
+AudioMixer::FilterType AudioMixerSW::channel_get_filter_type(ChannelID p_channel) const {
+
+ int chan = _get_channel(p_channel);
+ if (chan<0 || chan >=MAX_CHANNELS)
+ return FILTER_NONE;
+
+ const Channel &c = channels[chan];
+ return c.filter.type;
+}
+float AudioMixerSW::channel_get_filter_cutoff(ChannelID p_channel) const {
+
+
+ int chan = _get_channel(p_channel);
+ if (chan<0 || chan >=MAX_CHANNELS)
+ return 0;
+
+ const Channel &c = channels[chan];
+ return c.filter.cutoff;
+
+}
+float AudioMixerSW::channel_get_filter_resonance(ChannelID p_channel) const {
+
+ int chan = _get_channel(p_channel);
+ if (chan<0 || chan >=MAX_CHANNELS)
+ return 0;
+
+ const Channel &c = channels[chan];
+ return c.filter.resonance;
+
+}
+
+float AudioMixerSW::channel_get_filter_gain(ChannelID p_channel) const {
+
+ int chan = _get_channel(p_channel);
+ if (chan<0 || chan >=MAX_CHANNELS)
+ return 0;
+
+ const Channel &c = channels[chan];
+ return c.filter.gain;
+}
+
+
+float AudioMixerSW::channel_get_chorus(ChannelID p_channel) const {
+
+ int chan = _get_channel(p_channel);
+ if (chan<0 || chan >=MAX_CHANNELS)
+ return 0;
+
+ const Channel &c = channels[chan];
+ return c.chorus_send;
+
+}
+AudioMixer::ReverbRoomType AudioMixerSW::channel_get_reverb_type(ChannelID p_channel) const {
+
+ int chan = _get_channel(p_channel);
+ if (chan<0 || chan >=MAX_CHANNELS)
+ return REVERB_HALL;
+
+ const Channel &c = channels[chan];
+ return c.reverb_room;
+
+}
+float AudioMixerSW::channel_get_reverb(ChannelID p_channel) const {
+
+ int chan = _get_channel(p_channel);
+ if (chan<0 || chan >=MAX_CHANNELS)
+ return 0;
+
+ const Channel &c = channels[chan];
+ return c.reverb_send;
+}
+
+int AudioMixerSW::channel_get_mix_rate(ChannelID p_channel) const {
+
+ int chan = _get_channel(p_channel);
+ if (chan<0 || chan >=MAX_CHANNELS)
+ return 0;
+
+ const Channel &c = channels[chan];
+ return c.speed;
+}
+bool AudioMixerSW::channel_is_positional(ChannelID p_channel) const {
+
+ int chan = _get_channel(p_channel);
+ if (chan<0 || chan >=MAX_CHANNELS)
+ return false;
+
+ const Channel &c = channels[chan];
+ return c.positional;
+}
+
+bool AudioMixerSW::channel_is_valid(ChannelID p_channel) const {
+
+ int chan = _get_channel(p_channel);
+ if (chan<0 || chan >=MAX_CHANNELS)
+ return false;
+ return channels[chan].active;
+}
+
+
+void AudioMixerSW::channel_free(ChannelID p_channel) {
+
+ int chan = _get_channel(p_channel);
+ if (chan<0 || chan >=MAX_CHANNELS)
+ return;
+
+ Channel &c=channels[chan];
+
+ if (!c.active)
+ return;
+
+ bool has_vol=false;
+
+ for(int i=0;i<mix_channels;i++) {
+
+ if (c.mix.vol[i])
+ has_vol=true;
+ if (c.mix.reverb_vol[i])
+ has_vol=true;
+ if (c.mix.chorus_vol[i])
+ has_vol=true;
+ }
+ if (c.active && has_vol && inside_mix) {
+ // drive voice to zero, and run a chunk, the VRAMP will fade it good
+ c.vol=0;
+ c.reverb_send=0;
+ c.chorus_send=0;
+ mix_channel(c);
+ }
+ /* @TODO RAMP DOWN ON STOP */
+ c.active=false;
+}
+
+
+
+AudioMixerSW::AudioMixerSW(SampleManagerSW *p_sample_manager,int p_desired_latency_ms,int p_mix_rate,MixChannels p_mix_channels,bool p_use_fx,InterpolationType p_interp,MixStepCallback p_step_callback,void *p_step_udata) {
+
+ if (OS::get_singleton()->is_stdout_verbose()) {
+ print_line("AudioServerSW Params: ");
+ print_line(" -mix chans: "+itos(p_mix_channels));
+ print_line(" -mix rate: "+itos(p_mix_rate));
+ print_line(" -latency: "+itos(p_desired_latency_ms));
+ print_line(" -fx: "+itos(p_use_fx));
+ print_line(" -interp: "+itos(p_interp));
+ }
+ sample_manager=p_sample_manager;
+ mix_channels=p_mix_channels;
+ mix_rate=p_mix_rate;
+ step_callback=p_step_callback;
+ step_udata=p_step_udata;
+
+
+ mix_chunk_bits=nearest_shift( p_desired_latency_ms * p_mix_rate / 1000 );
+
+ mix_chunk_size=(1<<mix_chunk_bits);
+ mix_chunk_mask=mix_chunk_size-1;
+ mix_buffer = memnew_arr(int32_t,mix_chunk_size*mix_channels);
+#ifndef NO_REVERB
+ zero_buffer = memnew_arr(int32_t,mix_chunk_size*mix_channels);
+ for(int i=0;i<mix_chunk_size*mix_channels;i++)
+ zero_buffer[i]=0; //zero buffer is zero...
+
+ max_reverbs=MAX_REVERBS;
+ int reverberators=mix_channels/2;
+
+ reverb_state = memnew_arr(ReverbState,max_reverbs);
+ for(int i=0;i<max_reverbs;i++) {
+ reverb_state[i].enabled=false;
+ reverb_state[i].reverb = memnew_arr(ReverbSW,reverberators);
+ reverb_state[i].buffer = memnew_arr(int32_t,mix_chunk_size*mix_channels);
+ reverb_state[i].frames_idle=0;
+ for(int j=0;j<reverberators;j++) {
+ static ReverbSW::ReverbMode modes[MAX_REVERBS]={ReverbSW::REVERB_MODE_STUDIO_SMALL,ReverbSW::REVERB_MODE_STUDIO_MEDIUM,ReverbSW::REVERB_MODE_STUDIO_LARGE,ReverbSW::REVERB_MODE_HALL};
+ reverb_state[i].reverb[j].set_mix_rate(p_mix_rate);
+ reverb_state[i].reverb[j].set_mode(modes[i]);
+ }
+
+ }
+ fx_enabled=p_use_fx;
+#else
+ fx_enabled=false;
+#endif
+ mix_chunk_left=0;
+
+ interpolation_type=p_interp;
+ channel_id_count=1;
+ inside_mix=false;
+ channel_nrg=1.0;
+
+}
+
+void AudioMixerSW::set_mixer_volume(float p_volume) {
+
+ channel_nrg=p_volume;
+}
+
+AudioMixerSW::~AudioMixerSW() {
+
+ memdelete_arr(mix_buffer);
+
+#ifndef NO_REVERB
+ memdelete_arr(zero_buffer);
+ for(int i=0;i<max_reverbs;i++) {
+ memdelete_arr(reverb_state[i].reverb);
+ memdelete_arr(reverb_state[i].buffer);
+ }
+ memdelete_arr(reverb_state);
+#endif
+
+
+}
diff --git a/servers/audio/audio_mixer_sw.h b/servers/audio/audio_mixer_sw.h
new file mode 100644
index 000000000..eb3feee1c
--- /dev/null
+++ b/servers/audio/audio_mixer_sw.h
@@ -0,0 +1,248 @@
+/*************************************************************************/
+/* audio_mixer_sw.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#ifndef AUDIO_MIXER_SW_H
+#define AUDIO_MIXER_SW_H
+
+#include "servers/audio_server.h"
+#include "servers/audio/sample_manager_sw.h"
+#include "servers/audio/audio_filter_sw.h"
+#include "servers/audio/reverb_sw.h"
+
+class AudioMixerSW : public AudioMixer {
+public:
+
+ enum InterpolationType {
+
+ INTERPOLATION_RAW,
+ INTERPOLATION_LINEAR,
+ INTERPOLATION_CUBIC
+ };
+
+ enum MixChannels {
+
+ MIX_STEREO=2,
+ MIX_QUAD=4
+ };
+
+ typedef void (*MixStepCallback)(void*);
+
+private:
+ SampleManagerSW *sample_manager;
+
+ enum {
+
+ MAX_CHANNELS=64,
+ // fixed point defs
+
+ MIX_FRAC_BITS=13,
+ MIX_FRAC_LEN=(1<<MIX_FRAC_BITS),
+ MIX_FRAC_MASK=MIX_FRAC_LEN-1,
+ MIX_VOL_FRAC_BITS=12,
+ MIX_VOLRAMP_FRAC_BITS=16,
+ MIX_VOLRAMP_FRAC_LEN=(1<<MIX_VOLRAMP_FRAC_BITS),
+ MIX_VOLRAMP_FRAC_MASK=MIX_VOLRAMP_FRAC_LEN-1,
+ MIX_FILTER_FRAC_BITS=16,
+ MIX_FILTER_RAMP_FRAC_BITS=8,
+ MIX_VOL_MOVE_TO_24=4,
+ MAX_REVERBS=4
+ };
+
+ struct Channel {
+
+ RID sample;
+ struct Mix {
+ int64_t offset;
+ int32_t increment;
+
+ int32_t vol[4];
+ int32_t reverb_vol[4];
+ int32_t chorus_vol[4];
+
+ int32_t old_vol[4];
+ int32_t old_reverb_vol[4];
+ int32_t old_chorus_vol[4];
+
+
+ struct Filter { //history (stereo)
+ float ha[2],hb[2];
+ } filter_l,filter_r;
+
+ } mix;
+
+ float vol;
+ float pan;
+ float depth;
+ float height;
+
+ float chorus_send;
+ ReverbRoomType reverb_room;
+ float reverb_send;
+ int speed;
+ int check;
+ bool positional;
+
+ bool had_prev_reverb;
+ bool had_prev_chorus;
+ bool had_prev_vol;
+
+ struct Filter {
+
+ bool dirty;
+
+ FilterType type;
+ float cutoff;
+ float resonance;
+ float gain;
+
+ struct Coefs {
+
+ float a1,a2,b0,b1,b2; // fixed point coefficients
+ } coefs,old_coefs;
+
+ } filter;
+
+ bool first_mix;
+ bool active;
+ Channel() { active=false; check=-1; first_mix=false; filter.dirty=true; filter.type=FILTER_NONE; filter.cutoff=8000; filter.resonance=0; filter.gain=0; }
+ };
+
+ Channel channels[MAX_CHANNELS];
+
+ uint32_t mix_rate;
+ bool fx_enabled;
+ InterpolationType interpolation_type;
+
+ int mix_chunk_bits;
+ int mix_chunk_size;
+ int mix_chunk_mask;
+
+ int32_t *mix_buffer;
+ int32_t *zero_buffer; // fx feed when no input was mixed
+
+ struct ResamplerState {
+
+ uint32_t amount;
+ int32_t increment;
+
+
+ int32_t pos;
+
+
+ int32_t vol[4];
+ int32_t reverb_vol[4];
+ int32_t chorus_vol[4];
+
+ int32_t vol_inc[4];
+ int32_t reverb_vol_inc[4];
+ int32_t chorus_vol_inc[4];
+
+
+ Channel::Mix::Filter *filter_l;
+ Channel::Mix::Filter *filter_r;
+ Channel::Filter::Coefs coefs;
+ Channel::Filter::Coefs coefs_inc;
+
+ int32_t *reverb_buffer;
+ };
+
+
+
+ template<class Depth,bool is_stereo,bool use_filter,bool use_fx,InterpolationType type,MixChannels>
+ _FORCE_INLINE_ void do_resample(const Depth* p_src, int32_t *p_dst, ResamplerState *p_state);
+
+ MixChannels mix_channels;
+
+ void mix_channel(Channel& p_channel);
+ int mix_chunk_left;
+ void mix_chunk();
+
+ float channel_nrg;
+ int channel_id_count;
+ bool inside_mix;
+ MixStepCallback step_callback;
+ void *step_udata;
+ _FORCE_INLINE_ int _get_channel(ChannelID p_channel) const;
+
+ int max_reverbs;
+ struct ReverbState {
+
+ bool used_in_chunk;
+ bool enabled;
+ ReverbSW *reverb;
+ int frames_idle;
+ int32_t *buffer; //reverb is sent here
+ ReverbState() { enabled=false; frames_idle=0; used_in_chunk=false; }
+ };
+
+ ReverbState *reverb_state;
+
+
+public:
+
+
+ virtual ChannelID channel_alloc(RID p_sample);
+
+ virtual void channel_set_volume(ChannelID p_channel, float p_gain);
+ virtual void channel_set_pan(ChannelID p_channel, float p_pan, float p_depth=0,float height=0); //pan and depth go from -1 to 1
+ virtual void channel_set_filter(ChannelID p_channel, FilterType p_type, float p_cutoff, float p_resonance, float p_gain=1.0);
+ virtual void channel_set_chorus(ChannelID p_channel, float p_chorus );
+ virtual void channel_set_reverb(ChannelID p_channel, ReverbRoomType p_room_type, float p_reverb);
+ virtual void channel_set_mix_rate(ChannelID p_channel, int p_mix_rate);
+ virtual void channel_set_positional(ChannelID p_channel, bool p_positional);
+
+ virtual float channel_get_volume(ChannelID p_channel) const;
+ virtual float channel_get_pan(ChannelID p_channel) const; //pan and depth go from -1 to 1
+ virtual float channel_get_pan_depth(ChannelID p_channel) const; //pan and depth go from -1 to 1
+ virtual float channel_get_pan_height(ChannelID p_channel) const; //pan and depth go from -1 to 1
+ virtual FilterType channel_get_filter_type(ChannelID p_channel) const;
+ virtual float channel_get_filter_cutoff(ChannelID p_channel) const;
+ virtual float channel_get_filter_resonance(ChannelID p_channel) const;
+ virtual float channel_get_filter_gain(ChannelID p_channel) const;
+
+ virtual float channel_get_chorus(ChannelID p_channel) const;
+ virtual ReverbRoomType channel_get_reverb_type(ChannelID p_channel) const;
+ virtual float channel_get_reverb(ChannelID p_channel) const;
+
+ virtual int channel_get_mix_rate(ChannelID p_channel) const;
+ virtual bool channel_is_positional(ChannelID p_channel) const;
+
+ virtual bool channel_is_valid(ChannelID p_channel) const;
+
+ virtual void channel_free(ChannelID p_channel);
+
+ int mix(int32_t *p_buffer,int p_frames); //return amount of mixsteps
+ uint64_t get_step_usecs() const;
+
+ virtual void set_mixer_volume(float p_volume);
+
+ AudioMixerSW(SampleManagerSW *p_sample_manager,int p_desired_latency_ms,int p_mix_rate,MixChannels p_mix_channels,bool p_use_fx=true,InterpolationType p_interp=INTERPOLATION_LINEAR,MixStepCallback p_step_callback=NULL,void *p_callback_udata=NULL);
+ ~AudioMixerSW();
+};
+
+#endif // AUDIO_MIXER_SW_H
diff --git a/servers/audio/audio_server_sw.cpp b/servers/audio/audio_server_sw.cpp
new file mode 100644
index 000000000..069bc414f
--- /dev/null
+++ b/servers/audio/audio_server_sw.cpp
@@ -0,0 +1,1012 @@
+/*************************************************************************/
+/* audio_server_sw.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#include "audio_server_sw.h"
+#include "globals.h"
+#include "os/os.h"
+
+struct _AudioDriverLock {
+
+ _AudioDriverLock() { if (AudioDriverSW::get_singleton()) AudioDriverSW::get_singleton()->lock(); }
+ ~_AudioDriverLock() { if (AudioDriverSW::get_singleton()) AudioDriverSW::get_singleton()->unlock(); }
+
+};
+
+#define AUDIO_LOCK _AudioDriverLock _adlock;
+
+AudioMixer *AudioServerSW::get_mixer() {
+
+ return mixer;
+}
+
+/* CALLBACKS */
+
+void AudioServerSW::audio_mixer_chunk_callback(int p_frames) {
+/*
+ for(List<Stream*>::Element *E=event_streams.front();E;E=E->next()) {
+
+ if (E->get()->active)
+ E->get()->audio_stream->mix(NULL,p_frames);
+ }
+*/
+}
+
+void AudioServerSW::_mixer_callback(void *p_udata) {
+
+ AudioServerSW *self = (AudioServerSW*)p_udata;
+ for(List<Stream*>::Element *E=self->active_audio_streams.front();E;E=E->next()) {
+
+ if (!E->get()->active)
+ continue;
+
+ EventStream *es=E->get()->event_stream;
+ if (!es)
+ continue;
+
+ es->update(self->mixer_step_usecs);
+ }
+
+}
+
+void AudioServerSW::driver_process_chunk(int p_frames,int32_t *p_buffer) {
+
+
+
+ int samples=p_frames*internal_buffer_channels;
+
+ for(int i=0;i<samples;i++) {
+ internal_buffer[i]=0;
+ }
+
+ while(voice_rb.commands_left()) {
+
+ VoiceRBSW::Command cmd = voice_rb.pop_command();
+
+ if (cmd.type==VoiceRBSW::Command::CMD_CHANGE_ALL_FX_VOLUMES) {
+
+ SelfList<Voice>*al = active_list.first();
+ while(al) {
+
+ Voice *v=al->self();
+ if (v->channel!=AudioMixer::INVALID_CHANNEL) {
+ mixer->channel_set_volume(v->channel,v->volume*fx_volume_scale);
+ }
+ al=al->next();
+ }
+
+ continue;
+ }
+ if (!voice_owner.owns(cmd.voice))
+ continue;
+
+
+ Voice *v = voice_owner.get(cmd.voice);
+
+ switch(cmd.type) {
+ case VoiceRBSW::Command::CMD_NONE: {
+
+
+ } break;
+ case VoiceRBSW::Command::CMD_PLAY: {
+
+ if (v->channel!=AudioMixer::INVALID_CHANNEL)
+ mixer->channel_free(v->channel);
+
+ RID sample = cmd.play.sample;
+ if (!sample_manager->is_sample(sample))
+ continue;
+
+ v->channel=mixer->channel_alloc(sample);
+ v->volume=1.0;
+ mixer->channel_set_volume(v->channel,fx_volume_scale);
+ if (v->channel==AudioMixer::INVALID_CHANNEL) {
+#ifdef AUDIO_DEBUG
+ WARN_PRINT("AUDIO: all channels used, failed to allocate voice");
+#endif
+ v->active=false;
+ break; // no voices left?
+ }
+
+ v->active=true; // this kind of ensures it works
+ if (!v->active_item.in_list())
+ active_list.add(&v->active_item);
+
+ } break;
+ case VoiceRBSW::Command::CMD_STOP: {
+
+ if (v->channel!=AudioMixer::INVALID_CHANNEL) {
+ mixer->channel_free(v->channel);
+ if (v->active_item.in_list()) {
+ active_list.remove(&v->active_item);
+ }
+ }
+ v->active=false;
+ } break;
+ case VoiceRBSW::Command::CMD_SET_VOLUME: {
+
+
+ if (v->channel!=AudioMixer::INVALID_CHANNEL) {
+ v->volume=cmd.volume.volume;
+ mixer->channel_set_volume(v->channel,cmd.volume.volume*fx_volume_scale);
+ }
+
+ } break;
+ case VoiceRBSW::Command::CMD_SET_PAN: {
+
+ if (v->channel!=AudioMixer::INVALID_CHANNEL)
+ mixer->channel_set_pan(v->channel,cmd.pan.pan,cmd.pan.depth,cmd.pan.height);
+
+ } break;
+ case VoiceRBSW::Command::CMD_SET_FILTER: {
+
+
+ if (v->channel!=AudioMixer::INVALID_CHANNEL)
+ mixer->channel_set_filter(v->channel,(AudioMixer::FilterType)cmd.filter.type,cmd.filter.cutoff,cmd.filter.resonance,cmd.filter.gain);
+ } break;
+ case VoiceRBSW::Command::CMD_SET_CHORUS: {
+
+ if (v->channel!=AudioMixer::INVALID_CHANNEL)
+ mixer->channel_set_chorus(v->channel,cmd.chorus.send);
+
+ } break;
+ case VoiceRBSW::Command::CMD_SET_REVERB: {
+
+ if (v->channel!=AudioMixer::INVALID_CHANNEL)
+ mixer->channel_set_reverb(v->channel,(AudioMixer::ReverbRoomType)cmd.reverb.room,cmd.reverb.send);
+
+ } break;
+ case VoiceRBSW::Command::CMD_SET_MIX_RATE: {
+
+ if (v->channel!=AudioMixer::INVALID_CHANNEL)
+ mixer->channel_set_mix_rate(v->channel,cmd.mix_rate.mix_rate);
+
+ } break;
+ case VoiceRBSW::Command::CMD_SET_POSITIONAL: {
+
+ if (v->channel!=AudioMixer::INVALID_CHANNEL)
+ mixer->channel_set_positional(v->channel,cmd.positional.positional);
+
+ } break;
+ default: {}
+
+ }
+ }
+
+ mixer->mix(internal_buffer,p_frames);
+ //uint64_t stepsize=mixer->get_step_usecs();
+
+
+ for(List<Stream*>::Element *E=active_audio_streams.front();E;E=E->next()) {
+
+ ERR_CONTINUE(!E->get()->active); // bug?
+
+
+ AudioStream *as=E->get()->audio_stream;
+ if (!as)
+ continue;
+
+ int channels=as->get_channel_count();
+ if (channels==0)
+ continue; // does not want mix
+ if (!as->mix(stream_buffer,p_frames))
+ continue; //nothing was mixed!!
+
+ int32_t stream_vol_scale=(stream_volume*stream_volume_scale*E->get()->volume_scale)*(1<<STREAM_SCALE_BITS);
+
+#define STRSCALE(m_val) (((m_val>>STREAM_SCALE_BITS)*stream_vol_scale)>>8)
+ switch(internal_buffer_channels) {
+
+ case 2: {
+
+ switch(channels) {
+ case 1: {
+
+ for(int i=0;i<p_frames;i++) {
+
+ internal_buffer[(i<<1)+0]+=STRSCALE(stream_buffer[i]);
+ internal_buffer[(i<<1)+1]+=STRSCALE(stream_buffer[i]);
+ }
+ } break;
+ case 2: {
+
+ for(int i=0;i<p_frames*2;i++) {
+
+ internal_buffer[i]+=STRSCALE(stream_buffer[i]);
+ }
+ } break;
+ case 4: {
+
+ for(int i=0;i<p_frames;i++) {
+
+ internal_buffer[(i<<2)+0]+=STRSCALE((stream_buffer[(i<<2)+0]+stream_buffer[(i<<2)+2])>>1);
+ internal_buffer[(i<<2)+1]+=STRSCALE((stream_buffer[(i<<2)+1]+stream_buffer[(i<<2)+3])>>1);
+ }
+ } break;
+
+ } break;
+
+ } break;
+ case 4: {
+
+ switch(channels) {
+ case 1: {
+
+ for(int i=0;i<p_frames;i++) {
+
+ internal_buffer[(i<<2)+0]+=STRSCALE(stream_buffer[i]);
+ internal_buffer[(i<<2)+1]+=STRSCALE(stream_buffer[i]);
+ internal_buffer[(i<<2)+2]+=STRSCALE(stream_buffer[i]);
+ internal_buffer[(i<<2)+3]+=STRSCALE(stream_buffer[i]);
+ }
+ } break;
+ case 2: {
+
+ for(int i=0;i<p_frames*2;i++) {
+
+ internal_buffer[(i<<2)+0]+=STRSCALE(stream_buffer[(i<<1)+0]);
+ internal_buffer[(i<<2)+1]+=STRSCALE(stream_buffer[(i<<1)+1]);
+ internal_buffer[(i<<2)+2]+=STRSCALE(stream_buffer[(i<<1)+0]);
+ internal_buffer[(i<<2)+3]+=STRSCALE(stream_buffer[(i<<1)+1]);
+ }
+ } break;
+ case 4: {
+
+ for(int i=0;i<p_frames*4;i++) {
+ internal_buffer[i]+=STRSCALE(stream_buffer[i]);
+ }
+ } break;
+
+ } break;
+
+ } break;
+ case 6: {
+
+
+ } break;
+ }
+
+#undef STRSCALE
+ }
+
+ SelfList<Voice> *activeE=active_list.first();
+ while(activeE) {
+
+ SelfList<Voice> *activeN=activeE->next();
+ if (activeE->self()->channel==AudioMixer::INVALID_CHANNEL || !mixer->channel_is_valid(activeE->self()->channel)) {
+
+ active_list.remove(activeE);
+ activeE->self()->active=false;
+
+ }
+ activeE=activeN;
+ }
+
+ uint32_t peak=0;
+ for(int i=0;i<samples;i++) {
+ //clamp to (1<<24) using branchless code
+ int32_t in = internal_buffer[i];
+#ifdef DEBUG_ENABLED
+ {
+ int mask = (in >> (32 - 1));
+ uint32_t p = (in + mask) ^ mask;
+ if (p>peak)
+ peak=p;
+ }
+#endif
+ int32_t lo = -0x800000, hi=0x7FFFFF;
+ lo-=in;
+ hi-=in;
+ in += (lo & ((lo < 0) - 1)) + (hi & ((hi > 0) - 1));
+ p_buffer[i]=in<<8;
+ }
+
+ if (peak>max_peak)
+ max_peak=peak;
+}
+
+void AudioServerSW::driver_process(int p_frames,int32_t *p_buffer) {
+
+
+ //process in chunks to make sure to never process more than INTERNAL_BUFFER_SIZE
+ int todo=p_frames;
+ while(todo) {
+
+ int tomix=MIN(todo,INTERNAL_BUFFER_SIZE);
+ driver_process_chunk(tomix,p_buffer);
+ p_buffer+=tomix;
+ todo-=tomix;
+ }
+
+
+}
+
+/* SAMPLE API */
+
+RID AudioServerSW::sample_create(SampleFormat p_format, bool p_stereo, int p_length) {
+
+ AUDIO_LOCK
+
+ return sample_manager->sample_create(p_format,p_stereo,p_length);
+}
+
+void AudioServerSW::sample_set_description(RID p_sample, const String& p_description) {
+
+ AUDIO_LOCK
+ sample_manager->sample_set_description(p_sample,p_description);
+}
+String AudioServerSW::sample_get_description(RID p_sample, const String& p_description) const {
+
+ AUDIO_LOCK
+ return sample_manager->sample_get_description(p_sample);
+}
+
+AS::SampleFormat AudioServerSW::sample_get_format(RID p_sample) const {
+ //AUDIO_LOCK
+ return sample_manager->sample_get_format(p_sample);
+}
+bool AudioServerSW::sample_is_stereo(RID p_sample) const {
+ //AUDIO_LOCK
+ return sample_manager->sample_is_stereo(p_sample);
+}
+int AudioServerSW::sample_get_length(RID p_sample) const {
+ ///AUDIO_LOCK
+ return sample_manager->sample_get_length(p_sample);
+}
+
+const void* AudioServerSW::sample_get_data_ptr(RID p_sample) const {
+ ///AUDIO_LOCK
+ return sample_manager->sample_get_data_ptr(p_sample);
+}
+
+void AudioServerSW::sample_set_data(RID p_sample, const DVector<uint8_t>& p_buffer) {
+ AUDIO_LOCK
+ sample_manager->sample_set_data(p_sample,p_buffer);
+}
+const DVector<uint8_t> AudioServerSW::sample_get_data(RID p_sample) const {
+ AUDIO_LOCK
+ return sample_manager->sample_get_data(p_sample);
+}
+
+void AudioServerSW::sample_set_mix_rate(RID p_sample,int p_rate) {
+ AUDIO_LOCK
+ sample_manager->sample_set_mix_rate(p_sample,p_rate);
+}
+int AudioServerSW::sample_get_mix_rate(RID p_sample) const {
+ AUDIO_LOCK
+ return sample_manager->sample_get_mix_rate(p_sample);
+}
+
+void AudioServerSW::sample_set_loop_format(RID p_sample,SampleLoopFormat p_format) {
+ AUDIO_LOCK
+ sample_manager->sample_set_loop_format(p_sample,p_format);
+}
+AS::SampleLoopFormat AudioServerSW::sample_get_loop_format(RID p_sample) const {
+ AUDIO_LOCK
+ return sample_manager->sample_get_loop_format(p_sample);
+}
+
+void AudioServerSW::sample_set_loop_begin(RID p_sample,int p_pos) {
+ AUDIO_LOCK
+ sample_manager->sample_set_loop_begin(p_sample,p_pos);
+}
+int AudioServerSW::sample_get_loop_begin(RID p_sample) const {
+ AUDIO_LOCK
+ return sample_manager->sample_get_loop_begin(p_sample);
+}
+
+void AudioServerSW::sample_set_loop_end(RID p_sample,int p_pos) {
+ AUDIO_LOCK
+ sample_manager->sample_set_loop_end(p_sample,p_pos);
+}
+int AudioServerSW::sample_get_loop_end(RID p_sample) const {
+ AUDIO_LOCK
+ return sample_manager->sample_get_loop_end(p_sample);
+}
+
+/* VOICE API */
+
+RID AudioServerSW::voice_create() {
+
+ Voice * v = memnew( Voice );
+ v->channel=AudioMixer::INVALID_CHANNEL;
+
+ AUDIO_LOCK
+ return voice_owner.make_rid(v);
+
+}
+void AudioServerSW::voice_play(RID p_voice, RID p_sample) {
+
+ Voice *v = voice_owner.get( p_voice );
+ ERR_FAIL_COND(!v);
+ v->active=true; // force actvive (will be disabled later i gues..)
+
+ //stop old, start new
+ VoiceRBSW::Command cmd;
+ cmd.type=VoiceRBSW::Command::CMD_PLAY;
+ cmd.voice=p_voice;
+ cmd.play.sample=p_sample;
+ voice_rb.push_command(cmd);
+
+}
+
+void AudioServerSW::voice_set_volume(RID p_voice, float p_db) {
+
+ VoiceRBSW::Command cmd;
+ cmd.type=VoiceRBSW::Command::CMD_SET_VOLUME;
+ cmd.voice=p_voice;
+ cmd.volume.volume=p_db;
+ voice_rb.push_command(cmd);
+
+}
+void AudioServerSW::voice_set_pan(RID p_voice, float p_pan, float p_depth,float p_height) {
+
+ VoiceRBSW::Command cmd;
+ cmd.type=VoiceRBSW::Command::CMD_SET_PAN;
+ cmd.voice=p_voice;
+ cmd.pan.pan=p_pan;
+ cmd.pan.depth=p_depth;
+ cmd.pan.height=p_height;
+ voice_rb.push_command(cmd);
+
+}
+void AudioServerSW::voice_set_filter(RID p_voice, FilterType p_type, float p_cutoff, float p_resonance,float p_gain) {
+
+ VoiceRBSW::Command cmd;
+ cmd.type=VoiceRBSW::Command::CMD_SET_FILTER;
+ cmd.voice=p_voice;
+ cmd.filter.type=p_type;
+ cmd.filter.cutoff=p_cutoff;
+ cmd.filter.resonance=p_resonance;
+ cmd.filter.gain=p_gain;
+ voice_rb.push_command(cmd);
+
+}
+void AudioServerSW::voice_set_chorus(RID p_voice, float p_chorus ) {
+
+ VoiceRBSW::Command cmd;
+ cmd.type=VoiceRBSW::Command::CMD_SET_CHORUS;
+ cmd.voice=p_voice;
+ cmd.chorus.send=p_chorus;
+ voice_rb.push_command(cmd);
+
+}
+void AudioServerSW::voice_set_reverb(RID p_voice, ReverbRoomType p_room_type, float p_reverb) {
+
+ VoiceRBSW::Command cmd;
+ cmd.type=VoiceRBSW::Command::CMD_SET_REVERB;
+ cmd.voice=p_voice;
+ cmd.reverb.room=p_room_type;
+ cmd.reverb.send=p_reverb;
+ voice_rb.push_command(cmd);
+
+}
+void AudioServerSW::voice_set_mix_rate(RID p_voice, int p_mix_rate) {
+
+ VoiceRBSW::Command cmd;
+ cmd.type=VoiceRBSW::Command::CMD_SET_MIX_RATE;
+ cmd.voice=p_voice;
+ cmd.mix_rate.mix_rate=p_mix_rate;
+ voice_rb.push_command(cmd);
+
+}
+void AudioServerSW::voice_set_positional(RID p_voice, bool p_positional) {
+
+ VoiceRBSW::Command cmd;
+ cmd.type=VoiceRBSW::Command::CMD_SET_POSITIONAL;
+ cmd.voice=p_voice;
+ cmd.positional.positional=p_positional;
+ voice_rb.push_command(cmd);
+
+}
+
+float AudioServerSW::voice_get_volume(RID p_voice) const {
+
+ AUDIO_LOCK
+ Voice *v = voice_owner.get( p_voice );
+ ERR_FAIL_COND_V(!v, 0);
+
+ return mixer->channel_get_volume( v->channel );
+
+}
+float AudioServerSW::voice_get_pan(RID p_voice) const {
+
+ AUDIO_LOCK
+ Voice *v = voice_owner.get( p_voice );
+ ERR_FAIL_COND_V(!v, 0);
+
+ return mixer->channel_get_pan( v->channel );
+
+}
+float AudioServerSW::voice_get_pan_depth(RID p_voice) const {
+
+ AUDIO_LOCK
+ Voice *v = voice_owner.get( p_voice );
+ ERR_FAIL_COND_V(!v, 0);
+
+ return mixer->channel_get_pan_depth( v->channel );
+
+}
+float AudioServerSW::voice_get_pan_height(RID p_voice) const {
+
+ AUDIO_LOCK
+ Voice *v = voice_owner.get( p_voice );
+ ERR_FAIL_COND_V(!v, 0);
+
+ return mixer->channel_get_pan_height( v->channel );
+
+}
+AS::FilterType AudioServerSW::voice_get_filter_type(RID p_voice) const {
+
+ AUDIO_LOCK
+ Voice *v = voice_owner.get( p_voice );
+ ERR_FAIL_COND_V(!v, AS::FILTER_NONE);
+
+ return (AS::FilterType)mixer->channel_get_filter_type(v->channel);
+
+}
+float AudioServerSW::voice_get_filter_cutoff(RID p_voice) const {
+
+ AUDIO_LOCK
+ Voice *v = voice_owner.get( p_voice );
+ ERR_FAIL_COND_V(!v, 0);
+
+ return mixer->channel_get_filter_cutoff( v->channel );
+
+}
+float AudioServerSW::voice_get_filter_resonance(RID p_voice) const {
+
+ AUDIO_LOCK
+ Voice *v = voice_owner.get( p_voice );
+ ERR_FAIL_COND_V(!v, 0);
+
+ return mixer->channel_get_filter_resonance( v->channel );
+
+}
+float AudioServerSW::voice_get_chorus(RID p_voice) const {
+
+ AUDIO_LOCK
+ Voice *v = voice_owner.get( p_voice );
+ ERR_FAIL_COND_V(!v, 0);
+
+ return mixer->channel_get_chorus( v->channel );
+
+}
+AS::ReverbRoomType AudioServerSW::voice_get_reverb_type(RID p_voice) const {
+
+ AUDIO_LOCK
+ Voice *v = voice_owner.get( p_voice );
+ ERR_FAIL_COND_V(!v, REVERB_SMALL);
+
+ return (AS::ReverbRoomType)mixer->channel_get_reverb_type( v->channel );
+
+}
+float AudioServerSW::voice_get_reverb(RID p_voice) const {
+
+ AUDIO_LOCK
+ Voice *v = voice_owner.get( p_voice );
+ ERR_FAIL_COND_V(!v, 0);
+
+ return mixer->channel_get_reverb( v->channel );
+
+}
+
+int AudioServerSW::voice_get_mix_rate(RID p_voice) const {
+
+ AUDIO_LOCK
+ Voice *v = voice_owner.get( p_voice );
+ ERR_FAIL_COND_V(!v, 0);
+
+ return mixer->channel_get_mix_rate( v->channel );
+
+}
+bool AudioServerSW::voice_is_positional(RID p_voice) const {
+
+ AUDIO_LOCK
+ Voice *v = voice_owner.get( p_voice );
+ ERR_FAIL_COND_V(!v, 0);
+
+ return mixer->channel_is_positional( v->channel );
+
+}
+
+void AudioServerSW::voice_stop(RID p_voice) {
+
+ VoiceRBSW::Command cmd;
+ cmd.type=VoiceRBSW::Command::CMD_STOP;
+ cmd.voice=p_voice;
+ voice_rb.push_command(cmd);
+
+ //return mixer->channel_free( v->channel );
+
+}
+
+bool AudioServerSW::voice_is_active(RID p_voice) const {
+
+ Voice *v = voice_owner.get(p_voice);
+ ERR_FAIL_COND_V(!v,false);
+ return v->active;
+
+}
+
+/* STREAM API */
+
+RID AudioServerSW::audio_stream_create(AudioStream *p_stream) {
+
+ AUDIO_LOCK
+ Stream *s = memnew(Stream);
+ s->audio_stream=p_stream;
+ s->event_stream=NULL;
+ s->active=false;
+ s->E=NULL;
+ s->volume_scale=1.0;
+ p_stream->set_mix_rate(AudioDriverSW::get_singleton()->get_mix_rate());
+
+ return stream_owner.make_rid(s);
+}
+
+RID AudioServerSW::event_stream_create(EventStream *p_stream) {
+
+ AUDIO_LOCK
+ Stream *s = memnew(Stream);
+ s->audio_stream=NULL;
+ s->event_stream=p_stream;
+ s->active=false;
+ s->E=NULL;
+ s->volume_scale=1.0;
+ //p_stream->set_mix_rate(AudioDriverSW::get_singleton()->get_mix_rate());
+
+ return stream_owner.make_rid(s);
+
+
+}
+
+
+void AudioServerSW::stream_set_active(RID p_stream, bool p_active) {
+
+
+ Stream *s = stream_owner.get(p_stream);
+ ERR_FAIL_COND(!s);
+
+ if (s->active==p_active)
+ return;
+ AUDIO_LOCK;
+ _THREAD_SAFE_METHOD_
+ s->active=p_active;
+ if (p_active)
+ s->E=active_audio_streams.push_back(s);
+ else {
+ active_audio_streams.erase(s->E);
+ s->E=NULL;
+ }
+}
+
+bool AudioServerSW::stream_is_active(RID p_stream) const {
+
+ Stream *s = stream_owner.get(p_stream);
+ ERR_FAIL_COND_V(!s,false);
+ return s->active;
+}
+
+void AudioServerSW::stream_set_volume_scale(RID p_stream, float p_scale) {
+
+ Stream *s = stream_owner.get(p_stream);
+ ERR_FAIL_COND(!s);
+ s->volume_scale=p_scale;
+
+}
+
+float AudioServerSW::stream_set_volume_scale(RID p_stream) const {
+
+ Stream *s = stream_owner.get(p_stream);
+ ERR_FAIL_COND_V(!s,0);
+ return s->volume_scale;
+
+}
+
+
+void AudioServerSW::free(RID p_id) {
+
+ if(voice_owner.owns(p_id)) {
+
+ Voice *v = voice_owner.get(p_id);
+ AUDIO_LOCK
+ mixer->channel_free( v->channel );
+ voice_owner.free(p_id);
+ memdelete(v);
+
+ } else if (stream_owner.owns(p_id)) {
+
+
+ Stream *s=stream_owner.get(p_id);
+
+ if (s->active) {
+ stream_set_active(p_id,false);
+ }
+
+ memdelete(s);
+ stream_owner.free(p_id);
+
+ } else if (sample_manager->is_sample(p_id)) {
+
+ AUDIO_LOCK
+ sample_manager->free(p_id);
+ }
+
+}
+
+void AudioServerSW::_thread_func(void *self) {
+
+
+ AudioServerSW *as=(AudioServerSW *)self;
+
+ while (!as->exit_update_thread) {
+ as->_update_streams(true);
+ OS::get_singleton()->delay_usec(5000);
+ }
+
+}
+
+void AudioServerSW::init() {
+
+ int latency = GLOBAL_DEF("audio/mixer_latency",10);
+ internal_buffer_channels=2; // read from driver
+ internal_buffer = memnew_arr(int32_t,INTERNAL_BUFFER_SIZE*internal_buffer_channels);
+ stream_buffer = memnew_arr(int32_t,INTERNAL_BUFFER_SIZE*4); //max 4 channels
+ AudioMixerSW::MixChannels mix_chans = AudioMixerSW::MIX_STEREO;
+
+ switch(AudioDriverSW::get_singleton()->get_output_format()) {
+
+ case AudioDriverSW::OUTPUT_MONO:
+ case AudioDriverSW::OUTPUT_STEREO:
+ mix_chans=AudioMixerSW::MIX_STEREO;
+ break;
+ case AudioDriverSW::OUTPUT_QUAD:
+ case AudioDriverSW::OUTPUT_5_1:
+ mix_chans=AudioMixerSW::MIX_QUAD;
+ break;
+ }
+
+ mixer = memnew( AudioMixerSW( sample_manager, latency, AudioDriverSW::get_singleton()->get_mix_rate(),mix_chans,mixer_use_fx,mixer_interp,_mixer_callback,this ) );
+ mixer_step_usecs=mixer->get_step_usecs();
+
+ stream_volume=0.3;
+ // start the audio driver
+ if (AudioDriverSW::get_singleton())
+ AudioDriverSW::get_singleton()->start();
+
+#ifndef NO_THREADS
+ exit_update_thread=false;
+ thread = Thread::create(_thread_func,this);
+#endif
+
+}
+
+void AudioServerSW::finish() {
+
+#ifndef NO_THREADS
+ exit_update_thread=true;
+ Thread::wait_to_finish(thread);
+ memdelete(thread);
+#endif
+
+ if (AudioDriverSW::get_singleton())
+ AudioDriverSW::get_singleton()->finish();
+
+ memdelete_arr(internal_buffer);
+ memdelete_arr(stream_buffer);
+ memdelete(mixer);
+
+}
+
+void AudioServerSW::_update_streams(bool p_thread) {
+
+ _THREAD_SAFE_METHOD_
+ for(List<Stream*>::Element *E=active_audio_streams.front();E;E=E->next()) {
+
+ if (E->get()->audio_stream && p_thread == E->get()->audio_stream->can_update_mt())
+ E->get()->audio_stream->update();
+ }
+
+}
+
+void AudioServerSW::update() {
+
+ _update_streams(false);
+#ifdef NO_THREADS
+
+ _update_streams(true);
+#endif
+}
+
+
+void AudioServerSW::lock() {
+
+ AudioDriverSW::get_singleton()->lock();
+}
+
+void AudioServerSW::unlock() {
+ AudioDriverSW::get_singleton()->unlock();
+
+}
+
+int AudioServerSW::get_default_mix_rate() const {
+
+ return AudioDriverSW::get_singleton()->get_mix_rate();
+}
+int AudioServerSW::get_default_channel_count() const {
+ return internal_buffer_channels;
+}
+
+void AudioServerSW::set_mixer_params(AudioMixerSW::InterpolationType p_interp, bool p_use_fx) {
+
+ mixer_interp=p_interp;
+ mixer_use_fx=p_use_fx;
+}
+
+void AudioServerSW::set_stream_global_volume_scale(float p_volume) {
+
+ stream_volume_scale=p_volume;
+}
+
+float AudioServerSW::get_stream_global_volume_scale() const {
+
+ return stream_volume_scale;
+
+
+}
+
+void AudioServerSW::set_fx_global_volume_scale(float p_volume) {
+
+ fx_volume_scale=p_volume;
+ //mixer->set_mixer_volume(fx_volume_scale);
+ VoiceRBSW::Command cmd;
+ cmd.type=VoiceRBSW::Command::CMD_CHANGE_ALL_FX_VOLUMES;
+ cmd.voice=RID();
+ cmd.volume.volume=p_volume;
+ voice_rb.push_command(cmd);
+
+}
+
+
+float AudioServerSW::get_fx_global_volume_scale() const {
+
+ return fx_volume_scale;
+}
+
+void AudioServerSW::set_event_voice_global_volume_scale(float p_volume) {
+
+ event_voice_volume_scale=p_volume;
+ //mixer->set_mixer_volume(event_voice_volume_scale);
+}
+
+
+float AudioServerSW::get_event_voice_global_volume_scale() const {
+
+ return event_voice_volume_scale;
+}
+
+double AudioServerSW::get_mix_time() const {
+
+ return AudioDriverSW::get_singleton()->get_mix_time();
+}
+
+uint32_t AudioServerSW::read_output_peak() const {
+
+ uint32_t val = max_peak;
+ uint32_t *p = (uint32_t*)&max_peak;
+ *p=0;
+ return val;
+}
+
+AudioServerSW::AudioServerSW(SampleManagerSW *p_sample_manager) {
+
+ sample_manager=p_sample_manager;
+ String interp = GLOBAL_DEF("audio/mixer_interp","linear");
+ Globals::get_singleton()->set_custom_property_info("audio/mixer",PropertyInfo(Variant::STRING,"audio/mixer",PROPERTY_HINT_ENUM,"raw,linear,cubic"));
+ if (interp=="raw")
+ mixer_interp=AudioMixerSW::INTERPOLATION_RAW;
+ else if (interp=="cubic")
+ mixer_interp=AudioMixerSW::INTERPOLATION_CUBIC;
+ else
+ mixer_interp=AudioMixerSW::INTERPOLATION_LINEAR;
+ mixer_use_fx = GLOBAL_DEF("audio/use_chorus_reverb",true);
+ stream_volume_scale=GLOBAL_DEF("audio/stream_volume_scale",1.0);
+ fx_volume_scale=GLOBAL_DEF("audio/fx_volume_scale",1.0);
+ event_voice_volume_scale=GLOBAL_DEF("audio/event_voice_volume_scale",0.5);
+ max_peak=0;
+
+
+}
+
+AudioServerSW::~AudioServerSW() {
+
+}
+
+
+AudioDriverSW *AudioDriverSW::singleton=NULL;
+AudioDriverSW *AudioDriverSW::get_singleton() {
+
+ return singleton;
+}
+
+void AudioDriverSW::set_singleton() {
+
+ singleton=this;
+}
+
+void AudioDriverSW::audio_server_process(int p_frames,int32_t *p_buffer,bool p_update_mix_time) {
+
+ AudioServerSW * audio_server = static_cast<AudioServerSW*>(AudioServer::get_singleton());
+ if (p_update_mix_time)
+ update_mix_time(p_frames);
+ audio_server->driver_process(p_frames,p_buffer);
+}
+
+void AudioDriverSW::update_mix_time(int p_frames) {
+
+ _mix_amount+=p_frames;
+ _last_mix_time=OS::get_singleton()->get_ticks_usec();
+}
+
+double AudioDriverSW::get_mix_time() const {
+
+ double total = (OS::get_singleton()->get_ticks_usec() - _last_mix_time) / 1000000.0;
+ total+=_mix_amount/(double)get_mix_rate();
+ return total;
+
+}
+
+
+AudioDriverSW::AudioDriverSW() {
+
+ _last_mix_time=0;
+ _mix_amount=0;
+}
+
+
+AudioDriverSW *AudioDriverManagerSW::drivers[MAX_DRIVERS];
+int AudioDriverManagerSW::driver_count=0;
+
+
+
+void AudioDriverManagerSW::add_driver(AudioDriverSW *p_driver) {
+
+ ERR_FAIL_COND(driver_count>=MAX_DRIVERS);
+ drivers[driver_count++]=p_driver;
+}
+
+int AudioDriverManagerSW::get_driver_count() {
+
+ return driver_count;
+}
+AudioDriverSW *AudioDriverManagerSW::get_driver(int p_driver) {
+
+ ERR_FAIL_INDEX_V(p_driver,driver_count,NULL);
+ return drivers[p_driver];
+}
+
diff --git a/servers/audio/audio_server_sw.h b/servers/audio/audio_server_sw.h
new file mode 100644
index 000000000..d137c1563
--- /dev/null
+++ b/servers/audio/audio_server_sw.h
@@ -0,0 +1,279 @@
+/*************************************************************************/
+/* audio_server_sw.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#ifndef AUDIO_SERVER_SW_H
+#define AUDIO_SERVER_SW_H
+
+#include "servers/audio_server.h"
+#include "servers/audio/audio_mixer_sw.h"
+#include "servers/audio/voice_rb_sw.h"
+#include "self_list.h"
+#include "os/thread_safe.h"
+#include "os/thread.h"
+class AudioServerSW : public AudioServer {
+
+ OBJ_TYPE( AudioServerSW, AudioServer );
+
+ _THREAD_SAFE_CLASS_
+
+ enum {
+ INTERNAL_BUFFER_SIZE=4096,
+ STREAM_SCALE_BITS=12
+
+ };
+
+ SampleManagerSW *sample_manager;
+ AudioMixerSW *mixer;
+
+ virtual AudioMixer *get_mixer();
+ virtual void audio_mixer_chunk_callback(int p_frames);
+
+ struct Voice {
+
+ float volume;
+ volatile bool active;
+ SelfList<Voice> active_item;
+ AudioMixer::ChannelID channel;
+
+
+ Voice () : active_item(this) { channel=AudioMixer::INVALID_CHANNEL; active=false;}
+ };
+
+ mutable RID_Owner<Voice> voice_owner;
+ SelfList<Voice>::List active_list;
+
+ struct Stream {
+ bool active;
+ List<Stream*>::Element *E;
+ AudioStream *audio_stream;
+ EventStream *event_stream;
+ float volume_scale;
+ };
+
+ List<Stream*> active_audio_streams;
+
+ //List<Stream*> event_streams;
+
+ int32_t * internal_buffer;
+ int internal_buffer_channels;
+ int32_t * stream_buffer;
+
+ mutable RID_Owner<Stream> stream_owner;
+
+ float stream_volume;
+ float stream_volume_scale;
+ float fx_volume_scale;
+ float event_voice_volume_scale;
+ float peak_left,peak_right;
+ uint32_t max_peak;
+
+ VoiceRBSW voice_rb;
+
+ bool exit_update_thread;
+ Thread *thread;
+ static void _thread_func(void *self);
+
+ void _update_streams(bool p_thread);
+ void driver_process_chunk(int p_frames,int32_t *p_buffer);
+
+ AudioMixerSW::InterpolationType mixer_interp;
+ bool mixer_use_fx;
+ uint64_t mixer_step_usecs;
+
+ static void _mixer_callback(void *p_udata);
+friend class AudioDriverSW;
+ void driver_process(int p_frames,int32_t *p_buffer);
+public:
+
+
+ /* SAMPLE API */
+
+ virtual RID sample_create(SampleFormat p_format, bool p_stereo, int p_length);
+
+ virtual void sample_set_description(RID p_sample, const String& p_description);
+ virtual String sample_get_description(RID p_sample, const String& p_description) const;
+
+ virtual SampleFormat sample_get_format(RID p_sample) const;
+ virtual bool sample_is_stereo(RID p_sample) const;
+ virtual int sample_get_length(RID p_sample) const;
+ const void* sample_get_data_ptr(RID p_sample) const;
+
+ virtual void sample_set_data(RID p_sample, const DVector<uint8_t>& p_buffer);
+ virtual const DVector<uint8_t> sample_get_data(RID p_sample) const;
+
+ virtual void sample_set_mix_rate(RID p_sample,int p_rate);
+ virtual int sample_get_mix_rate(RID p_sample) const;
+
+ virtual void sample_set_loop_format(RID p_sample,SampleLoopFormat p_format);
+ virtual SampleLoopFormat sample_get_loop_format(RID p_sample) const;
+
+ virtual void sample_set_loop_begin(RID p_sample,int p_pos);
+ virtual int sample_get_loop_begin(RID p_sample) const;
+
+ virtual void sample_set_loop_end(RID p_sample,int p_pos);
+ virtual int sample_get_loop_end(RID p_sample) const;
+
+ /* VOICE API */
+
+ virtual RID voice_create();
+
+ virtual void voice_play(RID p_voice, RID p_sample);
+
+ virtual void voice_set_volume(RID p_voice, float p_db);
+ virtual void voice_set_pan(RID p_voice, float p_pan, float p_depth=0,float height=0); //pan and depth go from -1 to 1
+ virtual void voice_set_filter(RID p_voice, FilterType p_type, float p_cutoff, float p_resonance,float p_gain=0);
+ virtual void voice_set_chorus(RID p_voice, float p_chorus );
+ virtual void voice_set_reverb(RID p_voice, ReverbRoomType p_room_type, float p_reverb);
+ virtual void voice_set_mix_rate(RID p_voice, int p_mix_rate);
+ virtual void voice_set_positional(RID p_voice, bool p_positional);
+
+ virtual float voice_get_volume(RID p_voice) const;
+ virtual float voice_get_pan(RID p_voice) const; //pan and depth go from -1 to 1
+ virtual float voice_get_pan_depth(RID p_voice) const; //pan and depth go from -1 to 1
+ virtual float voice_get_pan_height(RID p_voice) const; //pan and depth go from -1 to 1
+ virtual FilterType voice_get_filter_type(RID p_voice) const;
+ virtual float voice_get_filter_cutoff(RID p_voice) const;
+ virtual float voice_get_filter_resonance(RID p_voice) const;
+ virtual float voice_get_chorus(RID p_voice) const;
+ virtual ReverbRoomType voice_get_reverb_type(RID p_voice) const;
+ virtual float voice_get_reverb(RID p_voice) const;
+
+ virtual int voice_get_mix_rate(RID p_voice) const;
+ virtual bool voice_is_positional(RID p_voice) const;
+
+ virtual void voice_stop(RID p_voice);
+ virtual bool voice_is_active(RID p_voice) const;
+
+ /* STREAM API */
+
+ virtual RID audio_stream_create(AudioStream *p_stream);
+ virtual RID event_stream_create(EventStream *p_stream);
+
+ virtual void stream_set_active(RID p_stream, bool p_active);
+ virtual bool stream_is_active(RID p_stream) const;
+
+ virtual void stream_set_volume_scale(RID p_stream, float p_scale);
+ virtual float stream_set_volume_scale(RID p_stream) const;
+
+ virtual void free(RID p_id);
+
+ virtual void init();
+ virtual void finish();
+ virtual void update();
+
+ virtual void lock();
+ virtual void unlock();
+ virtual int get_default_channel_count() const;
+ virtual int get_default_mix_rate() const;
+
+ void set_mixer_params(AudioMixerSW::InterpolationType p_interp, bool p_use_fx);
+
+ virtual void set_stream_global_volume_scale(float p_volume);
+ virtual void set_fx_global_volume_scale(float p_volume);
+ virtual void set_event_voice_global_volume_scale(float p_volume);
+
+
+ virtual float get_stream_global_volume_scale() const;
+ virtual float get_fx_global_volume_scale() const;
+ virtual float get_event_voice_global_volume_scale() const;
+
+ virtual uint32_t read_output_peak() const;
+
+ virtual double get_mix_time() const; //useful for video -> audio sync
+
+ AudioServerSW(SampleManagerSW *p_sample_manager);
+ ~AudioServerSW();
+
+};
+
+
+class AudioDriverSW {
+
+
+ static AudioDriverSW *singleton;
+ uint64_t _last_mix_time;
+ uint64_t _mix_amount;
+
+
+protected:
+
+ void audio_server_process(int p_frames,int32_t *p_buffer,bool p_update_mix_time=true);
+ void update_mix_time(int p_frames);
+
+public:
+
+
+ double get_mix_time() const; //useful for video -> audio sync
+
+ enum OutputFormat {
+
+ OUTPUT_MONO,
+ OUTPUT_STEREO,
+ OUTPUT_QUAD,
+ OUTPUT_5_1
+ };
+
+ static AudioDriverSW *get_singleton();
+ void set_singleton();
+
+ virtual const char* get_name() const=0;
+
+ virtual Error init()=0;
+ virtual void start()=0;
+ virtual int get_mix_rate() const =0;
+ virtual OutputFormat get_output_format() const=0;
+ virtual void lock()=0;
+ virtual void unlock()=0;
+ virtual void finish()=0;
+
+
+
+
+ AudioDriverSW();
+ virtual ~AudioDriverSW() {};
+};
+
+
+
+class AudioDriverManagerSW {
+
+ enum {
+
+ MAX_DRIVERS=10
+ };
+
+ static AudioDriverSW *drivers[MAX_DRIVERS];
+ static int driver_count;
+public:
+
+ static void add_driver(AudioDriverSW *p_driver);
+ static int get_driver_count();
+ static AudioDriverSW *get_driver(int p_driver);
+};
+
+#endif // AUDIO_SERVER_SW_H
diff --git a/servers/audio/reverb_buffers_sw.cpp b/servers/audio/reverb_buffers_sw.cpp
new file mode 100644
index 000000000..6d09be9a3
--- /dev/null
+++ b/servers/audio/reverb_buffers_sw.cpp
@@ -0,0 +1,33 @@
+/*************************************************************************/
+/* reverb_buffers_sw.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#include "reverb_buffers_sw.h"
+
+ReverbBuffersSW::ReverbBuffersSW()
+{
+}
diff --git a/servers/audio/reverb_buffers_sw.h b/servers/audio/reverb_buffers_sw.h
new file mode 100644
index 000000000..64a9e4fe7
--- /dev/null
+++ b/servers/audio/reverb_buffers_sw.h
@@ -0,0 +1,38 @@
+/*************************************************************************/
+/* reverb_buffers_sw.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#ifndef REVERB_BUFFERS_SW_H
+#define REVERB_BUFFERS_SW_H
+
+class ReverbBuffersSW
+{
+public:
+ ReverbBuffersSW();
+};
+
+#endif // REVERB_BUFFERS_SW_H
diff --git a/servers/audio/reverb_sw.cpp b/servers/audio/reverb_sw.cpp
new file mode 100644
index 000000000..df36886db
--- /dev/null
+++ b/servers/audio/reverb_sw.cpp
@@ -0,0 +1,569 @@
+/*************************************************************************/
+/* reverb_sw.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#include "reverb_sw.h"
+#include "stdlib.h"
+#include "print_string.h"
+#define SETMIN( x, y ) (x) = MIN ( (x), (y) )
+#define rangeloop( c, min, max ) \
+ for ( (c) = (min) ; (c) < (max) ; (c)++ )
+
+#define ABSDIFF(x, y)\
+ ( ((x) < (y)) ? ((y) - (x)) : ((x) - (y)) )
+
+
+#ifdef bleh_MSC_VER
+
+#if _MSC_VER >= 1400
+ _FORCE_INLINE_ int32_tMULSHIFT_S32 (
+ int32_t Factor1,
+ int32_t Factor2,
+ uint8_t Bits
+ ) {
+
+ return __ll_rshift (
+ __emul ( Factor1, Factor2 ),
+ Bits
+ );
+ }
+#endif
+
+#else
+#define MULSHIFT_S32( Factor1, Factor2, Bits )\
+ ( (int) (( (int64_t)(Factor1) * (Factor2) ) >> (Bits)) )
+#endif
+
+
+
+struct ReverbParamsSW {
+ unsigned int BufferSize; // Required buffer size
+ int gLPF; // Coefficient
+ int gEcho0; // Coefficient
+ int gEcho1; // Coefficient
+ int gEcho2; // Coefficient
+ int gEcho3; // Coefficient
+ int gWall; // Coefficient
+ int gReva; // Coefficient
+ int gRevb; // Coefficient
+ int gInputL; // Coefficient
+ int gInputR; // Coefficient
+ unsigned int nRevaOldL; // Offset
+ unsigned int nRevaOldR; // Offset
+ unsigned int nRevbOldL; // Offset
+ unsigned int nRevbOldR; // Offset
+ unsigned int nLwlNew; // Offset
+ unsigned int nRwrNew; // Offset
+ unsigned int nEcho0L; // Offset
+ unsigned int nEcho0R; // Offset
+ unsigned int nEcho1L; // Offset
+ unsigned int nEcho1R; // Offset
+ unsigned int nLwlOld; // Offset
+ unsigned int nRwrOld; // Offset
+ unsigned int nLwrNew; // Offset
+ unsigned int nRwlNew; // Offset
+ unsigned int nEcho2L; // Offset
+ unsigned int nEcho2R; // Offset
+ unsigned int nEcho3L; // Offset
+ unsigned int nEcho3R; // Offset
+ unsigned int nLwrOld; // Offset
+ unsigned int nRwlOld; // Offset
+ unsigned int nRevaNewL; // Offset
+ unsigned int nRevaNewR; // Offset
+ unsigned int nRevbNewL; // Offset
+ unsigned int nRevbNewR; // Offset
+};
+
+static ReverbParamsSW reverb_params_Room = {
+ 0x26C0/2,
+// gLPF gEcho0 gEcho1 gEcho2 gEcho3 gWall
+ 0x6D80, 0x54B8, -0x4130, 0x0000, 0x0000, -0x4580,
+// gReva gRevb gInputL gInputR
+ 0x5800, 0x5300, -0x8000, -0x8000,
+// nRevaOldL nRevaOldR nRevbOldL nRevbOldR
+ 0x01B4 - 0x007D, 0x0136 - 0x007D, 0x00B8 - 0x005B, 0x005C - 0x005B,
+// nLwlNew nRwrNew nEcho0L nEcho0R nEcho1L nEcho1R
+ 0x04D6, 0x0333, 0x03F0, 0x0227, 0x0374, 0x01EF,
+// nLwlOld nRwrOld nLwrNew nRwlNew nEcho2L nEcho2R nEcho3L nEcho3R
+ 0x0334, 0x01B5, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+// nLwrOld nRwlOld nRevaNewL nRevaNewR nRevbNewL nRevbNewR
+ 0x0000, 0x0000, 0x01B4, 0x0136, 0x00B8, 0x005C
+};
+
+static ReverbParamsSW reverb_params_StudioSmall = {
+ 0x1F40/2,
+// gLPF gEcho0 gEcho1 gEcho2 gEcho3 gWall
+ 0x70F0, 0x4FA8, -0x4320, 0x4410, -0x3F10, -0x6400,
+// gReva gRevb gInputL gInputR
+ 0x5280, 0x4EC0, -0x8000, -0x8000,
+// nRevaOldL nRevaOldR nRevbOldL nRevbOldR
+ 0x00B4 - 0x0033, 0x0080 - 0x0033, 0x004C - 0x0025, 0x0026 - 0x0025,
+// nLwlNew nRwrNew nEcho0L nEcho0R nEcho1L nEcho1R
+ 0x03E4, 0x031B, 0x03A4, 0x02AF, 0x0372, 0x0266,
+// nLwlOld nRwrOld nLwrNew nRwlNew nEcho2L nEcho2R nEcho3L nEcho3R
+ 0x031C, 0x025D, 0x025C, 0x018E, 0x022F, 0x0135, 0x01D2, 0x00B7,
+// nLwrOld nRwlOld nRevaNewL nRevaNewR nRevbNewL nRevbNewR
+ 0x018F, 0x00B5, 0x00B4, 0x0080, 0x004C, 0x0026
+};
+
+static ReverbParamsSW reverb_params_StudioMedium = {
+ 0x4840/2,
+// gLPF gEcho0 gEcho1 gEcho2 gEcho3 gWall
+ 0x70F0, 0x4FA8, -0x4320, 0x4510, -0x4110, -0x4B40,
+// gReva gRevb gInputL gInputR
+ 0x5280, 0x4EC0, -0x8000, -0x8000,
+// nRevaOldL nRevaOldR nRevbOldL nRevbOldR
+ 0x0264 - 0x00B1, 0x01B2 - 0x00B1, 0x0100 - 0x007F, 0x0080 - 0x007F,
+// nLwlNew nRwrNew nEcho0L nEcho0R nEcho1L nEcho1R
+ 0x0904, 0x076B, 0x0824, 0x065F, 0x07A2, 0x0616,
+// nLwlOld nRwrOld nLwrNew nRwlNew nEcho2L nEcho2R nEcho3L nEcho3R
+ 0x076C, 0x05ED, 0x05EC, 0x042E, 0x050F, 0x0305, 0x0462, 0x02B7,
+// nLwrOld nRwlOld nRevaNewL nRevaNewR nRevbNewL nRevbNewR
+ 0x042F, 0x0265, 0x0264, 0x01B2, 0x0100, 0x0080
+};
+
+static ReverbParamsSW reverb_params_StudioLarge = {
+ 0x6FE0/2,
+// gLPF gEcho0 gEcho1 gEcho2 gEcho3 gWall
+ 0x6F60, 0x4FA8, -0x4320, 0x4510, -0x4110, -0x5980,
+// gReva gRevb gInputL gInputR
+ 0x5680, 0x52C0, -0x8000, -0x8000,
+// nRevaOldL nRevaOldR nRevbOldL nRevbOldR
+ 0x031C - 0x00E3, 0x0238 - 0x00E3, 0x0154 - 0x00A9, 0x00AA - 0x00A9,
+// nLwlNew nRwrNew nEcho0L nEcho0R nEcho1L nEcho1R
+ 0x0DFB, 0x0B58, 0x0D09, 0x0A3C, 0x0BD9, 0x0973,
+// nLwlOld nRwrOld nLwrNew nRwlNew nEcho2L nEcho2R nEcho3L nEcho3R
+ 0x0B59, 0x08DA, 0x08D9, 0x05E9, 0x07EC, 0x04B0, 0x06EF, 0x03D2,
+// nLwrOld nRwlOld nRevaNewL nRevaNewR nRevbNewL nRevbNewR
+ 0x05EA, 0x031D, 0x031C, 0x0238, 0x0154, 0x00AA
+};
+
+static ReverbParamsSW reverb_params_Hall = {
+ 0xADE0/2,
+// gLPF gEcho0 gEcho1 gEcho2 gEcho3 gWall
+ 0x6000, 0x5000, 0x4C00, -0x4800, -0x4400, -0x4000,
+// gReva gRevb gInputL gInputR
+ 0x6000, 0x5C00, -0x8000, -0x8000,
+// nRevaOldL nRevaOldR nRevbOldL nRevbOldR
+ 0x05C0 - 0x01A5, 0x041A - 0x01A5, 0x0274 - 0x0139, 0x013A - 0x0139,
+// nLwlNew nRwrNew nEcho0L nEcho0R nEcho1L nEcho1R
+ 0x15BA, 0x11BB, 0x14C2, 0x10BD, 0x11BC, 0x0DC1,
+// nLwlOld nRwrOld nLwrNew nRwlNew nEcho2L nEcho2R nEcho3L nEcho3R
+ 0x11C0, 0x0DC3, 0x0DC0, 0x09C1, 0x0BC4, 0x07C1, 0x0A00, 0x06CD,
+// nLwrOld nRwlOld nRevaNewL nRevaNewR nRevbNewL nRevbNewR
+ 0x09C2, 0x05C1, 0x05C0, 0x041A, 0x0274, 0x013A
+};
+
+static ReverbParamsSW reverb_params_SpaceEcho = {
+ 0xF6C0/2,
+// gLPF gEcho0 gEcho1 gEcho2 gEcho3 gWall
+ 0x7E00, 0x5000, -0x4C00, -0x5000, 0x4C00, -0x5000,
+// gReva gRevb gInputL gInputR
+ 0x6000, 0x5400, -0x8000, -0x8000,
+// nRevaOldL nRevaOldR nRevbOldL nRevbOldR
+ 0x0AE0 - 0x033D, 0x07A2 - 0x033D, 0x0464 - 0x0231, 0x0232 - 0x0231,
+// nLwlNew nRwrNew nEcho0L nEcho0R nEcho1L nEcho1R
+ 0x1ED6, 0x1A31, 0x1D14, 0x183B, 0x1BC2, 0x16B2,
+// nLwlOld nRwrOld nLwrNew nRwlNew nEcho2L nEcho2R nEcho3L nEcho3R
+ 0x1A32, 0x15EF, 0x15EE, 0x1055, 0x1334, 0x0F2D, 0x11F6, 0x0C5D,
+// nLwrOld nRwlOld nRevaNewL nRevaNewR nRevbNewL nRevbNewR
+ 0x1056, 0x0AE1, 0x0AE0, 0x07A2, 0x0464, 0x0232
+};
+
+static ReverbParamsSW reverb_params_Echo = {
+ 0x18040/2,
+// gLPF gEcho0 gEcho1 gEcho2 gEcho3 gWall
+ 0x7FFF, 0x7FFF, 0x0000, 0x0000, 0x0000, -0x7F00,
+// gReva gRevb gInputL gInputR
+ 0x0000, 0x0000, -0x8000, -0x8000,
+// nRevaOldL nRevaOldR nRevbOldL nRevbOldR
+ 0x1004 - 0x0001, 0x1002 - 0x0001, 0x0004 - 0x0001, 0x0002 - 0x0001,
+// nLwlNew nRwrNew nEcho0L nEcho0R nEcho1L nEcho1R
+ 0x1FFF, 0x0FFF, 0x1005, 0x0005, 0x0000, 0x0000,
+// nLwlOld nRwrOld nLwrNew nRwlNew nEcho2L nEcho2R nEcho3L nEcho3R
+ 0x1005, 0x0005, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+// nLwrOld nRwlOld nRevaNewL nRevaNewR nRevbNewL nRevbNewR
+ 0x0000, 0x0000, 0x1004, 0x1002, 0x0004, 0x0002
+};
+
+static ReverbParamsSW reverb_params_Delay = {
+ 0x18040/2,
+// gLPF gEcho0 gEcho1 gEcho2 gEcho3 gWall
+ 0x7FFF, 0x7FFF, 0x0000, 0x0000, 0x0000, 0x0000,
+// gReva gRevb gInputL gInputR
+ 0x0000, 0x0000, -0x8000, -0x8000,
+// nRevaOldL nRevaOldR nRevbOldL nRevbOldR
+ 0x1004 - 0x0001, 0x1002 - 0x0001, 0x0004 - 0x0001, 0x0002 - 0x0001,
+// nLwlNew nRwrNew nEcho0L nEcho0R nEcho1L nEcho1R
+ 0x1FFF, 0x0FFF, 0x1005, 0x0005, 0x0000, 0x0000,
+// nLwlOld nRwrOld nLwrNew nRwlNew nEcho2L nEcho2R nEcho3L nEcho3R
+ 0x1005, 0x0005, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+// nLwrOld nRwlOld nRevaNewL nRevaNewR nRevbNewL nRevbNewR
+ 0x0000, 0x0000, 0x1004, 0x1002, 0x0004, 0x0002
+};
+
+static ReverbParamsSW reverb_params_HalfEcho = {
+ 0x3C00/2,
+// gLPF gEcho0 gEcho1 gEcho2 gEcho3 gWall
+ 0x70F0, 0x4FA8, -0x4320, 0x4510, -0x4110, -0x7B00,
+// gReva gRevb gInputL gInputR
+ 0x5F80, 0x54C0, -0x8000, -0x8000,
+// nRevaOldL nRevaOldR nRevbOldL nRevbOldR
+ 0x0058 - 0x0017, 0x0040 - 0x0017, 0x0028 - 0x0013, 0x0014 - 0x0013,
+// nLwlNew nRwrNew nEcho0L nEcho0R nEcho1L nEcho1R
+ 0x0371, 0x02AF, 0x02E5, 0x01DF, 0x02B0, 0x01D7,
+// nLwlOld nRwrOld nLwrNew nRwlNew nEcho2L nEcho2R nEcho3L nEcho3R
+ 0x0358, 0x026A, 0x01D6, 0x011E, 0x012D, 0x00B1, 0x011F, 0x0059,
+// nLwrOld nRwlOld nRevaNewL nRevaNewR nRevbNewL nRevbNewR
+ 0x01A0, 0x00E3, 0x0058, 0x0040, 0x0028, 0x0014
+};
+
+
+static ReverbParamsSW * reverb_param_modes[] = {
+ &reverb_params_Room,
+ &reverb_params_StudioSmall,
+ &reverb_params_StudioMedium,
+ &reverb_params_StudioLarge,
+ &reverb_params_Hall,
+ &reverb_params_SpaceEcho,
+ &reverb_params_Echo,
+ &reverb_params_Delay,
+ &reverb_params_HalfEcho,
+};
+
+bool ReverbSW::process(int *p_input,int *p_output,int p_frames,int p_stereo_stride) {
+
+ if (!reverb_buffer)
+ return false;
+
+
+ //
+ // p_input must point to a non-looping buffer.
+ // BOTH p_input and p_output must be touched (use ClearModuleBuffer).
+
+ // �������LOCAL MACROS ������۲
+
+#undef LM_SETSRCOFFSET
+#define LM_SETSRCOFFSET(x) \
+ (x) = current_params->x + Offset; \
+ if ( (x) >= reverb_buffer_size ) { \
+ (x) -= reverb_buffer_size; \
+ } \
+ SETMIN ( aSample, reverb_buffer_size - (x) );
+
+/*
+#undef LM_SETSRCOFFSET2
+#define LM_SETSRCOFFSET2(x,y) \
+ (x) = ((y) << 3) >> HZShift; \
+ (x) += Offset; \
+ if ( (x) >= reverb_buffer_size ) { \
+ (x) -= reverb_buffer_size; \
+ } \
+ SETMIN ( aSample, reverb_buffer_size - (x) );
+*/
+#undef LM_SRCADVANCE
+#define LM_SRCADVANCE(x) \
+ (x) += aSample;
+
+#undef LM_MUL
+#define LM_MUL(x,y) \
+MULSHIFT_S32 ( x, current_params->y, 15 )
+
+#undef LM_REVERB
+#define LM_REVERB(x) reverb_buffer[ (x) + cSample ]
+
+ // �������LOCAL VARIABLES ������۲
+
+ unsigned int Offset;
+
+ int lwl, lwr, rwl, rwr;
+// unsigned char HZShift;
+
+ // �������CODE ������۲
+
+
+ lwl = state.lwl;
+ lwr = state.lwr;
+ rwl = state.rwl;
+ rwr = state.rwr;
+ Offset = state.Offset;
+
+ int max=0;
+
+ while ( p_frames ) {
+
+ // Offsets
+
+ unsigned int nLwlOld;
+ unsigned int nRwrOld;
+ unsigned int nLwlNew;
+ unsigned int nRwrNew;
+
+ unsigned int nLwrOld;
+ unsigned int nRwlOld;
+ unsigned int nLwrNew;
+ unsigned int nRwlNew;
+
+ unsigned int nEcho0L;
+ unsigned int nEcho1L;
+ unsigned int nEcho2L;
+ unsigned int nEcho3L;
+
+ unsigned int nEcho0R;
+ unsigned int nEcho1R;
+ unsigned int nEcho2R;
+ unsigned int nEcho3R;
+
+ unsigned int nRevaOldL;
+ unsigned int nRevaOldR;
+ unsigned int nRevbOldL;
+ unsigned int nRevbOldR;
+
+ unsigned int nRevaNewL;
+ unsigned int nRevaNewR;
+ unsigned int nRevbNewL;
+ unsigned int nRevbNewR;
+
+ // Other variables
+
+ unsigned int aSample = p_frames;
+
+ // Set initial offsets
+
+ LM_SETSRCOFFSET ( nLwlOld );
+ LM_SETSRCOFFSET ( nRwrOld );
+ LM_SETSRCOFFSET ( nLwlNew );
+ LM_SETSRCOFFSET ( nRwrNew );
+ LM_SETSRCOFFSET ( nLwrOld );
+ LM_SETSRCOFFSET ( nRwlOld );
+ LM_SETSRCOFFSET ( nLwrNew );
+ LM_SETSRCOFFSET ( nRwlNew );
+ LM_SETSRCOFFSET ( nEcho0L );
+ LM_SETSRCOFFSET ( nEcho1L );
+ LM_SETSRCOFFSET ( nEcho2L );
+ LM_SETSRCOFFSET ( nEcho3L );
+ LM_SETSRCOFFSET ( nEcho0R );
+ LM_SETSRCOFFSET ( nEcho1R );
+ LM_SETSRCOFFSET ( nEcho2R );
+ LM_SETSRCOFFSET ( nEcho3R );
+ LM_SETSRCOFFSET ( nRevaOldL );
+ LM_SETSRCOFFSET ( nRevaOldR );
+ LM_SETSRCOFFSET ( nRevbOldL );
+ LM_SETSRCOFFSET ( nRevbOldR );
+ LM_SETSRCOFFSET ( nRevaNewL );
+ LM_SETSRCOFFSET ( nRevaNewR );
+ LM_SETSRCOFFSET ( nRevbNewL );
+ LM_SETSRCOFFSET ( nRevbNewR );
+
+ //SETMIN ( aSample, p_output.Size - p_output.Offset );
+
+ for (unsigned int cSample=0;cSample<aSample;cSample++) {
+
+ int tempL0, tempL1, tempR0, tempR1;
+
+ tempL1 = p_input[(cSample<<p_stereo_stride)]>>8;
+ tempR1 = p_input[(cSample<<p_stereo_stride) + 1]>>8;
+
+ tempL0 = LM_MUL ( tempL1, gInputL );
+ tempR0 = LM_MUL ( tempR1, gInputR );
+
+ /*
+ Left -> Wall -> Left Reflection
+ */
+ tempL1 = tempL0 + LM_MUL ( LM_REVERB( nLwlOld ), gWall );
+ tempR1 = tempR0 + LM_MUL ( LM_REVERB( nRwrOld ), gWall );
+ lwl += LM_MUL ( tempL1 - lwl, gLPF );
+ rwr += LM_MUL ( tempR1 - rwr, gLPF );
+ LM_REVERB( nLwlNew ) = lwl;
+ LM_REVERB( nRwrNew ) = rwr;
+ /*
+ Left -> Wall -> Right Reflection
+ */
+ tempL1 = tempL0 + LM_MUL ( LM_REVERB( nRwlOld ), gWall );
+ tempR1 = tempR0 + LM_MUL ( LM_REVERB( nLwrOld ), gWall );
+ lwr += LM_MUL ( tempL1 - lwr, gLPF );
+ rwl += LM_MUL ( tempR1 - rwl, gLPF );
+ LM_REVERB( nLwrNew ) = lwr;
+ LM_REVERB( nRwlNew ) = rwl;
+ /*
+ Early Echo(Early Reflection)
+ */
+ tempL0 =
+ LM_MUL ( LM_REVERB( nEcho0L ), gEcho0 ) +
+ LM_MUL ( LM_REVERB( nEcho1L ), gEcho1 ) +
+ LM_MUL ( LM_REVERB( nEcho2L ), gEcho2 ) +
+ LM_MUL ( LM_REVERB( nEcho3L ), gEcho3 );
+ tempR0 =
+ LM_MUL ( LM_REVERB( nEcho0R ), gEcho0 ) +
+ LM_MUL ( LM_REVERB( nEcho1R ), gEcho1 ) +
+ LM_MUL ( LM_REVERB( nEcho2R ), gEcho2 ) +
+ LM_MUL ( LM_REVERB( nEcho3R ), gEcho3 );
+ /*
+ Late Reverb
+ */
+ tempL1 = LM_REVERB( nRevaOldL );
+ tempR1 = LM_REVERB( nRevaOldR );
+ tempL0 -= LM_MUL ( tempL1, gReva );
+ tempR0 -= LM_MUL ( tempR1, gReva );
+ LM_REVERB( nRevaNewL ) = tempL0;
+ LM_REVERB( nRevaNewR ) = tempR0;
+ tempL0 = LM_MUL ( tempL0, gReva ) + tempL1;
+ tempR0 = LM_MUL ( tempR0, gReva ) + tempR1;
+ tempL1 = LM_REVERB( nRevbOldL );
+ tempR1 = LM_REVERB( nRevbOldR );
+ tempL0 -= LM_MUL ( tempL1, gRevb );
+ tempR0 -= LM_MUL ( tempR1, gRevb );
+ LM_REVERB( nRevbNewL ) = tempL0;
+ LM_REVERB( nRevbNewR ) = tempR0;
+ tempL0 = LM_MUL ( tempL0, gRevb ) + tempL1;
+ tempR0 = LM_MUL ( tempR0, gRevb ) + tempR1;
+ /*
+ Output
+ */
+
+ max|=abs(tempL0);
+ max|=abs(tempR0);
+
+ p_output[(cSample<<p_stereo_stride)] += tempL0<<8;
+ p_output[(cSample<<p_stereo_stride) + 1] += tempR0<<8;
+
+ }
+
+ // Advance offsets
+
+ Offset += aSample;
+ if ( Offset >= reverb_buffer_size ) { Offset -= reverb_buffer_size; }
+
+ p_input += aSample << p_stereo_stride;
+ p_output += aSample << p_stereo_stride;
+
+ p_frames -= aSample;
+ }
+
+ state.lwl = lwl;
+ state.lwr = lwr;
+ state.rwl = rwl;
+ state.rwr = rwr;
+ state.Offset = Offset;
+
+ return (max&0x7FFFFF00)!=0; // audio was mixed?
+}
+
+void ReverbSW::adjust_current_params() {
+
+ *current_params=*reverb_param_modes[mode];
+
+ uint32_t maxofs=0;
+
+#define LM_CONFIG_PARAM( x )\
+ current_params->x=(int)( ( (int64_t)current_params->x*(int64_t)mix_rate*8L)/(int64_t)44100);\
+ if (current_params->x>maxofs)\
+ maxofs=current_params->x;
+
+
+ LM_CONFIG_PARAM ( nLwlOld );
+ LM_CONFIG_PARAM ( nRwrOld );
+ LM_CONFIG_PARAM ( nLwlNew );
+ LM_CONFIG_PARAM ( nRwrNew );
+ LM_CONFIG_PARAM ( nLwrOld );
+ LM_CONFIG_PARAM ( nRwlOld );
+ LM_CONFIG_PARAM ( nLwrNew );
+ LM_CONFIG_PARAM ( nRwlNew );
+ LM_CONFIG_PARAM ( nEcho0L );
+ LM_CONFIG_PARAM ( nEcho1L );
+ LM_CONFIG_PARAM ( nEcho2L );
+ LM_CONFIG_PARAM ( nEcho3L );
+ LM_CONFIG_PARAM ( nEcho0R );
+ LM_CONFIG_PARAM ( nEcho1R );
+ LM_CONFIG_PARAM ( nEcho2R );
+ LM_CONFIG_PARAM ( nEcho3R );
+ LM_CONFIG_PARAM ( nRevaOldL );
+ LM_CONFIG_PARAM ( nRevaOldR );
+ LM_CONFIG_PARAM ( nRevbOldL );
+ LM_CONFIG_PARAM ( nRevbOldR );
+ LM_CONFIG_PARAM ( nRevaNewL );
+ LM_CONFIG_PARAM ( nRevaNewR );
+ LM_CONFIG_PARAM ( nRevbNewL );
+ LM_CONFIG_PARAM ( nRevbNewR );
+
+ int needed_buffer_size=maxofs+1;
+ if (reverb_buffer)
+ memdelete_arr(reverb_buffer);
+
+ reverb_buffer = memnew_arr(int,needed_buffer_size);
+ reverb_buffer_size=needed_buffer_size;
+
+ for (uint32_t i=0;i<reverb_buffer_size;i++)
+ reverb_buffer[i]=0;
+
+ state.reset();
+
+
+}
+
+void ReverbSW::set_mode(ReverbMode p_mode) {
+
+ if (mode==p_mode)
+ return;
+
+ mode=p_mode;
+
+ adjust_current_params();
+}
+
+void ReverbSW::set_mix_rate(int p_mix_rate) {
+
+ if (p_mix_rate==mix_rate)
+ return;
+
+ mix_rate=p_mix_rate;
+
+ adjust_current_params();
+
+}
+
+
+ReverbSW::ReverbSW() {
+
+ reverb_buffer=0;
+ reverb_buffer_size=0;
+ mode=REVERB_MODE_ROOM;
+ mix_rate=1;
+ current_params = memnew(ReverbParamsSW);
+}
+
+
+ReverbSW::~ReverbSW() {
+
+
+ if (reverb_buffer)
+ memdelete_arr(reverb_buffer);
+
+ memdelete(current_params);
+
+}
+
diff --git a/servers/audio/reverb_sw.h b/servers/audio/reverb_sw.h
new file mode 100644
index 000000000..acf22f01b
--- /dev/null
+++ b/servers/audio/reverb_sw.h
@@ -0,0 +1,84 @@
+/*************************************************************************/
+/* reverb_sw.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#ifndef REVERB_SW_H
+#define REVERB_SW_H
+
+#include "typedefs.h"
+#include "os/memory.h"
+
+class ReverbParamsSW;
+
+class ReverbSW {
+public:
+ enum ReverbMode {
+ REVERB_MODE_ROOM,
+ REVERB_MODE_STUDIO_SMALL,
+ REVERB_MODE_STUDIO_MEDIUM,
+ REVERB_MODE_STUDIO_LARGE,
+ REVERB_MODE_HALL,
+ REVERB_MODE_SPACE_ECHO,
+ REVERB_MODE_ECHO,
+ REVERB_MODE_DELAY,
+ REVERB_MODE_HALF_ECHO
+ };
+
+private:
+ struct State {
+ int lwl;
+ int lwr;
+ int rwl;
+ int rwr;
+ unsigned int Offset;
+ void reset() { lwl=0; lwr=0; rwl=0; rwr=0; Offset=0; }
+ State() { reset(); }
+ } state;
+
+ ReverbParamsSW *current_params;
+
+
+ int *reverb_buffer;
+ unsigned int reverb_buffer_size;
+ ReverbMode mode;
+ int mix_rate;
+
+ void adjust_current_params();
+
+public:
+
+
+ void set_mode(ReverbMode p_mode);
+ bool process(int *p_input,int *p_output,int p_frames,int p_stereo_stride=1); // return tru if audio was created
+ void set_mix_rate(int p_mix_rate);
+
+ ReverbSW();
+ ~ReverbSW();
+
+};
+
+#endif
diff --git a/servers/audio/sample_manager_sw.cpp b/servers/audio/sample_manager_sw.cpp
new file mode 100644
index 000000000..2c065a937
--- /dev/null
+++ b/servers/audio/sample_manager_sw.cpp
@@ -0,0 +1,280 @@
+/*************************************************************************/
+/* sample_manager_sw.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#include "sample_manager_sw.h"
+
+#include "print_string.h"
+
+SampleManagerSW::~SampleManagerSW()
+{
+}
+
+
+
+RID SampleManagerMallocSW::sample_create(AS::SampleFormat p_format, bool p_stereo, int p_length) {
+
+ ERR_EXPLAIN("IMA-ADPCM and STEREO are not a valid combination for sample format.");
+ ERR_FAIL_COND_V( p_format == AS::SAMPLE_FORMAT_IMA_ADPCM && p_stereo,RID());
+ Sample *s = memnew( Sample );
+ int datalen = p_length;
+ if (p_stereo)
+ datalen*=2;
+ if (p_format==AS::SAMPLE_FORMAT_PCM16)
+ datalen*=2;
+ else if (p_format==AS::SAMPLE_FORMAT_IMA_ADPCM)
+ datalen/=2;
+#define SAMPLE_EXTRA 16
+
+ s->data = memalloc(datalen+SAMPLE_EXTRA); //help the interpolator by allocating a little more..
+ for(int i=0;i<SAMPLE_EXTRA;i++) {
+
+ uint8_t *data = (uint8_t*)s->data;
+ data[datalen+i]=0;
+ }
+ if (!s->data) {
+
+ memdelete(s);
+ ERR_EXPLAIN("Cannot allocate sample of requested size.");
+ ERR_FAIL_V(RID());
+ }
+
+ s->format=p_format;
+ s->length=p_length;
+ s->length_bytes=datalen;
+ s->stereo=p_stereo;
+ s->loop_begin=0;
+ s->loop_end=0;
+ s->loop_format=AS::SAMPLE_LOOP_NONE;
+ s->mix_rate=44100;
+
+ AudioServer::get_singleton()->lock();
+ RID rid = sample_owner.make_rid(s);
+ AudioServer::get_singleton()->unlock();
+
+ return rid;
+}
+
+void SampleManagerMallocSW::sample_set_description(RID p_sample, const String& p_description) {
+
+ Sample *s = sample_owner.get(p_sample);
+ ERR_FAIL_COND(!s);
+
+ s->description=p_description;
+}
+
+String SampleManagerMallocSW::sample_get_description(RID p_sample) const {
+
+ const Sample *s = sample_owner.get(p_sample);
+ ERR_FAIL_COND_V(!s,String());
+
+ return s->description;
+}
+
+
+AS::SampleFormat SampleManagerMallocSW::sample_get_format(RID p_sample) const {
+
+ const Sample *s = sample_owner.get(p_sample);
+ ERR_FAIL_COND_V(!s,AS::SAMPLE_FORMAT_PCM8);
+
+ return s->format;
+}
+
+bool SampleManagerMallocSW::sample_is_stereo(RID p_sample) const {
+
+ const Sample *s = sample_owner.get(p_sample);
+ ERR_FAIL_COND_V(!s,false);
+
+ return s->stereo;
+
+}
+int SampleManagerMallocSW::sample_get_length(RID p_sample) const {
+
+ const Sample *s = sample_owner.get(p_sample);
+ ERR_FAIL_COND_V(!s,-1);
+
+ return s->length;
+}
+
+void SampleManagerMallocSW::sample_set_data(RID p_sample, const DVector<uint8_t>& p_buffer) {
+
+ Sample *s = sample_owner.get(p_sample);
+ ERR_FAIL_COND(!s);
+
+ int buff_size=p_buffer.size();
+ ERR_FAIL_COND(buff_size==0);
+
+ ERR_EXPLAIN("Sample buffer size does not match sample size.");
+ ERR_FAIL_COND(s->length_bytes!=buff_size);
+ DVector<uint8_t>::Read buffer_r=p_buffer.read();
+ const uint8_t *src = buffer_r.ptr();
+ uint8_t *dst = (uint8_t*)s->data;
+
+ for(int i=0;i<s->length_bytes;i++) {
+
+ dst[i]=src[i];
+ }
+
+}
+
+const DVector<uint8_t> SampleManagerMallocSW::sample_get_data(RID p_sample) const {
+
+ Sample *s = sample_owner.get(p_sample);
+ ERR_FAIL_COND_V(!s,DVector<uint8_t>());
+
+ DVector<uint8_t> ret_buffer;
+ ret_buffer.resize(s->length_bytes);
+ DVector<uint8_t>::Write buffer_w=ret_buffer.write();
+ uint8_t *dst = buffer_w.ptr();
+ const uint8_t *src = (const uint8_t*)s->data;
+
+ for(int i=0;i<s->length_bytes;i++) {
+
+ dst[i]=src[i];
+ }
+
+ buffer_w = DVector<uint8_t>::Write(); //unlock
+
+ return ret_buffer;
+}
+
+void *SampleManagerMallocSW::sample_get_data_ptr(RID p_sample) const {
+
+ const Sample *s = sample_owner.get(p_sample);
+ ERR_FAIL_COND_V(!s,NULL);
+
+ return s->data;
+
+}
+
+void SampleManagerMallocSW::sample_set_mix_rate(RID p_sample,int p_rate) {
+
+ ERR_FAIL_COND(p_rate<1);
+
+ Sample *s = sample_owner.get(p_sample);
+ ERR_FAIL_COND(!s);
+
+ s->mix_rate=p_rate;
+
+
+}
+int SampleManagerMallocSW::sample_get_mix_rate(RID p_sample) const {
+
+ const Sample *s = sample_owner.get(p_sample);
+ ERR_FAIL_COND_V(!s,-1);
+
+ return s->mix_rate;
+
+}
+void SampleManagerMallocSW::sample_set_loop_format(RID p_sample,AS::SampleLoopFormat p_format) {
+
+ Sample *s = sample_owner.get(p_sample);
+ ERR_FAIL_COND(!s);
+
+ s->loop_format=p_format;
+
+}
+AS::SampleLoopFormat SampleManagerMallocSW::sample_get_loop_format(RID p_sample) const {
+
+ const Sample *s = sample_owner.get(p_sample);
+ ERR_FAIL_COND_V(!s,AS::SAMPLE_LOOP_NONE);
+
+ return s->loop_format;
+}
+
+void SampleManagerMallocSW::sample_set_loop_begin(RID p_sample,int p_pos) {
+
+ Sample *s = sample_owner.get(p_sample);
+ ERR_FAIL_COND(!s);
+ ERR_FAIL_INDEX(p_pos,s->length);
+
+ s->loop_begin=p_pos;
+}
+int SampleManagerMallocSW::sample_get_loop_begin(RID p_sample) const {
+
+ const Sample *s = sample_owner.get(p_sample);
+ ERR_FAIL_COND_V(!s,-1);
+
+ return s->loop_begin;
+}
+
+void SampleManagerMallocSW::sample_set_loop_end(RID p_sample,int p_pos) {
+
+ Sample *s = sample_owner.get(p_sample);
+ ERR_FAIL_COND(!s);
+ if (p_pos>s->length)
+ p_pos=s->length;
+ s->loop_end=p_pos;
+
+}
+int SampleManagerMallocSW::sample_get_loop_end(RID p_sample) const {
+
+ const Sample *s = sample_owner.get(p_sample);
+ ERR_FAIL_COND_V(!s,-1);
+
+ return s->loop_end;
+}
+
+bool SampleManagerMallocSW::is_sample(RID p_sample) const {
+
+ return sample_owner.owns(p_sample);
+
+}
+void SampleManagerMallocSW::free(RID p_sample) {
+
+ Sample *s = sample_owner.get(p_sample);
+ ERR_FAIL_COND(!s);
+ AudioServer::get_singleton()->lock();
+ sample_owner.free(p_sample);
+ AudioServer::get_singleton()->unlock();
+
+ memfree(s->data);
+ memdelete(s);
+
+}
+
+SampleManagerMallocSW::SampleManagerMallocSW() {
+
+
+}
+
+SampleManagerMallocSW::~SampleManagerMallocSW() {
+
+ // check for sample leakage
+ List<RID> owned_list;
+ sample_owner.get_owned_list(&owned_list);
+
+ while(owned_list.size()) {
+
+ Sample *s = sample_owner.get(owned_list.front()->get());
+ String err="Leaked sample of size: "+itos(s->length_bytes)+" description: "+s->description;
+ ERR_PRINT(err.utf8().get_data());
+ free(owned_list.front()->get());
+ owned_list.pop_front();
+ }
+
+}
diff --git a/servers/audio/sample_manager_sw.h b/servers/audio/sample_manager_sw.h
new file mode 100644
index 000000000..5de1b4038
--- /dev/null
+++ b/servers/audio/sample_manager_sw.h
@@ -0,0 +1,129 @@
+/*************************************************************************/
+/* sample_manager_sw.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#ifndef SAMPLE_MANAGER_SW_H
+#define SAMPLE_MANAGER_SW_H
+
+#include "servers/audio_server.h"
+
+class SampleManagerSW {
+public:
+
+ /* SAMPLE API */
+
+ virtual RID sample_create(AS::SampleFormat p_format, bool p_stereo, int p_length)=0;
+
+ virtual void sample_set_description(RID p_sample, const String& p_description)=0;
+ virtual String sample_get_description(RID p_sample) const=0;
+
+ virtual AS::SampleFormat sample_get_format(RID p_sample) const=0;
+ virtual bool sample_is_stereo(RID p_sample) const=0;
+ virtual int sample_get_length(RID p_sample) const=0;
+
+ virtual void sample_set_data(RID p_sample, const DVector<uint8_t>& p_buffer)=0;
+ virtual const DVector<uint8_t> sample_get_data(RID p_sample) const=0;
+
+ virtual void *sample_get_data_ptr(RID p_sample) const=0;
+
+ virtual void sample_set_mix_rate(RID p_sample,int p_rate)=0;
+ virtual int sample_get_mix_rate(RID p_sample) const=0;
+
+ virtual void sample_set_loop_format(RID p_sample,AS::SampleLoopFormat p_format)=0;
+ virtual AS::SampleLoopFormat sample_get_loop_format(RID p_sample) const=0;
+
+ virtual void sample_set_loop_begin(RID p_sample,int p_pos)=0;
+ virtual int sample_get_loop_begin(RID p_sample) const=0;
+
+ virtual void sample_set_loop_end(RID p_sample,int p_pos)=0;
+ virtual int sample_get_loop_end(RID p_sample) const=0;
+
+ virtual bool is_sample(RID) const=0;
+ virtual void free(RID p_sample)=0;
+
+
+
+ virtual ~SampleManagerSW();
+};
+
+
+class SampleManagerMallocSW : public SampleManagerSW {
+
+
+ struct Sample {
+
+ void *data;
+ int length;
+ int length_bytes;
+ AS::SampleFormat format;
+ bool stereo;
+ AS::SampleLoopFormat loop_format;
+ int loop_begin;
+ int loop_end;
+ int mix_rate;
+ String description;
+ };
+
+ mutable RID_Owner<Sample> sample_owner;
+public:
+
+ /* SAMPLE API */
+
+ virtual RID sample_create(AS::SampleFormat p_format, bool p_stereo, int p_length);
+
+ virtual void sample_set_description(RID p_sample, const String& p_description);
+ virtual String sample_get_description(RID p_sample) const;
+
+ virtual AS::SampleFormat sample_get_format(RID p_sample) const;
+ virtual bool sample_is_stereo(RID p_sample) const;
+ virtual int sample_get_length(RID p_sample) const;
+
+ virtual void sample_set_data(RID p_sample, const DVector<uint8_t>& p_buffer);
+ virtual const DVector<uint8_t> sample_get_data(RID p_sample) const;
+
+ virtual void *sample_get_data_ptr(RID p_sample) const;
+
+ virtual void sample_set_mix_rate(RID p_sample,int p_rate);
+ virtual int sample_get_mix_rate(RID p_sample) const;
+
+ virtual void sample_set_loop_format(RID p_sample,AS::SampleLoopFormat p_format);
+ virtual AS::SampleLoopFormat sample_get_loop_format(RID p_sample) const;
+
+ virtual void sample_set_loop_begin(RID p_sample,int p_pos);
+ virtual int sample_get_loop_begin(RID p_sample) const;
+
+ virtual void sample_set_loop_end(RID p_sample,int p_pos);
+ virtual int sample_get_loop_end(RID p_sample) const;
+
+ virtual bool is_sample(RID) const;
+ virtual void free(RID p_sample);
+
+ SampleManagerMallocSW();
+ virtual ~SampleManagerMallocSW();
+};
+
+#endif // SAMPLE_MANAGER_SW_H
diff --git a/servers/audio/voice_rb_sw.cpp b/servers/audio/voice_rb_sw.cpp
new file mode 100644
index 000000000..93f176b75
--- /dev/null
+++ b/servers/audio/voice_rb_sw.cpp
@@ -0,0 +1,34 @@
+/*************************************************************************/
+/* voice_rb_sw.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#include "voice_rb_sw.h"
+/*
+VoiceRBSW::VoiceRBSW()
+{
+}
+*/
diff --git a/servers/audio/voice_rb_sw.h b/servers/audio/voice_rb_sw.h
new file mode 100644
index 000000000..7fcdebaa1
--- /dev/null
+++ b/servers/audio/voice_rb_sw.h
@@ -0,0 +1,146 @@
+/*************************************************************************/
+/* voice_rb_sw.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#ifndef VOICE_RB_SW_H
+#define VOICE_RB_SW_H
+
+#include "servers/audio_server.h"
+#include "os/os.h"
+class VoiceRBSW {
+public:
+
+ enum {
+ VOICE_RB_SIZE=1024
+ };
+
+ struct Command {
+
+ enum Type {
+ CMD_NONE,
+ CMD_PLAY,
+ CMD_STOP,
+ CMD_SET_VOLUME,
+ CMD_SET_PAN,
+ CMD_SET_FILTER,
+ CMD_SET_CHORUS,
+ CMD_SET_REVERB,
+ CMD_SET_MIX_RATE,
+ CMD_SET_POSITIONAL,
+ CMD_CHANGE_ALL_FX_VOLUMES
+ };
+
+ Type type;
+ RID voice;
+
+ struct {
+
+ RID sample;
+
+ } play;
+
+ union {
+
+ struct {
+
+ float volume;
+ } volume;
+
+ struct {
+
+ float pan,depth,height;
+ } pan;
+
+ struct {
+
+ AS::FilterType type;
+ float cutoff;
+ float resonance;
+ float gain;
+ } filter;
+
+ struct {
+ float send;
+ } chorus;
+ struct {
+ float send;
+ AS::ReverbRoomType room;
+ } reverb;
+
+ struct {
+
+ int mix_rate;
+ } mix_rate;
+
+ struct {
+
+ bool positional;
+ } positional;
+
+ };
+
+ Command() { type=CMD_NONE; }
+
+ };
+private:
+
+ Command voice_cmd_rb[VOICE_RB_SIZE];
+ volatile int read_pos;
+ volatile int write_pos;
+
+public:
+
+ _FORCE_INLINE_ bool commands_left() const { return read_pos!=write_pos; }
+ _FORCE_INLINE_ Command pop_command() {
+ ERR_FAIL_COND_V( read_pos==write_pos, Command() );
+ Command cmd=voice_cmd_rb[read_pos];
+ read_pos=(read_pos+1)%VOICE_RB_SIZE;
+ return cmd;
+ }
+ _FORCE_INLINE_ void push_command(const Command& p_command) {
+
+ bool full = ((write_pos+1)%VOICE_RB_SIZE)==read_pos;
+ if (full) {
+#ifdef DEBUG_ENABLED
+ if (OS::get_singleton()->is_stdout_verbose()) {
+ ERR_EXPLAIN("Audio Ring Buffer Full (too many commands");
+ ERR_FAIL_COND( ((write_pos+1)%VOICE_RB_SIZE)==read_pos);
+ }
+#endif
+ return;
+ }
+
+ voice_cmd_rb[write_pos]=p_command;
+ write_pos=(write_pos+1)%VOICE_RB_SIZE;
+
+ }
+
+ VoiceRBSW() { read_pos=write_pos=0; }
+
+};
+
+#endif // VOICE_RB_SW_H
diff --git a/servers/audio_server.cpp b/servers/audio_server.cpp
new file mode 100644
index 000000000..81dab367c
--- /dev/null
+++ b/servers/audio_server.cpp
@@ -0,0 +1,178 @@
+/*************************************************************************/
+/* audio_server.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#include "audio_server.h"
+#include "globals.h"
+
+void AudioMixer::audio_mixer_chunk_call(int p_frames) {
+
+ AudioServer::get_singleton()->audio_mixer_chunk_callback(p_frames);
+}
+
+AudioMixer *AudioServer::EventStream::get_mixer() const {
+
+ return AudioServer::get_singleton()->get_mixer();
+}
+
+AudioServer *AudioServer::singleton=NULL;
+
+AudioServer *AudioServer::get_singleton() {
+
+ return singleton;
+}
+
+void AudioServer::sample_set_signed_data(RID p_sample, const DVector<float>& p_buffer) {
+
+ int len = p_buffer.size();
+ ERR_FAIL_COND( len == 0 );
+
+ DVector<uint8_t> data;
+ data.resize(len*2);
+ DVector<uint8_t>::Write w=data.write();
+
+ int16_t *samples = (int16_t*)w.ptr();
+
+ DVector<float>::Read r = p_buffer.read();
+
+ for(int i=0;i<len;i++) {
+
+ float sample = r[i];
+ sample = Math::floor( sample * (1<<16) );
+ if (sample<-32768)
+ sample=-32768;
+ else if (sample>32767)
+ sample=32767;
+ samples[i]=sample;
+ }
+
+ w = DVector<uint8_t>::Write();
+
+ sample_set_data(p_sample,data);
+
+
+}
+
+void AudioServer::_bind_methods() {
+
+ ObjectTypeDB::bind_method(_MD("sample_create","format","stereo","length"), &AudioServer::sample_create );
+ ObjectTypeDB::bind_method(_MD("sample_set_description","sample","description"), &AudioServer::sample_set_description );
+ ObjectTypeDB::bind_method(_MD("sample_get_description","sample"), &AudioServer::sample_get_description );
+
+ ObjectTypeDB::bind_method(_MD("sample_get_format","sample"), &AudioServer::sample_get_format );
+ ObjectTypeDB::bind_method(_MD("sample_is_stereo","sample"), &AudioServer::sample_is_stereo );
+ ObjectTypeDB::bind_method(_MD("sample_get_length","sample"), &AudioServer::sample_get_length );
+
+ ObjectTypeDB::bind_method(_MD("sample_set_signed_data","sample","data"), &AudioServer::sample_set_signed_data );
+ ObjectTypeDB::bind_method(_MD("sample_set_data","sample"), &AudioServer::sample_set_data );
+ ObjectTypeDB::bind_method(_MD("sample_get_data","sample"), &AudioServer::sample_get_data );
+
+ ObjectTypeDB::bind_method(_MD("sample_set_mix_rate","sample","mix_rate"), &AudioServer::sample_set_mix_rate );
+ ObjectTypeDB::bind_method(_MD("sample_get_mix_rate","sample"), &AudioServer::sample_get_mix_rate );
+
+ ObjectTypeDB::bind_method(_MD("sample_set_loop_format","sample","loop_format"), &AudioServer::sample_set_loop_format );
+ ObjectTypeDB::bind_method(_MD("sample_get_loop_format","sample"), &AudioServer::sample_get_loop_format );
+
+
+ ObjectTypeDB::bind_method(_MD("sample_set_loop_begin","sample","pos"), &AudioServer::sample_set_loop_begin );
+ ObjectTypeDB::bind_method(_MD("sample_get_loop_begin","sample"), &AudioServer::sample_get_loop_begin );
+
+ ObjectTypeDB::bind_method(_MD("sample_set_loop_end","sample","pos"), &AudioServer::sample_set_loop_end );
+ ObjectTypeDB::bind_method(_MD("sample_get_loop_end","sample"), &AudioServer::sample_get_loop_end );
+
+
+
+ ObjectTypeDB::bind_method(_MD("voice_create"), &AudioServer::voice_create );
+ ObjectTypeDB::bind_method(_MD("voice_play","voice","sample"), &AudioServer::voice_play );
+ ObjectTypeDB::bind_method(_MD("voice_set_volume","voice","volume"), &AudioServer::voice_set_volume );
+ ObjectTypeDB::bind_method(_MD("voice_set_pan","voice","pan","depth","height"), &AudioServer::voice_set_pan,DEFVAL(0),DEFVAL(0) );
+ ObjectTypeDB::bind_method(_MD("voice_set_filter","voice","type","cutoff","resonance","gain"), &AudioServer::voice_set_filter,DEFVAL(0) );
+ ObjectTypeDB::bind_method(_MD("voice_set_chorus","voice","chorus"), &AudioServer::voice_set_chorus );
+ ObjectTypeDB::bind_method(_MD("voice_set_reverb","voice","room","reverb"), &AudioServer::voice_set_reverb );
+ ObjectTypeDB::bind_method(_MD("voice_set_mix_rate","voice","rate"), &AudioServer::voice_set_mix_rate );
+ ObjectTypeDB::bind_method(_MD("voice_set_positional","voice","enabled"), &AudioServer::voice_set_positional );
+
+
+ ObjectTypeDB::bind_method(_MD("voice_get_volume","voice"), &AudioServer::voice_get_volume );
+ ObjectTypeDB::bind_method(_MD("voice_get_pan","voice"), &AudioServer::voice_get_pan );
+ ObjectTypeDB::bind_method(_MD("voice_get_pan_height","voice"), &AudioServer::voice_get_pan_height );
+ ObjectTypeDB::bind_method(_MD("voice_get_pan_depth","voice"), &AudioServer::voice_get_pan_depth );
+ ObjectTypeDB::bind_method(_MD("voice_get_filter_type","voice"), &AudioServer::voice_get_filter_type );
+ ObjectTypeDB::bind_method(_MD("voice_get_filter_cutoff","voice"), &AudioServer::voice_get_filter_cutoff );
+ ObjectTypeDB::bind_method(_MD("voice_get_filter_resonance","voice"), &AudioServer::voice_get_filter_resonance );
+ ObjectTypeDB::bind_method(_MD("voice_get_chorus","voice"), &AudioServer::voice_get_chorus );
+ ObjectTypeDB::bind_method(_MD("voice_get_reverb_type","voice"), &AudioServer::voice_get_reverb_type );
+ ObjectTypeDB::bind_method(_MD("voice_get_reverb","voice"), &AudioServer::voice_get_reverb );
+ ObjectTypeDB::bind_method(_MD("voice_get_mix_rate","voice"), &AudioServer::voice_get_mix_rate );
+ ObjectTypeDB::bind_method(_MD("voice_is_positional","voice"), &AudioServer::voice_is_positional );
+
+ ObjectTypeDB::bind_method(_MD("voice_stop","voice"), &AudioServer::voice_stop );
+
+ ObjectTypeDB::bind_method(_MD("free","rid"), &AudioServer::free );
+
+ ObjectTypeDB::bind_method(_MD("set_stream_global_volume_scale","scale"), &AudioServer::set_stream_global_volume_scale );
+ ObjectTypeDB::bind_method(_MD("get_stream_global_volume_scale"), &AudioServer::get_stream_global_volume_scale );
+
+ ObjectTypeDB::bind_method(_MD("set_fx_global_volume_scale","scale"), &AudioServer::set_fx_global_volume_scale );
+ ObjectTypeDB::bind_method(_MD("get_fx_global_volume_scale"), &AudioServer::get_fx_global_volume_scale );
+
+ ObjectTypeDB::bind_method(_MD("set_event_voice_global_volume_scale","scale"), &AudioServer::set_event_voice_global_volume_scale );
+ ObjectTypeDB::bind_method(_MD("get_event_voice_global_volume_scale"), &AudioServer::get_event_voice_global_volume_scale );
+
+ BIND_CONSTANT( SAMPLE_FORMAT_PCM8 );
+ BIND_CONSTANT( SAMPLE_FORMAT_PCM16 );
+ BIND_CONSTANT( SAMPLE_FORMAT_IMA_ADPCM );
+
+ BIND_CONSTANT( SAMPLE_LOOP_NONE );
+ BIND_CONSTANT( SAMPLE_LOOP_FORWARD );
+ BIND_CONSTANT( SAMPLE_LOOP_PING_PONG );
+
+ BIND_CONSTANT( FILTER_NONE );
+ BIND_CONSTANT( FILTER_LOWPASS );
+ BIND_CONSTANT( FILTER_BANDPASS );
+ BIND_CONSTANT( FILTER_HIPASS );
+ BIND_CONSTANT( FILTER_NOTCH );
+ BIND_CONSTANT( FILTER_BANDLIMIT ); ///< cutoff is LP resonace is HP
+
+ BIND_CONSTANT( REVERB_SMALL );
+ BIND_CONSTANT( REVERB_MEDIUM );
+ BIND_CONSTANT( REVERB_LARGE );
+ BIND_CONSTANT( REVERB_HALL );
+
+ GLOBAL_DEF("audio/stream_buffering_ms",500);
+
+}
+
+AudioServer::AudioServer() {
+
+ singleton=this;
+}
+
+AudioServer::~AudioServer() {
+
+
+}
diff --git a/servers/audio_server.h b/servers/audio_server.h
new file mode 100644
index 000000000..85289de58
--- /dev/null
+++ b/servers/audio_server.h
@@ -0,0 +1,289 @@
+/*************************************************************************/
+/* audio_server.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#ifndef AUDIO_SERVER_H
+#define AUDIO_SERVER_H
+
+#include "variant.h"
+#include "object.h"
+
+class AudioMixer {
+protected:
+
+ void audio_mixer_chunk_call(int p_frames);
+public:
+
+ enum {
+
+ INVALID_CHANNEL=0xFFFFFFFF
+ };
+
+ typedef uint32_t ChannelID;
+
+ /* CHANNEL API */
+
+ enum FilterType {
+ FILTER_NONE,
+ FILTER_LOWPASS,
+ FILTER_BANDPASS,
+ FILTER_HIPASS,
+ FILTER_NOTCH,
+ FILTER_PEAK,
+ FILTER_BANDLIMIT, ///< cutoff is LP resonace is HP
+ FILTER_LOW_SHELF,
+ FILTER_HIGH_SHELF
+
+ };
+
+ enum ReverbRoomType {
+
+ REVERB_SMALL,
+ REVERB_MEDIUM,
+ REVERB_LARGE,
+ REVERB_HALL
+ };
+
+ virtual ChannelID channel_alloc(RID p_sample)=0;
+
+ virtual void channel_set_volume(ChannelID p_channel, float p_gain)=0;
+ virtual void channel_set_pan(ChannelID p_channel, float p_pan, float p_depth=0,float height=0)=0; //pan and depth go from -1 to 1
+ virtual void channel_set_filter(ChannelID p_channel, FilterType p_type, float p_cutoff, float p_resonance, float p_gain=1.0)=0;
+ virtual void channel_set_chorus(ChannelID p_channel, float p_chorus )=0;
+ virtual void channel_set_reverb(ChannelID p_channel, ReverbRoomType p_room_type, float p_reverb)=0;
+ virtual void channel_set_mix_rate(ChannelID p_channel, int p_mix_rate)=0;
+ virtual void channel_set_positional(ChannelID p_channel, bool p_positional)=0;
+
+ virtual float channel_get_volume(ChannelID p_channel) const=0;
+ virtual float channel_get_pan(ChannelID p_channel) const=0; //pan and depth go from -1 to 1
+ virtual float channel_get_pan_depth(ChannelID p_channel) const=0; //pan and depth go from -1 to 1
+ virtual float channel_get_pan_height(ChannelID p_channel) const=0; //pan and depth go from -1 to 1
+ virtual FilterType channel_get_filter_type(ChannelID p_channel) const=0;
+ virtual float channel_get_filter_cutoff(ChannelID p_channel) const=0;
+ virtual float channel_get_filter_resonance(ChannelID p_channel) const=0;
+ virtual float channel_get_filter_gain(ChannelID p_channel) const=0;
+ virtual float channel_get_chorus(ChannelID p_channel) const=0;
+ virtual ReverbRoomType channel_get_reverb_type(ChannelID p_channel) const=0;
+ virtual float channel_get_reverb(ChannelID p_channel) const=0;
+
+ virtual int channel_get_mix_rate(ChannelID p_channel) const=0;
+ virtual bool channel_is_positional(ChannelID p_channel) const=0;
+ virtual bool channel_is_valid(ChannelID p_channel) const=0;
+
+
+ virtual void channel_free(ChannelID p_channel)=0;
+
+ virtual void set_mixer_volume(float p_volume)=0;
+
+
+ virtual ~AudioMixer() {}
+};
+
+
+class AudioServer : public Object {
+
+ OBJ_TYPE( AudioServer, Object );
+
+ static AudioServer *singleton;
+protected:
+friend class AudioStream;
+friend class EventStream;
+friend class AudioMixer;
+
+ virtual AudioMixer *get_mixer()=0;
+ virtual void audio_mixer_chunk_callback(int p_frames)=0;
+
+ static void _bind_methods();
+public:
+
+
+ class EventStream {
+ protected:
+ AudioMixer *get_mixer() const;
+ public:
+ virtual void update(uint64_t p_usec)=0;
+
+ virtual ~EventStream() {}
+ };
+
+ class AudioStream {
+ public:
+ virtual int get_channel_count() const=0;
+ virtual void set_mix_rate(int p_rate)=0; //notify the stream of the mix rate
+ virtual bool mix(int32_t *p_buffer,int p_frames)=0;
+ virtual void update()=0;
+ virtual bool can_update_mt() const { return true; }
+ virtual ~AudioStream() {}
+ };
+
+
+ enum SampleFormat {
+
+ SAMPLE_FORMAT_PCM8,
+ SAMPLE_FORMAT_PCM16,
+ SAMPLE_FORMAT_IMA_ADPCM
+ };
+
+ enum SampleLoopFormat {
+ SAMPLE_LOOP_NONE,
+ SAMPLE_LOOP_FORWARD,
+ SAMPLE_LOOP_PING_PONG // not supported in every platform
+
+ };
+
+ /* SAMPLE API */
+
+ virtual RID sample_create(SampleFormat p_format, bool p_stereo, int p_length)=0;
+
+ virtual void sample_set_description(RID p_sample, const String& p_description)=0;
+ virtual String sample_get_description(RID p_sample, const String& p_description) const=0;
+
+ virtual SampleFormat sample_get_format(RID p_sample) const=0;
+ virtual bool sample_is_stereo(RID p_sample) const=0;
+ virtual int sample_get_length(RID p_sample) const=0;
+ virtual const void* sample_get_data_ptr(RID p_sample) const=0;
+
+ virtual void sample_set_signed_data(RID p_sample, const DVector<float>& p_buffer);
+ virtual void sample_set_data(RID p_sample, const DVector<uint8_t>& p_buffer)=0;
+ virtual const DVector<uint8_t> sample_get_data(RID p_sample) const=0;
+
+ virtual void sample_set_mix_rate(RID p_sample,int p_rate)=0;
+ virtual int sample_get_mix_rate(RID p_sample) const=0;
+
+ virtual void sample_set_loop_format(RID p_sample,SampleLoopFormat p_format)=0;
+ virtual SampleLoopFormat sample_get_loop_format(RID p_sample) const=0;
+
+ virtual void sample_set_loop_begin(RID p_sample,int p_pos)=0;
+ virtual int sample_get_loop_begin(RID p_sample) const=0;
+
+ virtual void sample_set_loop_end(RID p_sample,int p_pos)=0;
+ virtual int sample_get_loop_end(RID p_sample) const=0;
+
+
+ /* VOICE API */
+
+ enum FilterType {
+ FILTER_NONE,
+ FILTER_LOWPASS,
+ FILTER_BANDPASS,
+ FILTER_HIPASS,
+ FILTER_NOTCH,
+ FILTER_PEAK,
+ FILTER_BANDLIMIT, ///< cutoff is LP resonace is HP
+ FILTER_LOW_SHELF,
+ FILTER_HIGH_SHELF
+ };
+
+ enum ReverbRoomType {
+
+ REVERB_SMALL,
+ REVERB_MEDIUM,
+ REVERB_LARGE,
+ REVERB_HALL
+ };
+
+ virtual RID voice_create()=0;
+
+ virtual void voice_play(RID p_voice, RID p_sample)=0;
+
+ virtual void voice_set_volume(RID p_voice, float p_gain)=0;
+ virtual void voice_set_pan(RID p_voice, float p_pan, float p_depth=0,float height=0)=0; //pan and depth go from -1 to 1
+ virtual void voice_set_filter(RID p_voice, FilterType p_type, float p_cutoff, float p_resonance, float p_gain=0)=0;
+ virtual void voice_set_chorus(RID p_voice, float p_chorus )=0;
+ virtual void voice_set_reverb(RID p_voice, ReverbRoomType p_room_type, float p_reverb)=0;
+ virtual void voice_set_mix_rate(RID p_voice, int p_mix_rate)=0;
+ virtual void voice_set_positional(RID p_voice, bool p_positional)=0;
+
+ virtual float voice_get_volume(RID p_voice) const=0;
+ virtual float voice_get_pan(RID p_voice) const=0; //pan and depth go from -1 to 1
+ virtual float voice_get_pan_depth(RID p_voice) const=0; //pan and depth go from -1 to 1
+ virtual float voice_get_pan_height(RID p_voice) const=0; //pan and depth go from -1 to 1
+ virtual FilterType voice_get_filter_type(RID p_voice) const=0;
+ virtual float voice_get_filter_cutoff(RID p_voice) const=0;
+ virtual float voice_get_filter_resonance(RID p_voice) const=0;
+ virtual float voice_get_chorus(RID p_voice) const=0;
+ virtual ReverbRoomType voice_get_reverb_type(RID p_voice) const=0;
+ virtual float voice_get_reverb(RID p_voice) const=0;
+
+ virtual int voice_get_mix_rate(RID p_voice) const=0;
+ virtual bool voice_is_positional(RID p_voice) const=0;
+
+ virtual void voice_stop(RID p_voice)=0;
+ virtual bool voice_is_active(RID p_voice) const=0;
+
+ /* STREAM API */
+
+ virtual RID audio_stream_create(AudioStream *p_stream)=0;
+ virtual RID event_stream_create(EventStream *p_stream)=0;
+
+ virtual void stream_set_active(RID p_stream, bool p_active)=0;
+ virtual bool stream_is_active(RID p_stream) const=0;
+
+ virtual void stream_set_volume_scale(RID p_stream, float p_scale)=0;
+ virtual float stream_set_volume_scale(RID p_stream) const=0;
+
+ /* Audio Physics API */
+
+ virtual void free(RID p_id)=0;
+
+ virtual void init()=0;
+ virtual void finish()=0;
+ virtual void update()=0;
+
+ /* MISC config */
+
+ virtual void lock()=0;
+ virtual void unlock()=0;
+ virtual int get_default_channel_count() const=0;
+ virtual int get_default_mix_rate() const=0;
+
+ virtual void set_stream_global_volume_scale(float p_volume)=0;
+ virtual void set_fx_global_volume_scale(float p_volume)=0;
+ virtual void set_event_voice_global_volume_scale(float p_volume)=0;
+
+ virtual float get_stream_global_volume_scale() const=0;
+ virtual float get_fx_global_volume_scale() const=0;
+ virtual float get_event_voice_global_volume_scale() const=0;
+
+ virtual uint32_t read_output_peak() const=0;
+
+ static AudioServer *get_singleton();
+
+ virtual double get_mix_time() const=0; //useful for video -> audio sync
+
+ AudioServer();
+ virtual ~AudioServer();
+};
+
+VARIANT_ENUM_CAST( AudioServer::SampleFormat );
+VARIANT_ENUM_CAST( AudioServer::SampleLoopFormat );
+VARIANT_ENUM_CAST( AudioServer::FilterType );
+VARIANT_ENUM_CAST( AudioServer::ReverbRoomType );
+
+typedef AudioServer AS;
+
+#endif // AUDIO_SERVER_H