diff options
author | Juan Linietsky <reduzio@gmail.com> | 2017-01-21 19:00:25 -0300 |
---|---|---|
committer | Juan Linietsky <reduzio@gmail.com> | 2017-01-21 19:01:00 -0300 |
commit | 0aa7242624fcd74eaf13db006274829c284fab3b (patch) | |
tree | 85ae8bc9d725f191da68f1b9ffe1e426025e8fb2 /servers/audio | |
parent | c4d6e54e93431e94888c5594386bcd0aa22528ee (diff) |
WIP new AudioServer, with buses, effects, etc.
Diffstat (limited to 'servers/audio')
-rw-r--r-- | servers/audio/SCsub | 2 | ||||
-rw-r--r-- | servers/audio/audio_effect.h | 2 | ||||
-rw-r--r-- | servers/audio/audio_filter_sw.h | 2 | ||||
-rw-r--r-- | servers/audio/audio_stream.cpp | 86 | ||||
-rw-r--r-- | servers/audio/audio_stream.h | 101 | ||||
-rw-r--r-- | servers/audio/effects/SCsub | 7 | ||||
-rw-r--r-- | servers/audio/effects/audio_effect_amplify.cpp | 50 | ||||
-rw-r--r-- | servers/audio/effects/audio_effect_amplify.h | 40 | ||||
-rw-r--r-- | servers/audio/effects/audio_effect_eq.cpp | 122 | ||||
-rw-r--r-- | servers/audio/effects/audio_effect_eq.h | 72 | ||||
-rw-r--r-- | servers/audio/effects/audio_effect_filter.cpp | 151 | ||||
-rw-r--r-- | servers/audio/effects/audio_effect_filter.h | 125 | ||||
-rw-r--r-- | servers/audio/effects/audio_effect_reverb.cpp | 182 | ||||
-rw-r--r-- | servers/audio/effects/audio_effect_reverb.h | 76 | ||||
-rw-r--r-- | servers/audio/effects/eq.cpp | 218 | ||||
-rw-r--r-- | servers/audio/effects/eq.h | 106 | ||||
-rw-r--r-- | servers/audio/effects/reverb.cpp | 363 | ||||
-rw-r--r-- | servers/audio/effects/reverb.h | 111 |
18 files changed, 1814 insertions, 2 deletions
diff --git a/servers/audio/SCsub b/servers/audio/SCsub index ccc76e823f..afaffcfe93 100644 --- a/servers/audio/SCsub +++ b/servers/audio/SCsub @@ -5,3 +5,5 @@ Import('env') env.add_source_files(env.servers_sources, "*.cpp") Export('env') + +SConscript("effects/SCsub") diff --git a/servers/audio/audio_effect.h b/servers/audio/audio_effect.h index 2fcd22251b..02eb258f99 100644 --- a/servers/audio/audio_effect.h +++ b/servers/audio/audio_effect.h @@ -10,7 +10,7 @@ class AudioEffectInstance : public Reference { public: - virtual void process(AudioFrame *p_frames,int p_frame_count)=0; + virtual void process(const AudioFrame *p_src_frames,AudioFrame *p_dst_frames,int p_frame_count)=0; }; diff --git a/servers/audio/audio_filter_sw.h b/servers/audio/audio_filter_sw.h index 0f3e2410fd..b711944ca8 100644 --- a/servers/audio/audio_filter_sw.h +++ b/servers/audio/audio_filter_sw.h @@ -65,7 +65,7 @@ public: void set_filter(AudioFilterSW * p_filter); void process(float *p_samples,int p_amount, int p_stride=1); void update_coeffs(); - inline void process_one(float& p_sample); + _ALWAYS_INLINE_ void process_one(float& p_sample); Processor(); }; diff --git a/servers/audio/audio_stream.cpp b/servers/audio/audio_stream.cpp new file mode 100644 index 0000000000..f4214838a1 --- /dev/null +++ b/servers/audio/audio_stream.cpp @@ -0,0 +1,86 @@ +/*************************************************************************/ +/* audio_stream.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* http://www.godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2017 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_stream.h" + +////////////////////////////// + + + +void AudioStreamPlaybackResampled::_begin_resample() { + + //clear cubic interpolation history + internal_buffer[0]=AudioFrame(0.0,0.0); + internal_buffer[1]=AudioFrame(0.0,0.0); + internal_buffer[2]=AudioFrame(0.0,0.0); + internal_buffer[3]=AudioFrame(0.0,0.0); + //mix buffer + _mix_internal(internal_buffer+4,INTERNAL_BUFFER_LEN); + mix_offset=0; +} + +void AudioStreamPlaybackResampled::mix(AudioFrame* p_buffer,float p_rate_scale,int p_frames) { + + float target_rate = AudioServer::get_singleton()->get_mix_rate() * p_rate_scale; + + uint64_t mix_increment = uint64_t((get_stream_sampling_rate() / double(target_rate)) * double( FP_LEN )); + + for(int i=0;i<p_frames;i++) { + + + + uint32_t idx = CUBIC_INTERP_HISTORY + uint32_t(mix_offset >> FP_BITS); + //standard cubic interpolation (great quality/performance ratio) + //this used to be moved to a LUT for greater performance, but nowadays CPU speed is generally faster than memory. + float mu = (mix_offset&FP_MASK)/float(FP_LEN); + AudioFrame y0 = internal_buffer[idx-3]; + AudioFrame y1 = internal_buffer[idx-2]; + AudioFrame y2 = internal_buffer[idx-1]; + AudioFrame y3 = internal_buffer[idx-0]; + + float mu2 = mu*mu; + AudioFrame a0 = y3 - y2 - y0 + y1; + AudioFrame a1 = y0 - y1 - a0; + AudioFrame a2 = y2 - y0; + AudioFrame a3 = y1; + + p_buffer[i] = (a0*mu*mu2 + a1*mu2 + a2*mu + a3); + + mix_offset+=mix_increment; + + while ( (mix_offset >> FP_BITS) >= INTERNAL_BUFFER_LEN ) { + + internal_buffer[0]=internal_buffer[INTERNAL_BUFFER_LEN+0]; + internal_buffer[1]=internal_buffer[INTERNAL_BUFFER_LEN+1]; + internal_buffer[2]=internal_buffer[INTERNAL_BUFFER_LEN+2]; + internal_buffer[3]=internal_buffer[INTERNAL_BUFFER_LEN+3]; + _mix_internal(internal_buffer+4,INTERNAL_BUFFER_LEN); + mix_offset-=(INTERNAL_BUFFER_LEN<<FP_BITS); + } + } +} diff --git a/servers/audio/audio_stream.h b/servers/audio/audio_stream.h new file mode 100644 index 0000000000..d08fedb084 --- /dev/null +++ b/servers/audio/audio_stream.h @@ -0,0 +1,101 @@ +/*************************************************************************/ +/* audio_stream.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* http://www.godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#ifndef AUDIO_STREAM_H +#define AUDIO_STREAM_H + +#include "resource.h" +#include "servers/audio_server.h" + +class AudioStreamPlayback : public Reference { + + GDCLASS( AudioStreamPlayback, Reference ) + +public: + + virtual void start(float p_from_pos=0.0)=0; + virtual void stop()=0; + virtual bool is_playing() const=0; + + virtual int get_loop_count() const=0; //times it looped + + virtual float get_pos() const=0; + virtual void seek_pos(float p_time)=0; + + virtual void mix(AudioFrame* p_bufer,float p_rate_scale,int p_frames)=0; + + virtual float get_length() const=0; //if supported, otherwise return 0 + + +}; + +class AudioStreamPlaybackResampled : public AudioStreamPlayback { + + GDCLASS( AudioStreamPlaybackResampled, AudioStreamPlayback ) + + + + enum { + FP_BITS=16, //fixed point used for resampling + FP_LEN=(1<<FP_BITS), + FP_MASK=FP_LEN-1, + INTERNAL_BUFFER_LEN=256, + CUBIC_INTERP_HISTORY=4 + }; + + AudioFrame internal_buffer[INTERNAL_BUFFER_LEN+CUBIC_INTERP_HISTORY]; + uint64_t mix_offset; + +protected: + void _begin_resample(); + virtual void _mix_internal(AudioFrame* p_bufer,int p_frames)=0; + virtual float get_stream_sampling_rate()=0; + +public: + + virtual void mix(AudioFrame* p_bufer,float p_rate_scale,int p_frames); + + AudioStreamPlaybackResampled() { mix_offset=0; } +}; + +class AudioStream : public Resource { + + GDCLASS( AudioStream, Resource ) + OBJ_SAVE_TYPE( AudioStream ) //children are all saved as AudioStream, so they can be exchanged + + +public: + + virtual Ref<AudioStreamPlayback> instance_playback()=0; + virtual String get_stream_name() const=0; + + +}; + + +#endif // AUDIO_STREAM_H diff --git a/servers/audio/effects/SCsub b/servers/audio/effects/SCsub new file mode 100644 index 0000000000..ccc76e823f --- /dev/null +++ b/servers/audio/effects/SCsub @@ -0,0 +1,7 @@ +#!/usr/bin/env python + +Import('env') + +env.add_source_files(env.servers_sources, "*.cpp") + +Export('env') diff --git a/servers/audio/effects/audio_effect_amplify.cpp b/servers/audio/effects/audio_effect_amplify.cpp new file mode 100644 index 0000000000..d723f8d2fe --- /dev/null +++ b/servers/audio/effects/audio_effect_amplify.cpp @@ -0,0 +1,50 @@ +#include "audio_effect_amplify.h" + + +void AudioEffectAmplifyInstance::process(const AudioFrame *p_src_frames,AudioFrame *p_dst_frames,int p_frame_count) { + + + //multiply volume interpolating to avoid clicks if this changes + float volume_db = base->volume_db; + float vol = Math::db2linear(mix_volume_db); + float vol_inc = (Math::db2linear(volume_db) - vol)/float(p_frame_count); + + for(int i=0;i<p_frame_count;i++) { + p_dst_frames[i]=p_src_frames[i]*vol; + vol+=vol_inc; + } + //set volume for next mix + mix_volume_db = volume_db; + +} + + +Ref<AudioEffectInstance> AudioEffectAmplify::instance() { + Ref<AudioEffectAmplifyInstance> ins; + ins.instance(); + ins->base=Ref<AudioEffectAmplify>(this); + ins->mix_volume_db=volume_db; + return ins; +} + +void AudioEffectAmplify::set_volume_db(float p_volume) { + volume_db=p_volume; +} + +float AudioEffectAmplify::get_volume_db() const { + + return volume_db; +} + +void AudioEffectAmplify::_bind_methods() { + + ClassDB::bind_method(_MD("set_volume_db","volume"),&AudioEffectAmplify::set_volume_db); + ClassDB::bind_method(_MD("get_volume_db"),&AudioEffectAmplify::get_volume_db); + + ADD_PROPERTY(PropertyInfo(Variant::REAL,"volume_db",PROPERTY_HINT_RANGE,"-80,24,0.01"),_SCS("set_volume_db"),_SCS("get_volume_db")); +} + +AudioEffectAmplify::AudioEffectAmplify() +{ + volume_db=0; +} diff --git a/servers/audio/effects/audio_effect_amplify.h b/servers/audio/effects/audio_effect_amplify.h new file mode 100644 index 0000000000..921054e2cd --- /dev/null +++ b/servers/audio/effects/audio_effect_amplify.h @@ -0,0 +1,40 @@ +#ifndef AUDIOEFFECTAMPLIFY_H +#define AUDIOEFFECTAMPLIFY_H + +#include "servers/audio/audio_effect.h" + +class AudioEffectAmplify; + +class AudioEffectAmplifyInstance : public AudioEffectInstance { + GDCLASS(AudioEffectAmplifyInstance,AudioEffectInstance) +friend class AudioEffectAmplify; + Ref<AudioEffectAmplify> base; + + float mix_volume_db; +public: + + virtual void process(const AudioFrame *p_src_frames,AudioFrame *p_dst_frames,int p_frame_count); + +}; + + +class AudioEffectAmplify : public AudioEffect { + GDCLASS(AudioEffectAmplify,AudioEffect) + +friend class AudioEffectAmplifyInstance; + float volume_db; + +protected: + + static void _bind_methods(); +public: + + + Ref<AudioEffectInstance> instance(); + void set_volume_db(float p_volume); + float get_volume_db() const; + + AudioEffectAmplify(); +}; + +#endif // AUDIOEFFECTAMPLIFY_H diff --git a/servers/audio/effects/audio_effect_eq.cpp b/servers/audio/effects/audio_effect_eq.cpp new file mode 100644 index 0000000000..3c6a684224 --- /dev/null +++ b/servers/audio/effects/audio_effect_eq.cpp @@ -0,0 +1,122 @@ +#include "audio_effect_eq.h" +#include "servers/audio_server.h" + + +void AudioEffectEQInstance::process(const AudioFrame *p_src_frames,AudioFrame *p_dst_frames,int p_frame_count) { + + int band_count = bands[0].size(); + EQ::BandProcess *proc_l = bands[0].ptr(); + EQ::BandProcess *proc_r = bands[1].ptr(); + float *bgain = gains.ptr(); + for(int i=0;i<band_count;i++) { + bgain[i]=Math::db2linear(base->gain[i]); + } + + + for(int i=0;i<p_frame_count;i++) { + + AudioFrame src = p_src_frames[i]; + AudioFrame dst = AudioFrame(0,0); + + for(int j=0;j<band_count;j++) { + + float l = src.l; + float r = src.r; + + proc_l[j].process_one(l); + proc_r[j].process_one(r); + + dst.l+=l * bgain[j]; + dst.r+=r * bgain[j]; + } + + p_dst_frames[i]=dst; + } + +} + + +Ref<AudioEffectInstance> AudioEffectEQ::instance() { + Ref<AudioEffectEQInstance> ins; + ins.instance(); + ins->base=Ref<AudioEffectEQ>(this); + ins->gains.resize(eq.get_band_count()); + for(int i=0;i<2;i++) { + ins->bands[i].resize(eq.get_band_count()); + for(int j=0;j<ins->bands[i].size();j++) { + ins->bands[i][j]=eq.get_band_processor(j); + } + } + + return ins; +} + +void AudioEffectEQ::set_band_gain_db(int p_band,float p_volume) { + ERR_FAIL_INDEX(p_band,gain.size()); + gain[p_band]=p_volume; +} + +float AudioEffectEQ::get_band_gain_db(int p_band) const { + ERR_FAIL_INDEX_V(p_band,gain.size(),0); + + return gain[p_band]; +} +int AudioEffectEQ::get_band_count() const { + return gain.size(); +} + +bool AudioEffectEQ::_set(const StringName& p_name, const Variant& p_value) { + + const Map<StringName,int>::Element *E=prop_band_map.find(p_name); + if (E) { + set_band_gain_db(E->get(),p_value); + return true; + } + + return false; +} + +bool AudioEffectEQ::_get(const StringName& p_name,Variant &r_ret) const{ + + const Map<StringName,int>::Element *E=prop_band_map.find(p_name); + if (E) { + r_ret=get_band_gain_db(E->get()); + return true; + } + + return false; + +} + +void AudioEffectEQ::_get_property_list( List<PropertyInfo> *p_list) const{ + + for(int i=0;i<band_names.size();i++) { + + p_list->push_back(PropertyInfo(Variant::REAL,band_names[i],PROPERTY_HINT_RANGE,"-60,24,0.1")); + } +} + + + +void AudioEffectEQ::_bind_methods() { + + ClassDB::bind_method(_MD("set_band_gain_db","band_idx","volume_db"),&AudioEffectEQ::set_band_gain_db); + ClassDB::bind_method(_MD("get_band_gain_db","band_idx"),&AudioEffectEQ::get_band_gain_db); + ClassDB::bind_method(_MD("get_band_count"),&AudioEffectEQ::get_band_count); + +} + +AudioEffectEQ::AudioEffectEQ(EQ::Preset p_preset) +{ + + + eq.set_mix_rate(AudioServer::get_singleton()->get_mix_rate()); + eq.set_preset_band_mode(p_preset); + gain.resize(eq.get_band_count()); + for(int i=0;i<gain.size();i++) { + gain[i]=0.0; + String name = "band_db/"+itos(eq.get_band_frequency(i))+"_hz"; + prop_band_map[name]=i; + band_names.push_back(name); + } +} diff --git a/servers/audio/effects/audio_effect_eq.h b/servers/audio/effects/audio_effect_eq.h new file mode 100644 index 0000000000..3fcc2c0056 --- /dev/null +++ b/servers/audio/effects/audio_effect_eq.h @@ -0,0 +1,72 @@ +#ifndef AUDIOEFFECTEQ_H +#define AUDIOEFFECTEQ_H + + +#include "servers/audio/audio_effect.h" +#include "servers/audio/effects/eq.h" + +class AudioEffectEQ; + +class AudioEffectEQInstance : public AudioEffectInstance { + GDCLASS(AudioEffectEQInstance,AudioEffectInstance) +friend class AudioEffectEQ; + Ref<AudioEffectEQ> base; + + Vector<EQ::BandProcess> bands[2]; + Vector<float> gains; +public: + + virtual void process(const AudioFrame *p_src_frames,AudioFrame *p_dst_frames,int p_frame_count); + +}; + + +class AudioEffectEQ : public AudioEffect { + GDCLASS(AudioEffectEQ,AudioEffect) + +friend class AudioEffectEQInstance; + + EQ eq; + Vector<float> gain; + Map<StringName,int> prop_band_map; + Vector<String> band_names; + +protected: + bool _set(const StringName& p_name, const Variant& p_value); + bool _get(const StringName& p_name,Variant &r_ret) const; + void _get_property_list( List<PropertyInfo> *p_list) const; + + + + static void _bind_methods(); +public: + + + Ref<AudioEffectInstance> instance(); + void set_band_gain_db(int p_band,float p_volume); + float get_band_gain_db(int p_band) const; + int get_band_count() const; + + AudioEffectEQ(EQ::Preset p_preset=EQ::PRESET_6_BANDS); +}; + + +class AudioEffectEQ6 : public AudioEffectEQ { + GDCLASS(AudioEffectEQ6,AudioEffectEQ) +public: + AudioEffectEQ6() : AudioEffectEQ(EQ::PRESET_6_BANDS) {} +}; + +class AudioEffectEQ10 : public AudioEffectEQ { + GDCLASS(AudioEffectEQ10,AudioEffectEQ) +public: + AudioEffectEQ10() : AudioEffectEQ(EQ::PRESET_10_BANDS) {} +}; + +class AudioEffectEQ21 : public AudioEffectEQ { + GDCLASS(AudioEffectEQ21,AudioEffectEQ) +public: + AudioEffectEQ21() : AudioEffectEQ(EQ::PRESET_21_BANDS) {} +}; + +#endif // AUDIOEFFECTEQ_H diff --git a/servers/audio/effects/audio_effect_filter.cpp b/servers/audio/effects/audio_effect_filter.cpp new file mode 100644 index 0000000000..4e54ea1f3e --- /dev/null +++ b/servers/audio/effects/audio_effect_filter.cpp @@ -0,0 +1,151 @@ +#include "audio_effect_filter.h" +#include "servers/audio_server.h" + +template<int S> +void AudioEffectFilterInstance::_process_filter(const AudioFrame *p_src_frames,AudioFrame *p_dst_frames,int p_frame_count) { + + for(int i=0;i<p_frame_count;i++) { + float f = p_src_frames[i].l; + filter_process[0][0].process_one(f); + if (S>1) + filter_process[0][1].process_one(f); + if (S>2) + filter_process[0][2].process_one(f); + if (S>3) + filter_process[0][3].process_one(f); + + p_dst_frames[i].l=f; + } + + for(int i=0;i<p_frame_count;i++) { + float f = p_src_frames[i].r; + filter_process[1][0].process_one(f); + if (S>1) + filter_process[1][1].process_one(f); + if (S>2) + filter_process[1][2].process_one(f); + if (S>3) + filter_process[1][3].process_one(f); + + p_dst_frames[i].r=f; + } + +} + +void AudioEffectFilterInstance::process(const AudioFrame *p_src_frames,AudioFrame *p_dst_frames,int p_frame_count) { + + filter.set_cutoff(base->cutoff); + filter.set_gain(base->gain); + filter.set_resonance(base->resonance); + filter.set_mode(base->mode); + int stages = int(base->db)+1; + filter.set_stages(stages); + filter.set_sampling_rate(AudioServer::get_singleton()->get_mix_rate()); + + for(int i=0;i<2;i++) { + for(int j=0;j<4;j++) { + filter_process[i][j].update_coeffs(); + } + } + + + if (stages==1) { + _process_filter<1>(p_src_frames,p_dst_frames,p_frame_count); + } else if (stages==2) { + _process_filter<2>(p_src_frames,p_dst_frames,p_frame_count); + } else if (stages==3) { + _process_filter<3>(p_src_frames,p_dst_frames,p_frame_count); + } else if (stages==4) { + _process_filter<4>(p_src_frames,p_dst_frames,p_frame_count); + } + +} + + +AudioEffectFilterInstance::AudioEffectFilterInstance() { + + for(int i=0;i<2;i++) { + for(int j=0;j<4;j++) { + filter_process[i][j].set_filter(&filter); + } + } + +} + + +Ref<AudioEffectInstance> AudioEffectFilter::instance() { + Ref<AudioEffectFilterInstance> ins; + ins.instance(); + ins->base=Ref<AudioEffectFilter>(this); + + return ins; +} + +void AudioEffectFilter::set_cutoff(float p_freq) { + + cutoff=p_freq; +} + +float AudioEffectFilter::get_cutoff() const{ + + return cutoff; +} + +void AudioEffectFilter::set_resonance(float p_amount){ + + resonance=p_amount; +} +float AudioEffectFilter::get_resonance() const{ + + return resonance; +} + +void AudioEffectFilter::set_gain(float p_amount){ + + gain=p_amount; +} +float AudioEffectFilter::get_gain() const { + + return gain; +} + + + +void AudioEffectFilter::set_db(FilterDB p_db) { + db=p_db; +} + +AudioEffectFilter::FilterDB AudioEffectFilter::get_db() const { + + return db; +} + +void AudioEffectFilter::_bind_methods() { + + ClassDB::bind_method(_MD("set_cutoff","freq"),&AudioEffectFilter::set_cutoff); + ClassDB::bind_method(_MD("get_cutoff"),&AudioEffectFilter::get_cutoff); + + ClassDB::bind_method(_MD("set_resonance","amount"),&AudioEffectFilter::set_resonance); + ClassDB::bind_method(_MD("get_resonance"),&AudioEffectFilter::get_resonance); + + ClassDB::bind_method(_MD("set_gain","amount"),&AudioEffectFilter::set_gain); + ClassDB::bind_method(_MD("get_gain"),&AudioEffectFilter::get_gain); + + ClassDB::bind_method(_MD("set_db","amount"),&AudioEffectFilter::set_db); + ClassDB::bind_method(_MD("get_db"),&AudioEffectFilter::get_db); + + ADD_PROPERTY(PropertyInfo(Variant::REAL,"cutoff_hz",PROPERTY_HINT_RANGE,"1,40000,0.1"),_SCS("set_cutoff"),_SCS("get_cutoff")); + ADD_PROPERTY(PropertyInfo(Variant::REAL,"resonance",PROPERTY_HINT_RANGE,"0,1,0.01"),_SCS("set_resonance"),_SCS("get_resonance")); + ADD_PROPERTY(PropertyInfo(Variant::REAL,"gain",PROPERTY_HINT_RANGE,"0,4,0.01"),_SCS("set_gain"),_SCS("get_gain")); + ADD_PROPERTY(PropertyInfo(Variant::INT,"dB",PROPERTY_HINT_ENUM,"6db,12db,18db,24db"),_SCS("set_db"),_SCS("get_db")); +} + +AudioEffectFilter::AudioEffectFilter(AudioFilterSW::Mode p_mode) +{ + + mode=p_mode; + cutoff=2000; + resonance=0.5; + gain=1.0; + db=FILTER_6DB; +} diff --git a/servers/audio/effects/audio_effect_filter.h b/servers/audio/effects/audio_effect_filter.h new file mode 100644 index 0000000000..7b5f1f1a1c --- /dev/null +++ b/servers/audio/effects/audio_effect_filter.h @@ -0,0 +1,125 @@ +#ifndef AUDIOEFFECTFILTER_H +#define AUDIOEFFECTFILTER_H + +#include "servers/audio/audio_effect.h" +#include "servers/audio/audio_filter_sw.h" + +class AudioEffectFilter; + +class AudioEffectFilterInstance : public AudioEffectInstance { + GDCLASS(AudioEffectFilterInstance,AudioEffectInstance) +friend class AudioEffectFilter; + + Ref<AudioEffectFilter> base; + + AudioFilterSW filter; + AudioFilterSW::Processor filter_process[2][4]; + + template<int S> + void _process_filter(const AudioFrame *p_src_frames,AudioFrame *p_dst_frames,int p_frame_count); +public: + + virtual void process(const AudioFrame *p_src_frames,AudioFrame *p_dst_frames,int p_frame_count); + + AudioEffectFilterInstance(); +}; + + +class AudioEffectFilter : public AudioEffect { + GDCLASS(AudioEffectFilter,AudioEffect) +public: + + enum FilterDB { + FILTER_6DB, + FILTER_12DB, + FILTER_18DB, + FILTER_24DB, + }; + friend class AudioEffectFilterInstance; + + AudioFilterSW::Mode mode; + float cutoff; + float resonance; + float gain; + FilterDB db; + + +protected: + + + static void _bind_methods(); +public: + + void set_cutoff(float p_freq); + float get_cutoff() const; + + void set_resonance(float p_amount); + float get_resonance() const; + + void set_gain(float p_amount); + float get_gain() const; + + void set_db(FilterDB p_db); + FilterDB get_db() const; + + Ref<AudioEffectInstance> instance(); + + AudioEffectFilter(AudioFilterSW::Mode p_mode=AudioFilterSW::LOWPASS); +}; + +VARIANT_ENUM_CAST(AudioEffectFilter::FilterDB) + +class AudioEffectLowPass : public AudioEffectFilter { + GDCLASS(AudioEffectLowPass,AudioEffectFilter) +public: + + AudioEffectLowPass() : AudioEffectFilter(AudioFilterSW::LOWPASS) {} +}; + +class AudioEffectHighPass : public AudioEffectFilter { + GDCLASS(AudioEffectHighPass,AudioEffectFilter) +public: + + AudioEffectHighPass() : AudioEffectFilter(AudioFilterSW::HIGHPASS) {} +}; + +class AudioEffectBandPass : public AudioEffectFilter { + GDCLASS(AudioEffectBandPass,AudioEffectFilter) +public: + + AudioEffectBandPass() : AudioEffectFilter(AudioFilterSW::BANDPASS) {} +}; + +class AudioEffectNotchPass : public AudioEffectFilter { + GDCLASS(AudioEffectNotchPass,AudioEffectFilter) +public: + + AudioEffectNotchPass() : AudioEffectFilter(AudioFilterSW::NOTCH) {} +}; + +class AudioEffectBandLimit : public AudioEffectFilter { + GDCLASS(AudioEffectBandLimit,AudioEffectFilter) +public: + + AudioEffectBandLimit() : AudioEffectFilter(AudioFilterSW::BANDLIMIT) {} +}; + + +class AudioEffectLowShelf : public AudioEffectFilter { + GDCLASS(AudioEffectLowShelf,AudioEffectFilter) +public: + + AudioEffectLowShelf() : AudioEffectFilter(AudioFilterSW::LOWSHELF) {} +}; + + +class AudioEffectHighShelf : public AudioEffectFilter { + GDCLASS(AudioEffectHighShelf,AudioEffectFilter) +public: + + AudioEffectHighShelf() : AudioEffectFilter(AudioFilterSW::HIGHSHELF) {} +}; + + + +#endif // AUDIOEFFECTFILTER_H diff --git a/servers/audio/effects/audio_effect_reverb.cpp b/servers/audio/effects/audio_effect_reverb.cpp new file mode 100644 index 0000000000..749814fd76 --- /dev/null +++ b/servers/audio/effects/audio_effect_reverb.cpp @@ -0,0 +1,182 @@ +#include "audio_effect_reverb.h" +#include "servers/audio_server.h" +void AudioEffectReverbInstance::process(const AudioFrame *p_src_frames,AudioFrame *p_dst_frames,int p_frame_count) { + + for(int i=0;i<2;i++) { + Reverb &r=reverb[i]; + + r.set_predelay( base->predelay); + r.set_predelay_feedback( base->predelay_fb ); + r.set_highpass( base->hpf ); + r.set_room_size( base->room_size ); + r.set_damp( base->damping ); + r.set_extra_spread( base->spread ); + r.set_wet( base->wet ); + r.set_dry( base->dry ); + } + + int todo = p_frame_count; + int offset=0; + + while(todo) { + + int to_mix = MIN(todo,Reverb::INPUT_BUFFER_MAX_SIZE); + + for(int j=0;j<to_mix;j++) { + tmp_src[j]=p_src_frames[offset+j].l; + } + + reverb[0].process(tmp_src,tmp_dst,to_mix); + + for(int j=0;j<to_mix;j++) { + p_dst_frames[offset+j].l=tmp_dst[j]; + tmp_src[j]=p_src_frames[offset+j].r; + } + + reverb[1].process(tmp_src,tmp_dst,to_mix); + + for(int j=0;j<to_mix;j++) { + p_dst_frames[offset+j].r=tmp_dst[j]; + } + + offset+=to_mix; + todo-=to_mix; + } +} + +AudioEffectReverbInstance::AudioEffectReverbInstance() { + + reverb[0].set_mix_rate( AudioServer::get_singleton()->get_mix_rate() ); + reverb[0].set_extra_spread_base(0); + reverb[1].set_mix_rate( AudioServer::get_singleton()->get_mix_rate() ); + reverb[1].set_extra_spread_base(0.000521); //for stereo effect + +} + +Ref<AudioEffectInstance> AudioEffectReverb::instance() { + Ref<AudioEffectReverbInstance> ins; + ins.instance(); + ins->base=Ref<AudioEffectReverb>(this); + return ins; +} + +void AudioEffectReverb::set_predelay_msec(float p_msec) { + + predelay=p_msec; +} + +void AudioEffectReverb::set_predelay_feedback(float p_feedback){ + + predelay_fb=p_feedback; +} +void AudioEffectReverb::set_room_size(float p_size){ + + room_size=p_size; +} +void AudioEffectReverb::set_damping(float p_damping){ + + damping=p_damping; +} +void AudioEffectReverb::set_spread(float p_spread){ + + spread=p_spread; +} + +void AudioEffectReverb::set_dry(float p_dry){ + + dry=p_dry; +} +void AudioEffectReverb::set_wet(float p_wet){ + + wet=p_wet; +} +void AudioEffectReverb::set_hpf(float p_hpf) { + + hpf=p_hpf; +} + +float AudioEffectReverb::get_predelay_msec() const { + + return predelay; +} +float AudioEffectReverb::get_predelay_feedback() const { + + return predelay_fb; +} +float AudioEffectReverb::get_room_size() const { + + return room_size; +} +float AudioEffectReverb::get_damping() const { + + return damping; +} +float AudioEffectReverb::get_spread() const { + + return spread; +} +float AudioEffectReverb::get_dry() const { + + return dry; +} +float AudioEffectReverb::get_wet() const { + + return wet; +} +float AudioEffectReverb::get_hpf() const { + + return hpf; +} + + +void AudioEffectReverb::_bind_methods() { + + + ClassDB::bind_method(_MD("set_predelay_msec","msec"),&AudioEffectReverb::set_predelay_msec); + ClassDB::bind_method(_MD("get_predelay_msec"),&AudioEffectReverb::get_predelay_msec); + + ClassDB::bind_method(_MD("set_predelay_feedback","feedback"),&AudioEffectReverb::set_predelay_feedback); + ClassDB::bind_method(_MD("get_predelay_feedback"),&AudioEffectReverb::get_predelay_feedback); + + ClassDB::bind_method(_MD("set_room_size","size"),&AudioEffectReverb::set_room_size); + ClassDB::bind_method(_MD("get_room_size"),&AudioEffectReverb::get_room_size); + + ClassDB::bind_method(_MD("set_damping","amount"),&AudioEffectReverb::set_damping); + ClassDB::bind_method(_MD("get_damping"),&AudioEffectReverb::get_damping); + + ClassDB::bind_method(_MD("set_spread","amount"),&AudioEffectReverb::set_spread); + ClassDB::bind_method(_MD("get_spread"),&AudioEffectReverb::get_spread); + + ClassDB::bind_method(_MD("set_dry","amount"),&AudioEffectReverb::set_dry); + ClassDB::bind_method(_MD("get_dry"),&AudioEffectReverb::get_dry); + + ClassDB::bind_method(_MD("set_wet","amount"),&AudioEffectReverb::set_wet); + ClassDB::bind_method(_MD("get_wet"),&AudioEffectReverb::get_wet); + + ClassDB::bind_method(_MD("set_hpf","amount"),&AudioEffectReverb::set_hpf); + ClassDB::bind_method(_MD("get_hpf"),&AudioEffectReverb::get_hpf); + + + ADD_GROUP("Predelay","predelay_"); + ADD_PROPERTY(PropertyInfo(Variant::REAL,"predelay_msec",PROPERTY_HINT_RANGE,"20,500,1"),_SCS("set_predelay_msec"),_SCS("get_predelay_msec")); + ADD_PROPERTY(PropertyInfo(Variant::REAL,"predelay_feedback",PROPERTY_HINT_RANGE,"0,1,0.01"),_SCS("set_predelay_msec"),_SCS("get_predelay_msec")); + ADD_GROUP("",""); + ADD_PROPERTY(PropertyInfo(Variant::REAL,"room_size",PROPERTY_HINT_RANGE,"0,1,0.01"),_SCS("set_room_size"),_SCS("get_room_size")); + ADD_PROPERTY(PropertyInfo(Variant::REAL,"damping",PROPERTY_HINT_RANGE,"0,1,0.01"),_SCS("set_damping"),_SCS("get_damping")); + ADD_PROPERTY(PropertyInfo(Variant::REAL,"spread",PROPERTY_HINT_RANGE,"0,1,0.01"),_SCS("set_spread"),_SCS("get_spread")); + ADD_PROPERTY(PropertyInfo(Variant::REAL,"hipass",PROPERTY_HINT_RANGE,"0,1,0.01"),_SCS("set_hpf"),_SCS("get_hpf")); + ADD_PROPERTY(PropertyInfo(Variant::REAL,"dry",PROPERTY_HINT_RANGE,"0,1,0.01"),_SCS("set_dry"),_SCS("get_dry")); + ADD_PROPERTY(PropertyInfo(Variant::REAL,"wet",PROPERTY_HINT_RANGE,"0,1,0.01"),_SCS("set_wet"),_SCS("get_wet")); +} + +AudioEffectReverb::AudioEffectReverb() { + predelay=150; + predelay_fb=0.4; + hpf=0; + room_size=0.8; + damping=0.5; + spread=1.0; + dry=1.0; + wet=0.5; + +} diff --git a/servers/audio/effects/audio_effect_reverb.h b/servers/audio/effects/audio_effect_reverb.h new file mode 100644 index 0000000000..e05ffe422f --- /dev/null +++ b/servers/audio/effects/audio_effect_reverb.h @@ -0,0 +1,76 @@ +#ifndef AUDIOEFFECTREVERB_H +#define AUDIOEFFECTREVERB_H + + +#include "servers/audio/audio_effect.h" +#include "servers/audio/effects/reverb.h" + +class AudioEffectReverb; + +class AudioEffectReverbInstance : public AudioEffectInstance { + GDCLASS(AudioEffectReverbInstance,AudioEffectInstance) + + Ref<AudioEffectReverb> base; + + float tmp_src[Reverb::INPUT_BUFFER_MAX_SIZE]; + float tmp_dst[Reverb::INPUT_BUFFER_MAX_SIZE]; + +friend class AudioEffectReverb; + + Reverb reverb[2]; + + +public: + + virtual void process(const AudioFrame *p_src_frames,AudioFrame *p_dst_frames,int p_frame_count); + AudioEffectReverbInstance(); +}; + + +class AudioEffectReverb : public AudioEffect { + GDCLASS(AudioEffectReverb,AudioEffect) + +friend class AudioEffectReverbInstance; + + float predelay; + float predelay_fb; + float hpf; + float room_size; + float damping; + float spread; + float dry; + float wet; + +protected: + + static void _bind_methods(); +public: + + + void set_predelay_msec(float p_msec); + void set_predelay_feedback(float p_feedback); + void set_room_size(float p_size); + void set_damping(float p_damping); + void set_spread(float p_spread); + void set_dry(float p_dry); + void set_wet(float p_wet); + void set_hpf(float p_hpf); + + float get_predelay_msec() const; + float get_predelay_feedback() const; + float get_room_size() const; + float get_damping() const; + float get_spread() const; + float get_dry() const; + float get_wet() const; + float get_hpf() const; + + Ref<AudioEffectInstance> instance(); + void set_volume_db(float p_volume); + float get_volume_db() const; + + AudioEffectReverb(); +}; + + +#endif // AUDIOEFFECTREVERB_H diff --git a/servers/audio/effects/eq.cpp b/servers/audio/effects/eq.cpp new file mode 100644 index 0000000000..14659585b8 --- /dev/null +++ b/servers/audio/effects/eq.cpp @@ -0,0 +1,218 @@ +// +// C++ Interface: eq +// +// Description: +// +// +// Author: reduzio@gmail.com (C) 2006 +// +// Copyright: See COPYING file that comes with this distribution +// +// +#include "eq.h" +#include <math.h> +#include "error_macros.h" + +#define POW2(v) ((v)*(v)) + +/* Helper */ + static int solve_quadratic(double a,double b,double c,double *r1, double *r2) { +//solves quadractic and returns number of roots + + double base=2*a; + if (base == 0.0f) + return 0; + + double squared=b*b-4*a*c; + if (squared<0.0) + return 0; + + squared=sqrt(squared); + + *r1=(-b+squared)/base; + *r2=(-b-squared)/base; + + if (*r1==*r2) + return 1; + else + return 2; + } + +EQ::BandProcess::BandProcess() { + + c1=c2=c3=history.a1=history.a2=history.a3=0; + history.b1=history.b2=history.b3=0; +} + +void EQ::recalculate_band_coefficients() { + +#define BAND_LOG( m_f ) ( log((m_f)) / log(2) ) + + for (int i=0;i<band.size();i++) { + + double octave_size; + + double frq=band[i].freq; + + if (i==0) { + + octave_size=BAND_LOG(band[1].freq)-BAND_LOG(frq); + } else if (i==(band.size()-1)) { + + octave_size=BAND_LOG(frq)-BAND_LOG(band[i-1].freq); + } else { + + double next=BAND_LOG(band[i+1].freq)-BAND_LOG(frq); + double prev=BAND_LOG(frq)-BAND_LOG(band[i-1].freq); + octave_size=(next+prev)/2.0; + } + + + + double frq_l=round(frq/pow(2.0,octave_size/2.0)); + + + + double side_gain2=POW2(1.0/M_SQRT2); + double th=2.0*M_PI*frq/mix_rate; + double th_l=2.0*M_PI*frq_l/mix_rate; + + double c2a=side_gain2 * POW2(cos(th)) + - 2.0 * side_gain2 * cos(th_l) * cos(th) + + side_gain2 + - POW2(sin(th_l)); + + double c2b=2.0 * side_gain2 * POW2(cos(th_l)) + + side_gain2 * POW2(cos(th)) + - 2.0 * side_gain2 * cos(th_l) * cos(th) + - side_gain2 + + POW2(sin(th_l)); + + double c2c=0.25 * side_gain2 * POW2(cos(th)) + - 0.5 * side_gain2 * cos(th_l) * cos(th) + + 0.25 * side_gain2 + - 0.25 * POW2(sin(th_l)); + + //printf("band %i, precoefs = %f,%f,%f\n",i,c2a,c2b,c2c); + + double r1,r2; //roots + int roots=solve_quadratic(c2a,c2b,c2c,&r1,&r2); + + ERR_CONTINUE( roots==0 ); + + band[i].c1=2.0 * ((0.5-r1)/2.0); + band[i].c2=2.0 * r1; + band[i].c3=2.0 * (0.5+r1) * cos(th); + //printf("band %i, coefs = %f,%f,%f\n",i,(float)bands[i].c1,(float)bands[i].c2,(float)bands[i].c3); + + } +} + +void EQ::set_preset_band_mode(Preset p_preset) { + + + band.clear(); + +#define PUSH_BANDS(m_bands) \ + for (int i=0;i<m_bands;i++) { \ + Band b; \ + b.freq=bands[i];\ + band.push_back(b);\ + } + + switch (p_preset) { + + case PRESET_6_BANDS: { + + static const double bands[] = { 32 , 100 , 320 , 1e3, 3200, 10e3 }; + PUSH_BANDS(6); + + } break; + + case PRESET_8_BANDS: { + + static const double bands[] = { 32,72,192,512,1200,3000,7500,16e3 }; + + PUSH_BANDS(8); + } break; + + case PRESET_10_BANDS: { + static const double bands[] = { 31.25, 62.5, 125 , 250 , 500 , 1e3, 2e3, 4e3, 8e3, 16e3 }; + + PUSH_BANDS(10); + + } break; + + case PRESET_21_BANDS: { + + static const double bands[] = { 22 , 32 , 44 , 63 , 90 , 125 , 175 , 250 , 350 , 500 , 700 , 1e3, 1400 , 2e3, 2800 , 4e3, 5600 , 8e3, 11e3, 16e3, 22e3 }; + PUSH_BANDS(21); + + } break; + + case PRESET_31_BANDS: { + + static const double bands[] = { 20, 25, 31.5, 40 , 50 , 63 , 80 , 100 , 125 , 160 , 200 , 250 , 315 , 400 , 500 , 630 , 800 , 1e3 , 1250 , 1600 , 2e3, 2500 , 3150 , 4e3, 5e3, 6300 , 8e3, 10e3, 12500 , 16e3, 20e3 }; + PUSH_BANDS(31); + } break; + + }; + + recalculate_band_coefficients(); +} + +int EQ::get_band_count() const { + + return band.size(); +} +float EQ::get_band_frequency(int p_band) { + + ERR_FAIL_INDEX_V(p_band,band.size(),0); + return band[p_band].freq; +} +void EQ::set_bands(const Vector<float>& p_bands) { + + band.resize(p_bands.size()); + for (int i=0;i<p_bands.size();i++) { + + band[i].freq=p_bands[i]; + } + + recalculate_band_coefficients(); + +} + +void EQ::set_mix_rate(float p_mix_rate) { + + mix_rate=p_mix_rate; + recalculate_band_coefficients(); +} + +EQ::BandProcess EQ::get_band_processor(int p_band) const { + + + EQ::BandProcess band_proc; + + ERR_FAIL_INDEX_V(p_band,band.size(),band_proc); + + band_proc.c1=band[p_band].c1; + band_proc.c2=band[p_band].c2; + band_proc.c3=band[p_band].c3; + + return band_proc; + + +} + + +EQ::EQ() +{ + mix_rate=44100; +} + + +EQ::~EQ() +{ +} + + diff --git a/servers/audio/effects/eq.h b/servers/audio/effects/eq.h new file mode 100644 index 0000000000..2c4668cd0b --- /dev/null +++ b/servers/audio/effects/eq.h @@ -0,0 +1,106 @@ +// +// C++ Interface: eq +// +// Description: +// +// +// Author: reduzio@gmail.com (C) 2006 +// +// Copyright: See COPYING file that comes with this distribution +// +// +#ifndef EQ_FILTER_H +#define EQ_FILTER_H + + +#include "typedefs.h" +#include "vector.h" + + +/** +@author Juan Linietsky +*/ + +class EQ { +public: + + enum Preset { + + PRESET_6_BANDS, + PRESET_8_BANDS, + PRESET_10_BANDS, + PRESET_21_BANDS, + PRESET_31_BANDS + }; + + + + class BandProcess { + + friend class EQ; + float c1,c2,c3; + struct History { + float a1,a2,a3; + float b1,b2,b3; + + } history; + + public: + + inline void process_one(float & p_data); + + BandProcess(); + }; + +private: + struct Band { + + float freq; + float c1,c2,c3; + }; + + Vector<Band> band; + + float mix_rate; + + void recalculate_band_coefficients(); + +public: + + + void set_mix_rate(float p_mix_rate); + + int get_band_count() const; + void set_preset_band_mode(Preset p_preset); + void set_bands(const Vector<float>& p_bands); + BandProcess get_band_processor(int p_band) const; + float get_band_frequency(int p_band); + + EQ(); + ~EQ(); + +}; + + +/* Inline Function */ + +inline void EQ::BandProcess::process_one(float & p_data) { + + + history.a1=p_data; + + history.b1= c1 * ( history.a1 - history.a3 ) + + c3 * history.b2 + - c2 * history.b3; + + p_data = history.b1; + + history.a3=history.a2; + history.a2=history.a1; + history.b3=history.b2; + history.b2=history.b1; + +} + + +#endif diff --git a/servers/audio/effects/reverb.cpp b/servers/audio/effects/reverb.cpp new file mode 100644 index 0000000000..2a4c728b3f --- /dev/null +++ b/servers/audio/effects/reverb.cpp @@ -0,0 +1,363 @@ +// +// C++ Interface: reverb +// +// Description: +// +// +// Author: Juan Linietsky <reduzio@gmail.com>, (C) 2006 +// +// Copyright: See COPYING file that comes with this distribution +// +// + +#include "reverb.h" +#include <math.h> + + +const float Reverb::comb_tunings[MAX_COMBS]={ + //freeverb comb tunings + 0.025306122448979593, + 0.026938775510204082, + 0.028956916099773241, + 0.03074829931972789, + 0.032244897959183672, + 0.03380952380952381, + 0.035306122448979592, + 0.036666666666666667 +}; + +const float Reverb::allpass_tunings[MAX_ALLPASS]={ + //freeverb allpass tunings + 0.0051020408163265302, + 0.007732426303854875, + 0.01, + 0.012607709750566893 +}; + + + +void Reverb::process(float *p_src,float *p_dst,int p_frames) { + + if (p_frames>INPUT_BUFFER_MAX_SIZE) + p_frames=INPUT_BUFFER_MAX_SIZE; + + int predelay_frames=lrint((params.predelay/1000.0)*params.mix_rate); + if (predelay_frames<10) + predelay_frames=10; + if (predelay_frames>=echo_buffer_size) + predelay_frames=echo_buffer_size-1; + + for (int i=0;i<p_frames;i++) { + + if (echo_buffer_pos>=echo_buffer_size) + echo_buffer_pos=0; + + int read_pos=echo_buffer_pos-predelay_frames; + while (read_pos<0) + read_pos+=echo_buffer_size; + + float in=undenormalise(echo_buffer[read_pos]*params.predelay_fb+p_src[i]); + + echo_buffer[echo_buffer_pos]=in; + + input_buffer[i]=in; + + p_dst[i]=0; //take the chance and clear this + + echo_buffer_pos++; + } + + if (params.hpf>0) { + float hpaux=expf(-2.0*M_PI*params.hpf*6000/params.mix_rate); + float hp_a1=(1.0+hpaux)/2.0; + float hp_a2=-(1.0+hpaux)/2.0; + float hp_b1=hpaux; + + for (int i=0;i<p_frames;i++) { + + float in=input_buffer[i]; + input_buffer[i]=in*hp_a1+hpf_h1*hp_a2+hpf_h2*hp_b1; + hpf_h2=input_buffer[i]; + hpf_h1=in; + } + } + + for (int i=0;i<MAX_COMBS;i++) { + + Comb &c=comb[i]; + + int size_limit=c.size-lrintf((float)c.extra_spread_frames*(1.0-params.extra_spread)); + for (int j=0;j<p_frames;j++) { + + if (c.pos>=size_limit) //reset this now just in case + c.pos=0; + + float out=undenormalise(c.buffer[c.pos]*c.feedback); + out=out*(1.0-c.damp)+c.damp_h*c.damp; //lowpass + c.damp_h=out; + c.buffer[c.pos]=input_buffer[j]+out; + p_dst[j]+=out; + c.pos++; + } + + } + + + static const float allpass_feedback=0.7; + /* this one works, but the other version is just nicer.... + int ap_size_limit[MAX_ALLPASS]; + + for (int i=0;i<MAX_ALLPASS;i++) { + + AllPass &a=allpass[i]; + ap_size_limit[i]=a.size-lrintf((float)a.extra_spread_frames*(1.0-params.extra_spread)); + } + + for (int i=0;i<p_frames;i++) { + + float sample=p_dst[i]; + float aux,in; + float AllPass*ap; + +#define PROCESS_ALLPASS(m_ap) \ + ap=&allpass[m_ap]; \ + if (ap->pos>=ap_size_limit[m_ap]) \ + ap->pos=0; \ + aux=undenormalise(ap->buffer[ap->pos]); \ + in=sample; \ + sample=-in+aux; \ + ap->pos++; + + + PROCESS_ALLPASS(0); + PROCESS_ALLPASS(1); + PROCESS_ALLPASS(2); + PROCESS_ALLPASS(3); + + p_dst[i]=sample; + } + */ + + for (int i=0;i<MAX_ALLPASS;i++) { + + AllPass &a=allpass[i]; + int size_limit=a.size-lrintf((float)a.extra_spread_frames*(1.0-params.extra_spread)); + + for (int j=0;j<p_frames;j++) { + + if (a.pos>=size_limit) + a.pos=0; + + float aux=a.buffer[a.pos]; + a.buffer[a.pos]=undenormalise(allpass_feedback*aux+p_dst[j]); + p_dst[j]=aux-allpass_feedback*a.buffer[a.pos]; + a.pos++; + + } + } + + static const float wet_scale=0.6; + + for (int i=0;i<p_frames;i++) { + + + p_dst[i]=p_dst[i]*params.wet*wet_scale+p_src[i]*params.dry; + } + +} + + +void Reverb::set_room_size(float p_size) { + + params.room_size=p_size; + update_parameters(); + +} +void Reverb::set_damp(float p_damp) { + + params.damp=p_damp; + update_parameters(); + +} +void Reverb::set_wet(float p_wet) { + + params.wet=p_wet; + +} + +void Reverb::set_dry(float p_dry) { + + params.dry=p_dry; + +} + +void Reverb::set_predelay(float p_predelay) { + + params.predelay=p_predelay; +} +void Reverb::set_predelay_feedback(float p_predelay_fb) { + + params.predelay_fb=p_predelay_fb; + +} + +void Reverb::set_highpass(float p_frq) { + + if (p_frq>1) + p_frq=1; + if (p_frq<0) + p_frq=0; + params.hpf=p_frq; +} + +void Reverb::set_extra_spread(float p_spread) { + + params.extra_spread=p_spread; + +} + + +void Reverb::set_mix_rate(float p_mix_rate) { + + params.mix_rate=p_mix_rate; + configure_buffers(); +} + +void Reverb::set_extra_spread_base(float p_sec) { + + params.extra_spread_base=p_sec; + configure_buffers(); +} + + +void Reverb::configure_buffers() { + + clear_buffers(); //clear if necesary + + for (int i=0;i<MAX_COMBS;i++) { + + Comb &c=comb[i]; + + + c.extra_spread_frames=lrint(params.extra_spread_base*params.mix_rate); + + int len=lrint(comb_tunings[i]*params.mix_rate)+c.extra_spread_frames; + if (len<5) + len=5; //may this happen? + + c.buffer = memnew_arr(float,len); + c.pos=0; + for (int j=0;j<len;j++) + c.buffer[j]=0; + c.size=len; + + } + + for (int i=0;i<MAX_ALLPASS;i++) { + + AllPass &a=allpass[i]; + + a.extra_spread_frames=lrint(params.extra_spread_base*params.mix_rate); + + int len=lrint(allpass_tunings[i]*params.mix_rate)+a.extra_spread_frames; + if (len<5) + len=5; //may this happen? + + a.buffer = memnew_arr(float,len); + a.pos=0; + for (int j=0;j<len;j++) + a.buffer[j]=0; + a.size=len; + } + + echo_buffer_size=(int)(((float)MAX_ECHO_MS/1000.0)*params.mix_rate+1.0); + echo_buffer = memnew_arr(float,echo_buffer_size); + for (int i=0;i<echo_buffer_size;i++) { + + echo_buffer[i]=0; + } + + echo_buffer_pos=0; +} + + +void Reverb::update_parameters() { + + //more freeverb derived constants + static const float room_scale = 0.28f; + static const float room_offset = 0.7f; + + for (int i=0;i<MAX_COMBS;i++) { + + Comb &c=comb[i]; + c.feedback=room_offset+params.room_size*room_scale; + if (c.feedback<room_offset) + c.feedback=room_offset; + else if (c.feedback>(room_offset+room_scale)) + c.feedback=(room_offset+room_scale); + + float auxdmp=params.damp/2.0+0.5; //only half the range (0.5 .. 1.0 is enough) + auxdmp*=auxdmp; + + c.damp=expf(-2.0*M_PI*auxdmp*10000/params.mix_rate); // 0 .. 10khz + } + +} + +void Reverb::clear_buffers() { + + if (echo_buffer) + memdelete_arr(echo_buffer); + + for (int i=0;i<MAX_COMBS;i++) { + + if (comb[i].buffer) + memdelete_arr(comb[i].buffer); + + comb[i].buffer=0; + + } + + for (int i=0;i<MAX_ALLPASS;i++) { + + if (allpass[i].buffer) + memdelete_arr(allpass[i].buffer); + + allpass[i].buffer=0; + } + +} + +Reverb::Reverb() { + + params.room_size=0.8; + params.damp=0.5; + params.dry=1.0; + params.wet=0.0; + params.mix_rate=44100; + params.extra_spread_base=0; + params.extra_spread=1.0; + params.predelay=150; + params.predelay_fb=0.4; + params.hpf=0; + hpf_h1=0; + hpf_h2=0; + + + input_buffer=memnew_arr(float,INPUT_BUFFER_MAX_SIZE); + echo_buffer=0; + + configure_buffers(); + update_parameters(); + + +} + + +Reverb::~Reverb() { + + memdelete_arr(input_buffer); + clear_buffers(); +} + + diff --git a/servers/audio/effects/reverb.h b/servers/audio/effects/reverb.h new file mode 100644 index 0000000000..2c82be9156 --- /dev/null +++ b/servers/audio/effects/reverb.h @@ -0,0 +1,111 @@ +// +// C++ Interface: reverb +// +// Description: +// +// +// Author: Juan Linietsky <reduzio@gmail.com>, (C) 2006 +// +// Copyright: See COPYING file that comes with this distribution +// +// +#ifndef REVERB_H +#define REVERB_H + +#include "typedefs.h" +#include "os/memory.h" +#include "audio_frame.h" + +class Reverb { +public: + enum { + INPUT_BUFFER_MAX_SIZE=1024, + + }; +private: + enum { + + MAX_COMBS=8, + MAX_ALLPASS=4, + MAX_ECHO_MS=500 + + }; + + + + static const float comb_tunings[MAX_COMBS]; + static const float allpass_tunings[MAX_ALLPASS]; + + struct Comb { + + int size; + float *buffer; + float feedback; + float damp; //lowpass + float damp_h; //history + int pos; + int extra_spread_frames; + + Comb() { size=0; buffer=0; feedback=0; damp_h=0; pos=0; } + }; + + struct AllPass { + + int size; + float *buffer; + int pos; + int extra_spread_frames; + AllPass() { size=0; buffer=0; pos=0; } + }; + + Comb comb[MAX_COMBS]; + AllPass allpass[MAX_ALLPASS]; + float *input_buffer; + float *echo_buffer; + int echo_buffer_size; + int echo_buffer_pos; + + float hpf_h1,hpf_h2; + + + struct Parameters { + + float room_size; + float damp; + float wet; + float dry; + float mix_rate; + float extra_spread_base; + float extra_spread; + float predelay; + float predelay_fb; + float hpf; + } params; + + void configure_buffers(); + void update_parameters(); + void clear_buffers(); +public: + + void set_room_size(float p_size); + void set_damp(float p_damp); + void set_wet(float p_wet); + void set_dry(float p_dry); + void set_predelay(float p_predelay); // in ms + void set_predelay_feedback(float p_predelay_fb); // in ms + void set_highpass(float p_frq); + void set_mix_rate(float p_mix_rate); + void set_extra_spread(float p_spread); + void set_extra_spread_base(float p_sec); + + void process(float *p_src,float *p_dst,int p_frames); + + Reverb(); + + ~Reverb(); + +}; + + + +#endif |