summaryrefslogtreecommitdiff
path: root/servers/audio/audio_mixer_sw.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'servers/audio/audio_mixer_sw.cpp')
-rw-r--r--servers/audio/audio_mixer_sw.cpp1085
1 files changed, 1085 insertions, 0 deletions
diff --git a/servers/audio/audio_mixer_sw.cpp b/servers/audio/audio_mixer_sw.cpp
new file mode 100644
index 0000000000..2ca0c5e93a
--- /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
+
+
+}