/*************************************************************************/ /* 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 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;i0) c.mix.increment=((int64_t)c.speed<>mix_chunk_bits; rstate.vol[i]=c.mix.old_vol[i]<>mix_chunk_bits; rstate.reverb_vol[i]=c.mix.old_reverb_vol[i]<>mix_chunk_bits; rstate.chorus_vol[i]=c.mix.old_chorus_vol[i]<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(\ 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;iCMP_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;jCMP_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;iis_sample(p_sample), INVALID_CHANNEL ); int index=-1; for (int i=0;isample_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;iis_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<