diff options
| author | Juan Linietsky | 2014-02-09 22:10:30 -0300 |
|---|---|---|
| committer | Juan Linietsky | 2014-02-09 22:10:30 -0300 |
| commit | 0b806ee0fc9097fa7bda7ac0109191c9c5e0a1ac (patch) | |
| tree | 276c4d099e178eb67fbd14f61d77b05e3808e9e3 /servers/audio | |
| parent | 0e49da1687bc8192ed210947da52c9e5c5f301bb (diff) | |
| download | godot-0b806ee.tar.gz godot-0b806ee.tar.zst godot-0b806ee.zip | |
GODOT IS OPEN SOURCE
Diffstat (limited to '')
| -rw-r--r-- | servers/audio/SCsub | 7 | ||||
| -rw-r--r-- | servers/audio/audio_driver_dummy.cpp | 146 | ||||
| -rw-r--r-- | servers/audio/audio_driver_dummy.h | 76 | ||||
| -rw-r--r-- | servers/audio/audio_filter_sw.cpp | 286 | ||||
| -rw-r--r-- | servers/audio/audio_filter_sw.h | 119 | ||||
| -rw-r--r-- | servers/audio/audio_mixer_sw.cpp | 1085 | ||||
| -rw-r--r-- | servers/audio/audio_mixer_sw.h | 248 | ||||
| -rw-r--r-- | servers/audio/audio_server_sw.cpp | 1012 | ||||
| -rw-r--r-- | servers/audio/audio_server_sw.h | 279 | ||||
| -rw-r--r-- | servers/audio/reverb_buffers_sw.cpp | 33 | ||||
| -rw-r--r-- | servers/audio/reverb_buffers_sw.h | 38 | ||||
| -rw-r--r-- | servers/audio/reverb_sw.cpp | 569 | ||||
| -rw-r--r-- | servers/audio/reverb_sw.h | 84 | ||||
| -rw-r--r-- | servers/audio/sample_manager_sw.cpp | 280 | ||||
| -rw-r--r-- | servers/audio/sample_manager_sw.h | 129 | ||||
| -rw-r--r-- | servers/audio/voice_rb_sw.cpp | 34 | ||||
| -rw-r--r-- | servers/audio/voice_rb_sw.h | 146 | ||||
| -rw-r--r-- | servers/audio_server.cpp | 178 | ||||
| -rw-r--r-- | servers/audio_server.h | 289 |
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 |
