| 
									
										
										
										
											2014-02-09 22:10:30 -03:00
										 |  |  | /*************************************************************************/ | 
					
						
							|  |  |  | /*  audio_mixer_sw.cpp                                                   */ | 
					
						
							|  |  |  | /*************************************************************************/ | 
					
						
							|  |  |  | /*                       This file is part of:                           */ | 
					
						
							|  |  |  | /*                           GODOT ENGINE                                */ | 
					
						
							|  |  |  | /*                    http://www.godotengine.org                         */ | 
					
						
							|  |  |  | /*************************************************************************/ | 
					
						
							| 
									
										
										
										
											2016-01-01 11:50:53 -02:00
										 |  |  | /* Copyright (c) 2007-2016 Juan Linietsky, Ariel Manzur.                 */ | 
					
						
							| 
									
										
										
										
											2014-02-09 22:10:30 -03:00
										 |  |  | /*                                                                       */ | 
					
						
							|  |  |  | /* 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
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-05-14 01:22:15 -03:00
										 |  |  | template<class Depth,bool is_stereo,bool is_ima_adpcm,bool use_filter,bool use_fx,AudioMixerSW::InterpolationType type,AudioMixerSW::MixChannels mix_mode> | 
					
						
							| 
									
										
										
										
											2014-02-09 22:10:30 -03:00
										 |  |  | 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; | 
					
						
							| 
									
										
										
										
											2015-11-09 00:49:18 -03:00
										 |  |  | 		if (is_stereo && !is_ima_adpcm) | 
					
						
							| 
									
										
										
										
											2014-02-09 22:10:30 -03:00
										 |  |  | 			pos<<=1; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-05-14 01:22:15 -03:00
										 |  |  | 		if (is_ima_adpcm) { | 
					
						
							| 
									
										
										
										
											2014-02-09 22:10:30 -03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-11-09 00:49:18 -03:00
										 |  |  | 			int sample_pos = pos + p_state->ima_adpcm[0].window_ofs; | 
					
						
							| 
									
										
										
										
											2014-02-09 22:10:30 -03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-11-09 00:49:18 -03:00
										 |  |  | 			while(sample_pos>p_state->ima_adpcm[0].last_nibble) { | 
					
						
							| 
									
										
										
										
											2014-02-09 22:10:30 -03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-05-14 01:22:15 -03:00
										 |  |  | 				static const int16_t _ima_adpcm_step_table[89] = { | 
					
						
							|  |  |  | 					7, 8, 9, 10, 11, 12, 13, 14, 16, 17, | 
					
						
							|  |  |  | 					19, 21, 23, 25, 28, 31, 34, 37, 41, 45, | 
					
						
							|  |  |  | 					50, 55, 60, 66, 73, 80, 88, 97, 107, 118, | 
					
						
							|  |  |  | 					130, 143, 157, 173, 190, 209, 230, 253, 279, 307, | 
					
						
							|  |  |  | 					337, 371, 408, 449, 494, 544, 598, 658, 724, 796, | 
					
						
							|  |  |  | 					876, 963, 1060, 1166, 1282, 1411, 1552, 1707, 1878, 2066, | 
					
						
							|  |  |  | 					2272, 2499, 2749, 3024, 3327, 3660, 4026, 4428, 4871, 5358, | 
					
						
							|  |  |  | 					5894, 6484, 7132, 7845, 8630, 9493, 10442, 11487, 12635, 13899, | 
					
						
							|  |  |  | 					15289, 16818, 18500, 20350, 22385, 24623, 27086, 29794, 32767 | 
					
						
							|  |  |  | 				}; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				static const int8_t _ima_adpcm_index_table[16] = { | 
					
						
							|  |  |  | 					-1, -1, -1, -1, 2, 4, 6, 8, | 
					
						
							|  |  |  | 					-1, -1, -1, -1, 2, 4, 6, 8 | 
					
						
							|  |  |  | 				}; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-11-09 00:49:18 -03:00
										 |  |  | 				for(int i=0;i<(is_stereo?2:1);i++) { | 
					
						
							| 
									
										
										
										
											2014-05-14 01:22:15 -03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-11-09 00:49:18 -03:00
										 |  |  | 					int16_t nibble,signed_nibble,diff,step; | 
					
						
							| 
									
										
										
										
											2014-05-14 01:22:15 -03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-11-09 00:49:18 -03:00
										 |  |  | 					p_state->ima_adpcm[i].last_nibble++; | 
					
						
							|  |  |  | 					const uint8_t *src_ptr=p_state->ima_adpcm[i].ptr; | 
					
						
							| 
									
										
										
										
											2014-05-14 01:22:15 -03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-11-09 00:49:18 -03:00
										 |  |  | 					uint8_t nbb = src_ptr[ (p_state->ima_adpcm[i].last_nibble>>1) *  (is_stereo?2:1) + i ]; | 
					
						
							|  |  |  | 					nibble = (p_state->ima_adpcm[i].last_nibble&1)?(nbb>>4):(nbb&0xF); | 
					
						
							|  |  |  | 					step=_ima_adpcm_step_table[p_state->ima_adpcm[i].step_index]; | 
					
						
							| 
									
										
										
										
											2014-05-14 01:22:15 -03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-11-09 00:49:18 -03:00
										 |  |  | 					p_state->ima_adpcm[i].step_index += _ima_adpcm_index_table[nibble]; | 
					
						
							|  |  |  | 					if (p_state->ima_adpcm[i].step_index<0) | 
					
						
							|  |  |  | 						p_state->ima_adpcm[i].step_index=0; | 
					
						
							|  |  |  | 					if (p_state->ima_adpcm[i].step_index>88) | 
					
						
							|  |  |  | 						p_state->ima_adpcm[i].step_index=88; | 
					
						
							| 
									
										
										
										
											2014-05-14 01:22:15 -03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-11-09 00:49:18 -03:00
										 |  |  | 					/*
 | 
					
						
							|  |  |  | 					signed_nibble = (nibble&7) * ((nibble&8)?-1:1); | 
					
						
							|  |  |  | 					diff = (2 * signed_nibble + 1) * step / 4; */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 					diff = step >> 3 ; | 
					
						
							|  |  |  | 					if (nibble & 1) | 
					
						
							|  |  |  | 						diff += step >> 2 ; | 
					
						
							|  |  |  | 					if (nibble & 2) | 
					
						
							|  |  |  | 						diff += step >> 1 ; | 
					
						
							|  |  |  | 					if (nibble & 4) | 
					
						
							|  |  |  | 						diff += step ; | 
					
						
							|  |  |  | 					if (nibble & 8) | 
					
						
							|  |  |  | 						diff = -diff ; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 					p_state->ima_adpcm[i].predictor+=diff; | 
					
						
							|  |  |  | 					if (p_state->ima_adpcm[i].predictor<-0x8000) | 
					
						
							|  |  |  | 						p_state->ima_adpcm[i].predictor=-0x8000; | 
					
						
							|  |  |  | 					else if (p_state->ima_adpcm[i].predictor>0x7FFF) | 
					
						
							|  |  |  | 						p_state->ima_adpcm[i].predictor=0x7FFF; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 					/* store loop if there */ | 
					
						
							|  |  |  | 					if (p_state->ima_adpcm[i].last_nibble==p_state->ima_adpcm[i].loop_pos) { | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 						p_state->ima_adpcm[i].loop_step_index = p_state->ima_adpcm[i].step_index; | 
					
						
							|  |  |  | 						p_state->ima_adpcm[i].loop_predictor = p_state->ima_adpcm[i].predictor; | 
					
						
							|  |  |  | 					} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 					//printf("%i - %i - pred %i\n",int(p_state->ima_adpcm[i].last_nibble),int(nibble),int(p_state->ima_adpcm[i].predictor));
 | 
					
						
							| 
									
										
										
										
											2014-05-14 01:22:15 -03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-02-09 22:10:30 -03:00
										 |  |  | 			} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-11-09 00:49:18 -03:00
										 |  |  | 			final=p_state->ima_adpcm[0].predictor; | 
					
						
							|  |  |  | 			if (is_stereo) { | 
					
						
							|  |  |  | 				final_r=p_state->ima_adpcm[1].predictor; | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2014-05-14 01:22:15 -03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		} else { | 
					
						
							|  |  |  | 			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; | 
					
						
							| 
									
										
										
										
											2014-02-09 22:10:30 -03:00
										 |  |  | 				if (is_stereo) | 
					
						
							| 
									
										
										
										
											2014-05-14 01:22:15 -03:00
										 |  |  | 					final_r<<=8; | 
					
						
							| 
									
										
										
										
											2014-02-09 22:10:30 -03:00
										 |  |  | 			} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-05-14 01:22:15 -03:00
										 |  |  | 			if (type==INTERPOLATION_LINEAR) { | 
					
						
							| 
									
										
										
										
											2014-02-09 22:10:30 -03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-05-14 01:22:15 -03:00
										 |  |  | 				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); | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2014-02-09 22:10:30 -03:00
										 |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		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]; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-03-09 00:00:52 +01:00
										 |  |  | 		c.first_mix=false; | 
					
						
							| 
									
										
										
										
											2014-02-09 22:10:30 -03:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	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); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-09-17 20:03:10 -03:00
										 |  |  | 	bool use_fx=false; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (fx_enabled) { | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		for(int i=0;i<mix_channels;i++) { | 
					
						
							|  |  |  | 			if (c.mix.old_reverb_vol[i] || c.mix.reverb_vol[i] || c.mix.old_chorus_vol[i] || c.mix.chorus_vol[i] ) { | 
					
						
							|  |  |  | 				use_fx=true; | 
					
						
							|  |  |  | 				break; | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2014-02-09 22:10:30 -03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	/* 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; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-05-14 01:22:15 -03:00
										 |  |  | 	if (format==AS::SAMPLE_FORMAT_IMA_ADPCM) { | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-11-09 00:49:18 -03:00
										 |  |  | 		rstate.ima_adpcm=c.mix.ima_adpcm; | 
					
						
							| 
									
										
										
										
											2014-05-14 01:22:15 -03:00
										 |  |  | 		if (loop_format!=AS::SAMPLE_LOOP_NONE) { | 
					
						
							| 
									
										
										
										
											2015-11-09 00:49:18 -03:00
										 |  |  | 			c.mix.ima_adpcm[0].loop_pos=loop_begin_fp>>MIX_FRAC_BITS; | 
					
						
							|  |  |  | 			c.mix.ima_adpcm[1].loop_pos=loop_begin_fp>>MIX_FRAC_BITS; | 
					
						
							| 
									
										
										
										
											2014-05-14 01:22:15 -03:00
										 |  |  | 			loop_format=AS::SAMPLE_LOOP_FORWARD; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-02-09 22:10:30 -03:00
										 |  |  | 	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 */ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-05-14 01:22:15 -03:00
										 |  |  | 					if (format==AS::SAMPLE_FORMAT_IMA_ADPCM) { | 
					
						
							| 
									
										
										
										
											2015-11-09 00:49:18 -03:00
										 |  |  | 						for(int i=0;i<2;i++) { | 
					
						
							|  |  |  | 							c.mix.ima_adpcm[i].step_index=c.mix.ima_adpcm[i].loop_step_index; | 
					
						
							|  |  |  | 							c.mix.ima_adpcm[i].predictor=c.mix.ima_adpcm[i].loop_predictor; | 
					
						
							|  |  |  | 							c.mix.ima_adpcm[i].last_nibble=loop_begin_fp>>MIX_FRAC_BITS; | 
					
						
							|  |  |  | 						} | 
					
						
							| 
									
										
										
										
											2014-05-14 01:22:15 -03:00
										 |  |  | 						c.mix.offset=loop_begin_fp; | 
					
						
							|  |  |  | 					} else { | 
					
						
							|  |  |  | 						c.mix.offset=loop_begin_fp+(c.mix.offset-loop_end_fp); | 
					
						
							|  |  |  | 					} | 
					
						
							| 
									
										
										
										
											2014-02-09 22:10:30 -03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 			} 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*/ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-05-14 01:22:15 -03:00
										 |  |  | #define CALL_RESAMPLE_FUNC( m_depth, m_stereo, m_ima_adpcm, m_use_filter,  m_use_fx, m_interp, m_mode)\
 | 
					
						
							|  |  |  | 	do_resample<m_depth,m_stereo,m_ima_adpcm, m_use_filter,m_use_fx,m_interp, m_mode>(\ | 
					
						
							| 
									
										
										
										
											2014-02-09 22:10:30 -03:00
										 |  |  | 		src_ptr,\ | 
					
						
							|  |  |  | 		dst_buff,&rstate); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-05-14 01:22:15 -03:00
										 |  |  | #define CALL_RESAMPLE_INTERP( m_depth, m_stereo, m_ima_adpcm, m_use_filter,  m_use_fx, m_interp, m_mode)\
 | 
					
						
							| 
									
										
										
										
											2014-02-09 22:10:30 -03:00
										 |  |  | 	if(m_interp==INTERPOLATION_RAW) {\ | 
					
						
							| 
									
										
										
										
											2014-05-14 01:22:15 -03:00
										 |  |  | 		CALL_RESAMPLE_FUNC(m_depth,m_stereo, m_ima_adpcm,m_use_filter,m_use_fx,INTERPOLATION_RAW,m_mode);\ | 
					
						
							| 
									
										
										
										
											2014-02-09 22:10:30 -03:00
										 |  |  | 	} else if(m_interp==INTERPOLATION_LINEAR) {\ | 
					
						
							| 
									
										
										
										
											2014-05-14 01:22:15 -03:00
										 |  |  | 		CALL_RESAMPLE_FUNC(m_depth,m_stereo, m_ima_adpcm,m_use_filter,m_use_fx,INTERPOLATION_LINEAR,m_mode);\ | 
					
						
							| 
									
										
										
										
											2014-02-09 22:10:30 -03:00
										 |  |  | 	} else if(m_interp==INTERPOLATION_CUBIC) {\ | 
					
						
							| 
									
										
										
										
											2014-05-14 01:22:15 -03:00
										 |  |  | 		CALL_RESAMPLE_FUNC(m_depth,m_stereo, m_ima_adpcm,m_use_filter,m_use_fx,INTERPOLATION_CUBIC,m_mode);\ | 
					
						
							| 
									
										
										
										
											2014-02-09 22:10:30 -03:00
										 |  |  | 	}\ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-05-14 01:22:15 -03:00
										 |  |  | #define CALL_RESAMPLE_FX( m_depth, m_stereo, m_ima_adpcm, m_use_filter,  m_use_fx, m_interp, m_mode)\
 | 
					
						
							| 
									
										
										
										
											2014-02-09 22:10:30 -03:00
										 |  |  | 	if(m_use_fx) {\ | 
					
						
							| 
									
										
										
										
											2014-05-14 01:22:15 -03:00
										 |  |  | 		CALL_RESAMPLE_INTERP(m_depth,m_stereo, m_ima_adpcm,m_use_filter,true,m_interp, m_mode);\ | 
					
						
							| 
									
										
										
										
											2014-02-09 22:10:30 -03:00
										 |  |  | 	} else {\ | 
					
						
							| 
									
										
										
										
											2014-05-14 01:22:15 -03:00
										 |  |  | 		CALL_RESAMPLE_INTERP(m_depth,m_stereo, m_ima_adpcm,m_use_filter,false,m_interp, m_mode);\ | 
					
						
							| 
									
										
										
										
											2014-02-09 22:10:30 -03:00
										 |  |  | 	}\ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-05-14 01:22:15 -03:00
										 |  |  | #define CALL_RESAMPLE_FILTER( m_depth, m_stereo, m_ima_adpcm, m_use_filter,  m_use_fx, m_interp, m_mode)\
 | 
					
						
							| 
									
										
										
										
											2014-02-09 22:10:30 -03:00
										 |  |  | 	if(m_use_filter) {\ | 
					
						
							| 
									
										
										
										
											2014-05-14 01:22:15 -03:00
										 |  |  | 		CALL_RESAMPLE_FX(m_depth,m_stereo, m_ima_adpcm,true,m_use_fx,m_interp, m_mode);\ | 
					
						
							| 
									
										
										
										
											2014-02-09 22:10:30 -03:00
										 |  |  | 	} else {\ | 
					
						
							| 
									
										
										
										
											2014-05-14 01:22:15 -03:00
										 |  |  | 		CALL_RESAMPLE_FX(m_depth,m_stereo, m_ima_adpcm,false,m_use_fx,m_interp, m_mode);\ | 
					
						
							| 
									
										
										
										
											2014-02-09 22:10:30 -03:00
										 |  |  | 	}\ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-05-14 01:22:15 -03:00
										 |  |  | #define CALL_RESAMPLE_STEREO( m_depth, m_stereo, m_ima_adpcm, m_use_filter,  m_use_fx, m_interp, m_mode)\
 | 
					
						
							| 
									
										
										
										
											2014-02-09 22:10:30 -03:00
										 |  |  | 	if(m_stereo) {\ | 
					
						
							| 
									
										
										
										
											2014-05-14 01:22:15 -03:00
										 |  |  | 		CALL_RESAMPLE_FILTER(m_depth,true,m_ima_adpcm, m_use_filter,m_use_fx,m_interp, m_mode);\ | 
					
						
							| 
									
										
										
										
											2014-02-09 22:10:30 -03:00
										 |  |  | 	} else {\ | 
					
						
							| 
									
										
										
										
											2014-05-14 01:22:15 -03:00
										 |  |  | 		CALL_RESAMPLE_FILTER(m_depth,false,m_ima_adpcm,m_use_filter,m_use_fx,m_interp, m_mode);\ | 
					
						
							| 
									
										
										
										
											2014-02-09 22:10:30 -03:00
										 |  |  | 	}\ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-05-14 01:22:15 -03:00
										 |  |  | #define CALL_RESAMPLE_MODE( m_depth, m_stereo, m_ima_adpcm, m_use_filter,  m_use_fx, m_interp, m_mode)\
 | 
					
						
							| 
									
										
										
										
											2014-02-09 22:10:30 -03:00
										 |  |  | 	if(m_mode==MIX_STEREO) {\ | 
					
						
							| 
									
										
										
										
											2014-05-14 01:22:15 -03:00
										 |  |  | 		CALL_RESAMPLE_STEREO(m_depth,m_stereo, m_ima_adpcm,m_use_filter,m_use_fx,m_interp, MIX_STEREO);\ | 
					
						
							| 
									
										
										
										
											2014-02-09 22:10:30 -03:00
										 |  |  | 	} else {\ | 
					
						
							| 
									
										
										
										
											2014-05-14 01:22:15 -03:00
										 |  |  | 		CALL_RESAMPLE_STEREO(m_depth,m_stereo, m_ima_adpcm,m_use_filter,m_use_fx,m_interp, MIX_QUAD);\ | 
					
						
							| 
									
										
										
										
											2014-02-09 22:10:30 -03:00
										 |  |  | 	}\ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if (format==AS::SAMPLE_FORMAT_PCM8) { | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			int8_t *src_ptr =  &((int8_t*)data)[(c.mix.offset >> MIX_FRAC_BITS)<<(is_stereo?1:0) ]; | 
					
						
							| 
									
										
										
										
											2014-05-14 01:22:15 -03:00
										 |  |  | 			CALL_RESAMPLE_MODE(int8_t,is_stereo,false,use_filter,use_fx,interpolation_type,mix_channels); | 
					
						
							| 
									
										
										
										
											2014-02-09 22:10:30 -03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		} else if (format==AS::SAMPLE_FORMAT_PCM16) { | 
					
						
							|  |  |  | 			int16_t *src_ptr =  &((int16_t*)data)[(c.mix.offset >> MIX_FRAC_BITS)<<(is_stereo?1:0) ]; | 
					
						
							| 
									
										
										
										
											2014-05-14 01:22:15 -03:00
										 |  |  | 			CALL_RESAMPLE_MODE(int16_t,is_stereo,false,use_filter,use_fx,interpolation_type,mix_channels); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		} else if (format==AS::SAMPLE_FORMAT_IMA_ADPCM) { | 
					
						
							| 
									
										
										
										
											2015-11-09 00:49:18 -03:00
										 |  |  | 			for(int i=0;i<2;i++) { | 
					
						
							|  |  |  | 				c.mix.ima_adpcm[i].window_ofs=c.mix.offset>>MIX_FRAC_BITS; | 
					
						
							|  |  |  | 				c.mix.ima_adpcm[i].ptr=(const uint8_t*)data; | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			int8_t *src_ptr =  NULL; | 
					
						
							|  |  |  | 			CALL_RESAMPLE_MODE(int8_t,is_stereo,true,use_filter,use_fx,interpolation_type,mix_channels); | 
					
						
							| 
									
										
										
										
											2014-02-09 22:10:30 -03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		c.mix.offset+=rstate.pos; | 
					
						
							| 
									
										
										
										
											2014-09-17 20:03:10 -03:00
										 |  |  | 		dst_buff+=target*mix_channels; | 
					
						
							|  |  |  | 		rstate.reverb_buffer+=target*mix_channels; | 
					
						
							| 
									
										
										
										
											2014-02-09 22:10:30 -03:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	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; | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2016-03-09 00:00:52 +01:00
										 |  |  | 	if (!channels[idx].active) { | 
					
						
							| 
									
										
										
										
											2014-02-09 22:10:30 -03:00
										 |  |  | 		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++) { | 
					
						
							| 
									
										
										
										
											2015-10-08 14:50:18 -03:00
										 |  |  | 		c.mix.vol[i]=0; | 
					
						
							|  |  |  | 		c.mix.reverb_vol[i]=0; | 
					
						
							|  |  |  | 		c.mix.chorus_vol[i]=0; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-02-09 22:10:30 -03:00
										 |  |  | 		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; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-05-14 01:22:15 -03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	if (sample_manager->sample_get_format(c.sample)==AudioServer::SAMPLE_FORMAT_IMA_ADPCM) { | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-11-09 00:49:18 -03:00
										 |  |  | 		for(int i=0;i<2;i++) { | 
					
						
							|  |  |  | 			c.mix.ima_adpcm[i].step_index=0; | 
					
						
							|  |  |  | 			c.mix.ima_adpcm[i].predictor=0; | 
					
						
							|  |  |  | 			c.mix.ima_adpcm[i].loop_step_index=0; | 
					
						
							|  |  |  | 			c.mix.ima_adpcm[i].loop_predictor=0; | 
					
						
							|  |  |  | 			c.mix.ima_adpcm[i].last_nibble=-1; | 
					
						
							|  |  |  | 			c.mix.ima_adpcm[i].loop_pos=0x7FFFFFFF; | 
					
						
							|  |  |  | 			c.mix.ima_adpcm[i].window_ofs=0; | 
					
						
							|  |  |  | 			c.mix.ima_adpcm[i].ptr=NULL; | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2014-05-14 01:22:15 -03:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-02-09 22:10:30 -03:00
										 |  |  | 	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
 | 
					
						
							| 
									
										
										
										
											2016-03-09 00:00:52 +01:00
										 |  |  | 		c.filter.old_coefs=c.filter.coefs; | 
					
						
							| 
									
										
										
										
											2014-02-09 22:10:30 -03:00
										 |  |  | 		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
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | } |