WIP new AudioServer, with buses, effects, etc.
@ -3,8 +3,25 @@
|
||||
|
||||
#include "typedefs.h"
|
||||
|
||||
|
||||
static inline float undenormalise(volatile float f)
|
||||
{
|
||||
union {
|
||||
uint32_t i;
|
||||
float f;
|
||||
} v;
|
||||
|
||||
v.f = f;
|
||||
|
||||
// original: return (v.i & 0x7f800000) == 0 ? 0.0f : f;
|
||||
// version from Tim Blechmann:
|
||||
return (v.i & 0x7f800000) < 0x08000000 ? 0.0f : f;
|
||||
}
|
||||
|
||||
|
||||
struct AudioFrame {
|
||||
|
||||
//left and right samples
|
||||
float l,r;
|
||||
|
||||
_ALWAYS_INLINE_ const float& operator[](int idx) const { return idx==0?l:r; }
|
||||
@ -15,14 +32,30 @@ struct AudioFrame {
|
||||
_ALWAYS_INLINE_ AudioFrame operator*(const AudioFrame& p_frame) const { return AudioFrame(l*p_frame.l,r*p_frame.r); }
|
||||
_ALWAYS_INLINE_ AudioFrame operator/(const AudioFrame& p_frame) const { return AudioFrame(l/p_frame.l,r/p_frame.r); }
|
||||
|
||||
_ALWAYS_INLINE_ AudioFrame operator+(float p_sample) const { return AudioFrame(l+p_sample,r+p_sample); }
|
||||
_ALWAYS_INLINE_ AudioFrame operator-(float p_sample) const { return AudioFrame(l-p_sample,r-p_sample); }
|
||||
_ALWAYS_INLINE_ AudioFrame operator*(float p_sample) const { return AudioFrame(l*p_sample,r*p_sample); }
|
||||
_ALWAYS_INLINE_ AudioFrame operator/(float p_sample) const { return AudioFrame(l/p_sample,r/p_sample); }
|
||||
|
||||
_ALWAYS_INLINE_ void operator+=(const AudioFrame& p_frame) { l+=p_frame.l; r+=p_frame.r; }
|
||||
_ALWAYS_INLINE_ void operator-=(const AudioFrame& p_frame) { l-=p_frame.l; r-=p_frame.r; }
|
||||
_ALWAYS_INLINE_ void operator*=(const AudioFrame& p_frame) { l*=p_frame.l; r*=p_frame.r; }
|
||||
_ALWAYS_INLINE_ void operator/=(const AudioFrame& p_frame) { l/=p_frame.l; r/=p_frame.r; }
|
||||
|
||||
_ALWAYS_INLINE_ void operator+=(float p_sample) { l+=p_sample; r+=p_sample; }
|
||||
_ALWAYS_INLINE_ void operator-=(float p_sample) { l-=p_sample; r-=p_sample; }
|
||||
_ALWAYS_INLINE_ void operator*=(float p_sample) { l*=p_sample; r*=p_sample; }
|
||||
_ALWAYS_INLINE_ void operator/=(float p_sample) { l/=p_sample; r/=p_sample; }
|
||||
|
||||
_ALWAYS_INLINE_ void undenormalise() {
|
||||
l = ::undenormalise(l);
|
||||
r = ::undenormalise(r);
|
||||
}
|
||||
|
||||
_ALWAYS_INLINE_ AudioFrame(float p_l, float p_r) {l=p_l; r=p_r;}
|
||||
_ALWAYS_INLINE_ AudioFrame(const AudioFrame& p_frame) {l=p_frame.l; r=p_frame.r;}
|
||||
|
||||
_ALWAYS_INLINE_ AudioFrame() {}
|
||||
};
|
||||
|
||||
#endif
|
||||
|
@ -486,6 +486,7 @@ Resource::Resource() {
|
||||
#endif
|
||||
|
||||
subindex=0;
|
||||
local_to_scene=false;
|
||||
local_scene=NULL;
|
||||
}
|
||||
|
||||
|
@ -65,15 +65,15 @@ Error AudioDriverPulseAudio::init() {
|
||||
|
||||
int error_code;
|
||||
pulse = pa_simple_new( NULL, // default server
|
||||
"Godot", // application name
|
||||
PA_STREAM_PLAYBACK,
|
||||
NULL, // default device
|
||||
"Sound", // stream description
|
||||
&spec,
|
||||
NULL, // use default channel map
|
||||
&attr, // use buffering attributes from above
|
||||
&error_code
|
||||
);
|
||||
"Godot", // application name
|
||||
PA_STREAM_PLAYBACK,
|
||||
NULL, // default device
|
||||
"Sound", // stream description
|
||||
&spec,
|
||||
NULL, // use default channel map
|
||||
&attr, // use buffering attributes from above
|
||||
&error_code
|
||||
);
|
||||
|
||||
if (pulse == NULL) {
|
||||
fprintf(stderr, "PulseAudio ERR: %s\n", pa_strerror(error_code));\
|
||||
@ -103,6 +103,7 @@ float AudioDriverPulseAudio::get_latency() {
|
||||
|
||||
void AudioDriverPulseAudio::thread_func(void* p_udata) {
|
||||
|
||||
print_line("thread");
|
||||
AudioDriverPulseAudio* ad = (AudioDriverPulseAudio*)p_udata;
|
||||
|
||||
while (!ad->exit_thread) {
|
||||
@ -121,9 +122,9 @@ void AudioDriverPulseAudio::thread_func(void* p_udata) {
|
||||
for (unsigned int i=0; i < ad->buffer_size * ad->channels;i ++) {
|
||||
ad->samples_out[i] = ad->samples_in[i] >> 16;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// pa_simple_write always consumes the entire buffer
|
||||
// pa_simple_write always consumes the entire buffer
|
||||
|
||||
int error_code;
|
||||
int byte_size = ad->buffer_size * sizeof(int16_t) * ad->channels;
|
||||
@ -134,7 +135,7 @@ void AudioDriverPulseAudio::thread_func(void* p_udata) {
|
||||
ad->exit_thread = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ad->thread_exited = true;
|
||||
}
|
||||
|
10
modules/stb_vorbis/SCsub
Normal file
@ -0,0 +1,10 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
Import('env')
|
||||
Import('env_modules')
|
||||
|
||||
# Thirdparty source files
|
||||
|
||||
env_stb_vorbis = env_modules.Clone()
|
||||
|
||||
env_stb_vorbis.add_source_files(env.modules_sources, "*.cpp")
|
236
modules/stb_vorbis/audio_stream_ogg_vorbis.cpp
Normal file
@ -0,0 +1,236 @@
|
||||
|
||||
#include "audio_stream_ogg_vorbis.h"
|
||||
#include "thirdparty/stb_vorbis/stb_vorbis.c"
|
||||
#include "os/file_access.h"
|
||||
|
||||
|
||||
void AudioStreamPlaybackOGGVorbis::_mix_internal(AudioFrame* p_buffer,int p_frames) {
|
||||
|
||||
ERR_FAIL_COND(!active);
|
||||
|
||||
int todo=p_frames;
|
||||
|
||||
while(todo) {
|
||||
|
||||
int mixed = stb_vorbis_get_samples_float_interleaved(ogg_stream,2,(float*)p_buffer,todo*2);
|
||||
todo-=mixed;
|
||||
|
||||
if (todo) {
|
||||
//end of file!
|
||||
if (false) {
|
||||
//loop
|
||||
seek_pos(0);
|
||||
loops++;
|
||||
} else {
|
||||
for(int i=mixed;i<p_frames;i++) {
|
||||
p_buffer[i]=AudioFrame(0,0);
|
||||
}
|
||||
active=false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
float AudioStreamPlaybackOGGVorbis::get_stream_sampling_rate() {
|
||||
|
||||
return vorbis_stream->sample_rate;
|
||||
}
|
||||
|
||||
|
||||
void AudioStreamPlaybackOGGVorbis::start(float p_from_pos) {
|
||||
|
||||
seek_pos(p_from_pos);
|
||||
active=true;
|
||||
loops=0;
|
||||
_begin_resample();
|
||||
|
||||
|
||||
}
|
||||
|
||||
void AudioStreamPlaybackOGGVorbis::stop() {
|
||||
|
||||
active=false;
|
||||
}
|
||||
bool AudioStreamPlaybackOGGVorbis::is_playing() const {
|
||||
|
||||
return active;
|
||||
}
|
||||
|
||||
int AudioStreamPlaybackOGGVorbis::get_loop_count() const {
|
||||
|
||||
return loops;
|
||||
}
|
||||
|
||||
float AudioStreamPlaybackOGGVorbis::get_pos() const {
|
||||
|
||||
return float(frames_mixed)/vorbis_stream->sample_rate;
|
||||
}
|
||||
void AudioStreamPlaybackOGGVorbis::seek_pos(float p_time) {
|
||||
|
||||
if (!active)
|
||||
return;
|
||||
|
||||
stb_vorbis_seek(ogg_stream, uint32_t(p_time*vorbis_stream->sample_rate));
|
||||
}
|
||||
|
||||
float AudioStreamPlaybackOGGVorbis::get_length() const {
|
||||
|
||||
return vorbis_stream->length;
|
||||
}
|
||||
|
||||
AudioStreamPlaybackOGGVorbis::~AudioStreamPlaybackOGGVorbis() {
|
||||
if (ogg_alloc.alloc_buffer) {
|
||||
AudioServer::get_singleton()->audio_data_free(ogg_alloc.alloc_buffer);
|
||||
stb_vorbis_close(ogg_stream);
|
||||
}
|
||||
}
|
||||
|
||||
Ref<AudioStreamPlayback> AudioStreamOGGVorbis::instance_playback() {
|
||||
|
||||
|
||||
|
||||
Ref<AudioStreamPlaybackOGGVorbis> ovs;
|
||||
printf("instance at %p, data %p\n",this,data);
|
||||
|
||||
ERR_FAIL_COND_V(data==NULL,ovs);
|
||||
|
||||
ovs.instance();
|
||||
ovs->vorbis_stream=Ref<AudioStreamOGGVorbis>(this);
|
||||
ovs->ogg_alloc.alloc_buffer=(char*)AudioServer::get_singleton()->audio_data_alloc(decode_mem_size);
|
||||
ovs->ogg_alloc.alloc_buffer_length_in_bytes=decode_mem_size;
|
||||
ovs->frames_mixed=0;
|
||||
ovs->active=false;
|
||||
ovs->loops=0;
|
||||
int error ;
|
||||
ovs->ogg_stream = stb_vorbis_open_memory( (const unsigned char*)data, data_len, &error, &ovs->ogg_alloc );
|
||||
if (!ovs->ogg_stream) {
|
||||
|
||||
AudioServer::get_singleton()->audio_data_free(ovs->ogg_alloc.alloc_buffer);
|
||||
ovs->ogg_alloc.alloc_buffer=NULL;
|
||||
ERR_FAIL_COND_V(!ovs->ogg_stream,Ref<AudioStreamPlaybackOGGVorbis>());
|
||||
}
|
||||
|
||||
return ovs;
|
||||
}
|
||||
|
||||
String AudioStreamOGGVorbis::get_stream_name() const {
|
||||
|
||||
return "";//return stream_name;
|
||||
}
|
||||
|
||||
Error AudioStreamOGGVorbis::setup(const uint8_t *p_data,uint32_t p_data_len) {
|
||||
|
||||
|
||||
#define MAX_TEST_MEM (1<<20)
|
||||
|
||||
uint32_t alloc_try=1024;
|
||||
PoolVector<char> alloc_mem;
|
||||
PoolVector<char>::Write w;
|
||||
stb_vorbis * ogg_stream=NULL;
|
||||
stb_vorbis_alloc ogg_alloc;
|
||||
|
||||
while(alloc_try<MAX_TEST_MEM) {
|
||||
|
||||
alloc_mem.resize(alloc_try);
|
||||
w = alloc_mem.write();
|
||||
|
||||
ogg_alloc.alloc_buffer=w.ptr();
|
||||
ogg_alloc.alloc_buffer_length_in_bytes=alloc_try;
|
||||
|
||||
int error;
|
||||
ogg_stream = stb_vorbis_open_memory( (const unsigned char*)p_data, p_data_len, &error, &ogg_alloc );
|
||||
|
||||
if (!ogg_stream && error==VORBIS_outofmem) {
|
||||
w = PoolVector<char>::Write();
|
||||
alloc_try*=2;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
ERR_FAIL_COND_V(alloc_try==MAX_TEST_MEM,ERR_OUT_OF_MEMORY);
|
||||
ERR_FAIL_COND_V(ogg_stream==NULL,ERR_FILE_CORRUPT);
|
||||
|
||||
stb_vorbis_info info = stb_vorbis_get_info(ogg_stream);
|
||||
|
||||
channels = info.channels;
|
||||
sample_rate = info.sample_rate;
|
||||
decode_mem_size = alloc_try;
|
||||
//does this work? (it's less mem..)
|
||||
//decode_mem_size = ogg_alloc.alloc_buffer_length_in_bytes + info.setup_memory_required + info.temp_memory_required + info.max_frame_size;
|
||||
|
||||
//print_line("succeded "+itos(ogg_alloc.alloc_buffer_length_in_bytes)+" setup "+itos(info.setup_memory_required)+" setup temp "+itos(info.setup_temp_memory_required)+" temp "+itos(info.temp_memory_required)+" maxframe"+itos(info.max_frame_size));
|
||||
|
||||
length=stb_vorbis_stream_length_in_seconds(ogg_stream);
|
||||
stb_vorbis_close(ogg_stream);
|
||||
|
||||
data = AudioServer::get_singleton()->audio_data_alloc(p_data_len,p_data);
|
||||
data_len=p_data_len;
|
||||
|
||||
printf("create at %p, data %p\n",this,data);
|
||||
return OK;
|
||||
}
|
||||
|
||||
AudioStreamOGGVorbis::AudioStreamOGGVorbis() {
|
||||
|
||||
|
||||
data=NULL;
|
||||
length=0;
|
||||
sample_rate=1;
|
||||
channels=1;
|
||||
decode_mem_size=0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
RES ResourceFormatLoaderAudioStreamOGGVorbis::load(const String &p_path, const String& p_original_path, Error *r_error) {
|
||||
if (r_error)
|
||||
*r_error=OK;
|
||||
|
||||
FileAccess *f = FileAccess::open(p_path,FileAccess::READ);
|
||||
if (!f) {
|
||||
*r_error=ERR_CANT_OPEN;
|
||||
ERR_FAIL_COND_V(!f,RES());
|
||||
}
|
||||
|
||||
size_t len = f->get_len();
|
||||
|
||||
PoolVector<uint8_t> data;
|
||||
data.resize(len);
|
||||
PoolVector<uint8_t>::Write w = data.write();
|
||||
|
||||
f->get_buffer(w.ptr(),len);
|
||||
|
||||
memdelete(f);
|
||||
|
||||
Ref<AudioStreamOGGVorbis> ogg_stream;
|
||||
ogg_stream.instance();
|
||||
|
||||
Error err = ogg_stream->setup(w.ptr(),len);
|
||||
|
||||
if (err!=OK) {
|
||||
*r_error=err;
|
||||
ogg_stream.unref();
|
||||
ERR_FAIL_V(RES());
|
||||
}
|
||||
|
||||
return ogg_stream;
|
||||
}
|
||||
|
||||
void ResourceFormatLoaderAudioStreamOGGVorbis::get_recognized_extensions(List<String> *p_extensions) const {
|
||||
|
||||
p_extensions->push_back("ogg");
|
||||
}
|
||||
String ResourceFormatLoaderAudioStreamOGGVorbis::get_resource_type(const String &p_path) const {
|
||||
|
||||
if (p_path.get_extension().to_lower()=="ogg")
|
||||
return "AudioStreamOGGVorbis";
|
||||
return "";
|
||||
}
|
||||
|
||||
bool ResourceFormatLoaderAudioStreamOGGVorbis::handles_type(const String& p_type) const {
|
||||
return (p_type=="AudioStream" || p_type=="AudioStreamOGG" || p_type=="AudioStreamOGGVorbis");
|
||||
}
|
||||
|
84
modules/stb_vorbis/audio_stream_ogg_vorbis.h
Normal file
@ -0,0 +1,84 @@
|
||||
#ifndef AUDIO_STREAM_STB_VORBIS_H
|
||||
#define AUDIO_STREAM_STB_VORBIS_H
|
||||
|
||||
#include "servers/audio/audio_stream.h"
|
||||
#include "io/resource_loader.h"
|
||||
|
||||
#define STB_VORBIS_HEADER_ONLY
|
||||
#include "thirdparty/stb_vorbis/stb_vorbis.c"
|
||||
#undef STB_VORBIS_HEADER_ONLY
|
||||
|
||||
|
||||
class AudioStreamOGGVorbis;
|
||||
|
||||
class AudioStreamPlaybackOGGVorbis : public AudioStreamPlaybackResampled {
|
||||
|
||||
GDCLASS( AudioStreamPlaybackOGGVorbis, AudioStreamPlaybackResampled )
|
||||
|
||||
stb_vorbis * ogg_stream;
|
||||
stb_vorbis_alloc ogg_alloc;
|
||||
uint32_t frames_mixed;
|
||||
bool active;
|
||||
int loops;
|
||||
|
||||
friend class AudioStreamOGGVorbis;
|
||||
|
||||
Ref<AudioStreamOGGVorbis> vorbis_stream;
|
||||
protected:
|
||||
|
||||
virtual void _mix_internal(AudioFrame* p_buffer, int p_frames);
|
||||
virtual float get_stream_sampling_rate();
|
||||
|
||||
public:
|
||||
virtual void start(float p_from_pos=0.0);
|
||||
virtual void stop();
|
||||
virtual bool is_playing() const;
|
||||
|
||||
virtual int get_loop_count() const; //times it looped
|
||||
|
||||
virtual float get_pos() const;
|
||||
virtual void seek_pos(float p_time);
|
||||
|
||||
virtual float get_length() const; //if supported, otherwise return 0
|
||||
|
||||
AudioStreamPlaybackOGGVorbis() { }
|
||||
~AudioStreamPlaybackOGGVorbis();
|
||||
};
|
||||
|
||||
class AudioStreamOGGVorbis : public AudioStream {
|
||||
|
||||
GDCLASS( AudioStreamOGGVorbis, AudioStream )
|
||||
OBJ_SAVE_TYPE( AudioStream ) //children are all saved as AudioStream, so they can be exchanged
|
||||
|
||||
friend class AudioStreamPlaybackOGGVorbis;
|
||||
|
||||
void *data;
|
||||
uint32_t data_len;
|
||||
|
||||
int decode_mem_size;
|
||||
float sample_rate;
|
||||
int channels;
|
||||
float length;
|
||||
|
||||
public:
|
||||
|
||||
|
||||
virtual Ref<AudioStreamPlayback> instance_playback();
|
||||
virtual String get_stream_name() const;
|
||||
|
||||
Error setup(const uint8_t *p_data, uint32_t p_data_len);
|
||||
|
||||
AudioStreamOGGVorbis();
|
||||
};
|
||||
|
||||
class ResourceFormatLoaderAudioStreamOGGVorbis : public ResourceFormatLoader {
|
||||
public:
|
||||
virtual RES load(const String &p_path,const String& p_original_path="",Error *r_error=NULL);
|
||||
virtual void get_recognized_extensions(List<String> *p_extensions) const;
|
||||
virtual bool handles_type(const String& p_type) const;
|
||||
virtual String get_resource_type(const String &p_path) const;
|
||||
};
|
||||
|
||||
|
||||
|
||||
#endif
|
7
modules/stb_vorbis/config.py
Normal file
@ -0,0 +1,7 @@
|
||||
|
||||
def can_build(platform):
|
||||
return True
|
||||
|
||||
|
||||
def configure(env):
|
||||
pass
|
@ -1,5 +1,5 @@
|
||||
/*************************************************************************/
|
||||
/* audio_stream.cpp */
|
||||
/* register_types.cpp */
|
||||
/*************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
@ -26,36 +26,19 @@
|
||||
/* 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"
|
||||
#include "register_types.h"
|
||||
#include "audio_stream_ogg_vorbis.h"
|
||||
|
||||
//////////////////////////////
|
||||
|
||||
|
||||
void AudioStreamPlayback::_bind_methods() {
|
||||
|
||||
ClassDB::bind_method(_MD("play","from_pos_sec"),&AudioStreamPlayback::play,DEFVAL(0));
|
||||
ClassDB::bind_method(_MD("stop"),&AudioStreamPlayback::stop);
|
||||
ClassDB::bind_method(_MD("is_playing"),&AudioStreamPlayback::is_playing);
|
||||
|
||||
ClassDB::bind_method(_MD("set_loop","enabled"),&AudioStreamPlayback::set_loop);
|
||||
ClassDB::bind_method(_MD("has_loop"),&AudioStreamPlayback::has_loop);
|
||||
|
||||
ClassDB::bind_method(_MD("get_loop_count"),&AudioStreamPlayback::get_loop_count);
|
||||
|
||||
ClassDB::bind_method(_MD("seek_pos","pos"),&AudioStreamPlayback::seek_pos);
|
||||
ClassDB::bind_method(_MD("get_pos"),&AudioStreamPlayback::get_pos);
|
||||
|
||||
ClassDB::bind_method(_MD("get_length"),&AudioStreamPlayback::get_length);
|
||||
ClassDB::bind_method(_MD("get_channels"),&AudioStreamPlayback::get_channels);
|
||||
ClassDB::bind_method(_MD("get_mix_rate"),&AudioStreamPlayback::get_mix_rate);
|
||||
ClassDB::bind_method(_MD("get_minimum_buffer_size"),&AudioStreamPlayback::get_minimum_buffer_size);
|
||||
static ResourceFormatLoaderAudioStreamOGGVorbis *vorbis_stream_loader = NULL;
|
||||
|
||||
void register_stb_vorbis_types() {
|
||||
|
||||
vorbis_stream_loader = memnew( ResourceFormatLoaderAudioStreamOGGVorbis );
|
||||
ResourceLoader::add_resource_format_loader(vorbis_stream_loader);
|
||||
ClassDB::register_class<AudioStreamOGGVorbis>();
|
||||
}
|
||||
|
||||
void unregister_stb_vorbis_types() {
|
||||
|
||||
void AudioStream::_bind_methods() {
|
||||
|
||||
|
||||
memdelete( vorbis_stream_loader );
|
||||
}
|
||||
|
30
modules/stb_vorbis/register_types.h
Normal file
@ -0,0 +1,30 @@
|
||||
/*************************************************************************/
|
||||
/* register_types.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. */
|
||||
/*************************************************************************/
|
||||
void register_stb_vorbis_types();
|
||||
void unregister_stb_vorbis_types();
|
@ -295,6 +295,7 @@ void OS_X11::initialize(const VideoMode& p_desired,int p_video_driver,int p_audi
|
||||
}
|
||||
|
||||
|
||||
|
||||
ERR_FAIL_COND(!visual_server);
|
||||
ERR_FAIL_COND(x11_window==0);
|
||||
|
||||
|
301
scene/audio/audio_player.cpp
Normal file
@ -0,0 +1,301 @@
|
||||
#include "audio_player.h"
|
||||
|
||||
|
||||
void AudioPlayer::_mix_audio() {
|
||||
|
||||
if (!stream_playback.is_valid()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!active) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (setseek>=0.0) {
|
||||
stream_playback->start(setseek);
|
||||
setseek=-1.0; //reset seek
|
||||
|
||||
}
|
||||
|
||||
int bus_index = AudioServer::get_singleton()->thread_find_bus_index(bus);
|
||||
|
||||
//get data
|
||||
AudioFrame *buffer = mix_buffer.ptr();
|
||||
int buffer_size = mix_buffer.size();
|
||||
|
||||
//mix
|
||||
stream_playback->mix(buffer,1.0,buffer_size);
|
||||
|
||||
//multiply volume interpolating to avoid clicks if this changes
|
||||
float vol = Math::db2linear(mix_volume_db);
|
||||
float vol_inc = (Math::db2linear(volume_db) - vol)/float(buffer_size);
|
||||
|
||||
for(int i=0;i<buffer_size;i++) {
|
||||
buffer[i]*=vol;
|
||||
vol+=vol_inc;
|
||||
}
|
||||
//set volume for next mix
|
||||
mix_volume_db = volume_db;
|
||||
|
||||
AudioFrame * targets[3]={NULL,NULL,NULL};
|
||||
|
||||
if (AudioServer::get_singleton()->get_speaker_mode()==AudioServer::SPEAKER_MODE_STEREO) {
|
||||
targets[0] = AudioServer::get_singleton()->thread_get_channel_mix_buffer(bus_index,0);
|
||||
} else {
|
||||
switch(mix_target) {
|
||||
case MIX_TARGET_STEREO: {
|
||||
targets[0]=AudioServer::get_singleton()->thread_get_channel_mix_buffer(bus_index,1);
|
||||
} break;
|
||||
case MIX_TARGET_SURROUND: {
|
||||
targets[0]=AudioServer::get_singleton()->thread_get_channel_mix_buffer(bus_index,1);
|
||||
targets[1]=AudioServer::get_singleton()->thread_get_channel_mix_buffer(bus_index,2);
|
||||
if (AudioServer::get_singleton()->get_speaker_mode()==AudioServer::SPEAKER_SURROUND_71) {
|
||||
targets[2]=AudioServer::get_singleton()->thread_get_channel_mix_buffer(bus_index,3);
|
||||
}
|
||||
} break;
|
||||
case MIX_TARGET_CENTER: {
|
||||
targets[0]=AudioServer::get_singleton()->thread_get_channel_mix_buffer(bus_index,0);
|
||||
} break;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
for(int c=0;c<3;c++) {
|
||||
if (!targets[c])
|
||||
break;
|
||||
for(int i=0;i<buffer_size;i++) {
|
||||
targets[c][i]+=buffer[i];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
void AudioPlayer::_notification(int p_what) {
|
||||
|
||||
if (p_what==NOTIFICATION_ENTER_TREE) {
|
||||
|
||||
AudioServer::get_singleton()->add_callback(_mix_audios,this);
|
||||
if (autoplay && !get_tree()->is_editor_hint()) {
|
||||
play();
|
||||
}
|
||||
}
|
||||
|
||||
if (p_what==NOTIFICATION_EXIT_TREE) {
|
||||
|
||||
AudioServer::get_singleton()->remove_callback(_mix_audios,this);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
void AudioPlayer::set_stream(Ref<AudioStream> p_stream) {
|
||||
|
||||
AudioServer::get_singleton()->lock();
|
||||
|
||||
mix_buffer.resize(AudioServer::get_singleton()->thread_get_mix_buffer_size());
|
||||
|
||||
if (stream_playback.is_valid()) {
|
||||
stream_playback.unref();
|
||||
stream.unref();
|
||||
active=false;
|
||||
setseek=-1;
|
||||
}
|
||||
|
||||
stream=p_stream;
|
||||
stream_playback=p_stream->instance_playback();
|
||||
|
||||
if (stream_playback.is_null()) {
|
||||
stream.unref();
|
||||
ERR_FAIL_COND(stream_playback.is_null());
|
||||
}
|
||||
|
||||
AudioServer::get_singleton()->unlock();
|
||||
|
||||
}
|
||||
|
||||
Ref<AudioStream> AudioPlayer::get_stream() const {
|
||||
|
||||
return stream;
|
||||
}
|
||||
|
||||
void AudioPlayer::set_volume_db(float p_volume) {
|
||||
|
||||
volume_db=p_volume;
|
||||
}
|
||||
float AudioPlayer::get_volume_db() const {
|
||||
|
||||
return volume_db;
|
||||
}
|
||||
|
||||
void AudioPlayer::play(float p_from_pos) {
|
||||
|
||||
if (stream_playback.is_valid()) {
|
||||
mix_volume_db=volume_db; //reset volume ramp
|
||||
setseek=p_from_pos;
|
||||
active=true;
|
||||
}
|
||||
}
|
||||
|
||||
void AudioPlayer::seek(float p_seconds) {
|
||||
|
||||
if (stream_playback.is_valid()) {
|
||||
setseek=p_seconds;
|
||||
}
|
||||
}
|
||||
|
||||
void AudioPlayer::stop() {
|
||||
|
||||
if (stream_playback.is_valid()) {
|
||||
active=false;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
bool AudioPlayer::is_playing() const {
|
||||
|
||||
if (stream_playback.is_valid()) {
|
||||
return active && stream_playback->is_playing();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
float AudioPlayer::get_pos() {
|
||||
|
||||
if (stream_playback.is_valid()) {
|
||||
return stream_playback->get_pos();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void AudioPlayer::set_bus(const StringName& p_bus) {
|
||||
|
||||
//if audio is active, must lock this
|
||||
AudioServer::get_singleton()->lock();
|
||||
bus=p_bus;
|
||||
AudioServer::get_singleton()->unlock();
|
||||
|
||||
}
|
||||
StringName AudioPlayer::get_bus() const {
|
||||
|
||||
for(int i=0;i<AudioServer::get_singleton()->get_bus_count();i++) {
|
||||
if (AudioServer::get_singleton()->get_bus_name(i)==bus) {
|
||||
return bus;
|
||||
}
|
||||
}
|
||||
return "Master";
|
||||
}
|
||||
|
||||
void AudioPlayer::set_autoplay(bool p_enable) {
|
||||
|
||||
autoplay=p_enable;
|
||||
}
|
||||
bool AudioPlayer::is_autoplay_enabled() {
|
||||
|
||||
return autoplay;
|
||||
}
|
||||
|
||||
void AudioPlayer::set_mix_target(MixTarget p_target) {
|
||||
|
||||
mix_target=p_target;
|
||||
}
|
||||
|
||||
AudioPlayer::MixTarget AudioPlayer::get_mix_target() const{
|
||||
|
||||
return mix_target;
|
||||
}
|
||||
|
||||
void AudioPlayer::_set_playing(bool p_enable) {
|
||||
|
||||
if (p_enable)
|
||||
play();
|
||||
else
|
||||
stop();
|
||||
}
|
||||
bool AudioPlayer::_is_active() const {
|
||||
|
||||
return active;
|
||||
}
|
||||
|
||||
|
||||
void AudioPlayer::_validate_property(PropertyInfo& property) const {
|
||||
|
||||
if (property.name=="bus") {
|
||||
|
||||
String options;
|
||||
for(int i=0;i<AudioServer::get_singleton()->get_bus_count();i++) {
|
||||
if (i>0)
|
||||
options+=",";
|
||||
String name = AudioServer::get_singleton()->get_bus_name(i);
|
||||
options+=name;
|
||||
}
|
||||
|
||||
property.hint_string=options;
|
||||
}
|
||||
}
|
||||
|
||||
void AudioPlayer::_bus_layout_changed() {
|
||||
|
||||
_change_notify();
|
||||
}
|
||||
|
||||
void AudioPlayer::_bind_methods() {
|
||||
|
||||
ClassDB::bind_method(_MD("set_stream","stream:AudioStream"),&AudioPlayer::set_stream);
|
||||
ClassDB::bind_method(_MD("get_stream"),&AudioPlayer::get_stream);
|
||||
|
||||
ClassDB::bind_method(_MD("set_volume_db","volume_db"),&AudioPlayer::set_volume_db);
|
||||
ClassDB::bind_method(_MD("get_volume_db"),&AudioPlayer::get_volume_db);
|
||||
|
||||
ClassDB::bind_method(_MD("play","from_pos"),&AudioPlayer::play,DEFVAL(0.0));
|
||||
ClassDB::bind_method(_MD("seek","to_pos"),&AudioPlayer::seek);
|
||||
ClassDB::bind_method(_MD("stop"),&AudioPlayer::stop);
|
||||
|
||||
ClassDB::bind_method(_MD("is_playing"),&AudioPlayer::is_playing);
|
||||
ClassDB::bind_method(_MD("get_pos"),&AudioPlayer::get_pos);
|
||||
|
||||
ClassDB::bind_method(_MD("set_bus","bus"),&AudioPlayer::set_bus);
|
||||
ClassDB::bind_method(_MD("get_bus"),&AudioPlayer::get_bus);
|
||||
|
||||
ClassDB::bind_method(_MD("set_autoplay","enable"),&AudioPlayer::set_autoplay);
|
||||
ClassDB::bind_method(_MD("is_autoplay_enabled"),&AudioPlayer::is_autoplay_enabled);
|
||||
|
||||
ClassDB::bind_method(_MD("set_mix_target","mix_target"),&AudioPlayer::set_mix_target);
|
||||
ClassDB::bind_method(_MD("get_mix_target"),&AudioPlayer::get_mix_target);
|
||||
|
||||
ClassDB::bind_method(_MD("_set_playing","enable"),&AudioPlayer::_set_playing);
|
||||
ClassDB::bind_method(_MD("_is_active"),&AudioPlayer::_is_active);
|
||||
|
||||
ClassDB::bind_method(_MD("_bus_layout_changed"),&AudioPlayer::_bus_layout_changed);
|
||||
|
||||
|
||||
ADD_PROPERTY( PropertyInfo(Variant::OBJECT,"stream",PROPERTY_HINT_RESOURCE_TYPE,"AudioStream"),_SCS("set_stream"),_SCS("get_stream") );
|
||||
ADD_PROPERTY( PropertyInfo(Variant::REAL,"volume_db",PROPERTY_HINT_RANGE,"-80,24"),_SCS("set_volume_db"),_SCS("get_volume_db") );
|
||||
ADD_PROPERTY( PropertyInfo(Variant::BOOL,"playing",PROPERTY_HINT_NONE,"",PROPERTY_USAGE_EDITOR),_SCS("_set_playing"),_SCS("_is_active" ));
|
||||
ADD_PROPERTY( PropertyInfo(Variant::BOOL,"autoplay"),_SCS("set_autoplay"),_SCS("is_autoplay_enabled") );
|
||||
ADD_PROPERTY( PropertyInfo(Variant::INT,"mix_target",PROPERTY_HINT_ENUM,"Stereo,Surround,Center"),_SCS("set_mix_target"),_SCS("get_mix_target"));
|
||||
ADD_PROPERTY( PropertyInfo(Variant::STRING,"bus",PROPERTY_HINT_ENUM,""),_SCS("set_bus"),_SCS("get_bus"));
|
||||
|
||||
}
|
||||
|
||||
AudioPlayer::AudioPlayer() {
|
||||
|
||||
mix_volume_db=0;
|
||||
volume_db=0;
|
||||
autoplay=false;
|
||||
setseek=-1;
|
||||
active=false;
|
||||
mix_target=MIX_TARGET_STEREO;
|
||||
|
||||
AudioServer::get_singleton()->connect("bus_layout_changed",this,"_bus_layout_changed");
|
||||
}
|
||||
|
||||
|
||||
|
||||
AudioPlayer::~AudioPlayer() {
|
||||
|
||||
|
||||
}
|
||||
|
75
scene/audio/audio_player.h
Normal file
@ -0,0 +1,75 @@
|
||||
#ifndef AUDIOPLAYER_H
|
||||
#define AUDIOPLAYER_H
|
||||
|
||||
#include "scene/main/node.h"
|
||||
#include "servers/audio/audio_stream.h"
|
||||
|
||||
|
||||
class AudioPlayer : public Node {
|
||||
|
||||
GDCLASS( AudioPlayer, Node )
|
||||
|
||||
public:
|
||||
|
||||
enum MixTarget {
|
||||
MIX_TARGET_STEREO,
|
||||
MIX_TARGET_SURROUND,
|
||||
MIX_TARGET_CENTER
|
||||
};
|
||||
private:
|
||||
Ref<AudioStreamPlayback> stream_playback;
|
||||
Ref<AudioStream> stream;
|
||||
Vector<AudioFrame> mix_buffer;
|
||||
|
||||
volatile float setseek;
|
||||
volatile bool active;
|
||||
|
||||
float mix_volume_db;
|
||||
float volume_db;
|
||||
bool autoplay;
|
||||
StringName bus;
|
||||
|
||||
MixTarget mix_target;
|
||||
|
||||
void _mix_audio();
|
||||
static void _mix_audios(void *self) { reinterpret_cast<AudioPlayer*>(self)->_mix_audio(); }
|
||||
|
||||
void _set_playing(bool p_enable);
|
||||
bool _is_active() const;
|
||||
|
||||
void _bus_layout_changed();
|
||||
|
||||
protected:
|
||||
|
||||
void _validate_property(PropertyInfo& property) const;
|
||||
void _notification(int p_what);
|
||||
static void _bind_methods();
|
||||
public:
|
||||
|
||||
void set_stream(Ref<AudioStream> p_stream);
|
||||
Ref<AudioStream> get_stream() const;
|
||||
|
||||
void set_volume_db(float p_volume);
|
||||
float get_volume_db() const;
|
||||
|
||||
void play(float p_from_pos=0.0);
|
||||
void seek(float p_seconds);
|
||||
void stop();
|
||||
bool is_playing() const;
|
||||
float get_pos();
|
||||
|
||||
void set_bus(const StringName& p_bus);
|
||||
StringName get_bus() const;
|
||||
|
||||
void set_autoplay(bool p_enable);
|
||||
bool is_autoplay_enabled();
|
||||
|
||||
void set_mix_target(MixTarget p_target);
|
||||
MixTarget get_mix_target() const;
|
||||
|
||||
AudioPlayer();
|
||||
~AudioPlayer();
|
||||
};
|
||||
|
||||
VARIANT_ENUM_CAST(AudioPlayer::MixTarget)
|
||||
#endif // AUDIOPLAYER_H
|
@ -46,7 +46,9 @@ void TextureProgress::set_over_texture(const Ref<Texture>& p_texture) {
|
||||
|
||||
over=p_texture;
|
||||
update();
|
||||
minimum_size_changed();
|
||||
if (under.is_null()) {
|
||||
minimum_size_changed();
|
||||
}
|
||||
}
|
||||
|
||||
Ref<Texture> TextureProgress::get_over_texture() const{
|
||||
@ -302,4 +304,5 @@ TextureProgress::TextureProgress()
|
||||
rad_init_angle=0;
|
||||
rad_center_off=Point2();
|
||||
rad_max_degrees=360;
|
||||
set_mouse_filter(MOUSE_FILTER_PASS);
|
||||
}
|
||||
|
@ -160,7 +160,7 @@ TextureRect::TextureRect() {
|
||||
|
||||
|
||||
expand=false;
|
||||
set_mouse_filter(MOUSE_FILTER_IGNORE);
|
||||
set_mouse_filter(MOUSE_FILTER_PASS);
|
||||
stretch_mode=STRETCH_SCALE_ON_EXPAND;
|
||||
}
|
||||
|
||||
|
@ -1008,7 +1008,7 @@ int Tree::draw_item(const Point2i& p_pos,const Point2& p_draw_ofs, const Size2&
|
||||
/* Draw label, if height fits */
|
||||
|
||||
|
||||
bool skip=(p_item==root && hide_root);
|
||||
bool skip=(p_item==root && hide_root);
|
||||
|
||||
|
||||
if (!skip && (p_pos.y+label_h-cache.offset.y)>0) {
|
||||
@ -1711,8 +1711,15 @@ int Tree::propagate_mouse_event(const Point2i &p_pos,int x_ofs,int y_ofs,bool p_
|
||||
case TreeItem::CELL_MODE_CHECK: {
|
||||
|
||||
bring_up_editor=false; //checkboxes are not edited with editor
|
||||
p_item->set_checked(col, !c.checked);
|
||||
item_edited(col, p_item);
|
||||
if (force_edit_checkbox_only_on_checkbox) {
|
||||
if (x < cache.checked->get_width()) {
|
||||
p_item->set_checked(col, !c.checked);
|
||||
item_edited(col, p_item);
|
||||
}
|
||||
} else {
|
||||
p_item->set_checked(col, !c.checked);
|
||||
item_edited(col, p_item);
|
||||
}
|
||||
click_handled = true;
|
||||
//p_item->edited_signal.call(col);
|
||||
|
||||
@ -3555,6 +3562,16 @@ bool Tree::get_single_select_cell_editing_only_when_already_selected() const {
|
||||
return force_select_on_already_selected;
|
||||
}
|
||||
|
||||
void Tree::set_edit_checkbox_cell_only_when_checkbox_is_pressed(bool p_enable) {
|
||||
|
||||
force_edit_checkbox_only_on_checkbox=p_enable;
|
||||
}
|
||||
|
||||
bool Tree::get_edit_checkbox_cell_only_when_checkbox_is_pressed() const {
|
||||
|
||||
return force_edit_checkbox_only_on_checkbox;
|
||||
}
|
||||
|
||||
|
||||
void Tree::set_allow_rmb_select(bool p_allow) {
|
||||
|
||||
@ -3733,6 +3750,7 @@ Tree::Tree() {
|
||||
force_select_on_already_selected=false;
|
||||
|
||||
allow_rmb_select=false;
|
||||
force_edit_checkbox_only_on_checkbox=false;
|
||||
|
||||
set_clip_contents(true);
|
||||
}
|
||||
|
@ -452,6 +452,7 @@ friend class TreeItem;
|
||||
bool scrolling;
|
||||
|
||||
bool force_select_on_already_selected;
|
||||
bool force_edit_checkbox_only_on_checkbox;
|
||||
|
||||
bool hide_folding;
|
||||
|
||||
@ -531,6 +532,10 @@ public:
|
||||
void set_single_select_cell_editing_only_when_already_selected(bool p_enable);
|
||||
bool get_single_select_cell_editing_only_when_already_selected() const;
|
||||
|
||||
void set_edit_checkbox_cell_only_when_checkbox_is_pressed(bool p_enable);
|
||||
bool get_edit_checkbox_cell_only_when_checkbox_is_pressed() const;
|
||||
|
||||
|
||||
void set_allow_rmb_select(bool p_allow);
|
||||
bool get_allow_rmb_select() const;
|
||||
|
||||
|
@ -28,6 +28,7 @@
|
||||
/*************************************************************************/
|
||||
#include "video_player.h"
|
||||
#include "os/os.h"
|
||||
#include "servers/audio_server.h"
|
||||
/*
|
||||
|
||||
int VideoPlayer::InternalStream::get_channel_count() const {
|
||||
|
@ -1901,9 +1901,28 @@ void Viewport::_gui_input_event(InputEvent p_event) {
|
||||
}*/
|
||||
#endif
|
||||
|
||||
if (gui.mouse_focus->get_focus_mode()!=Control::FOCUS_NONE && gui.mouse_focus!=gui.key_focus && p_event.mouse_button.button_index==BUTTON_LEFT) {
|
||||
// also get keyboard focus
|
||||
gui.mouse_focus->grab_focus();
|
||||
if (p_event.mouse_button.button_index==BUTTON_LEFT) { //assign focus
|
||||
CanvasItem *ci=gui.mouse_focus;
|
||||
while(ci) {
|
||||
|
||||
Control *control = ci->cast_to<Control>();
|
||||
if (control) {
|
||||
if (control->get_focus_mode()!=Control::FOCUS_NONE) {
|
||||
if (control!=gui.key_focus) {
|
||||
control->grab_focus();
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (control->data.mouse_filter==Control::MOUSE_FILTER_STOP)
|
||||
break;
|
||||
}
|
||||
|
||||
if (ci->is_set_as_toplevel())
|
||||
break;
|
||||
|
||||
ci=ci->get_parent_item();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -139,7 +139,7 @@
|
||||
|
||||
#include "scene/main/timer.h"
|
||||
|
||||
//#include "scene/audio/stream_player.h"
|
||||
#include "scene/audio/audio_player.h"
|
||||
//#include "scene/audio/event_player.h"
|
||||
//#include "scene/audio/sound_room_params.h"
|
||||
#include "scene/resources/sphere_shape.h"
|
||||
@ -177,7 +177,7 @@
|
||||
#include "scene/resources/world_2d.h"
|
||||
|
||||
//#include "scene/resources/sample_library.h"
|
||||
#include "scene/resources/audio_stream.h"
|
||||
//#include "scene/resources/audio_stream.h"
|
||||
#include "scene/resources/gibberish_stream.h"
|
||||
#include "scene/resources/bit_mask.h"
|
||||
#include "scene/resources/color_ramp.h"
|
||||
@ -592,10 +592,7 @@ void register_scene_types() {
|
||||
|
||||
OS::get_singleton()->yield(); //may take time to init
|
||||
|
||||
ClassDB::register_virtual_class<AudioStream>();
|
||||
ClassDB::register_virtual_class<AudioStreamPlayback>();
|
||||
//TODO: Adapt to the new AudioStream API or drop (GH-3307)
|
||||
//ClassDB::register_type<AudioStreamGibberish>();
|
||||
ClassDB::register_class<AudioPlayer>();
|
||||
ClassDB::register_virtual_class<VideoStream>();
|
||||
|
||||
OS::get_singleton()->yield(); //may take time to init
|
||||
|
@ -29,7 +29,7 @@
|
||||
#ifndef AUDIO_STREAM_RESAMPLED_H
|
||||
#define AUDIO_STREAM_RESAMPLED_H
|
||||
|
||||
#include "scene/resources/audio_stream.h"
|
||||
//#include "scene/resources/audio_stream.h"
|
||||
|
||||
#if 0
|
||||
|
||||
|
@ -45,7 +45,7 @@ static const unsigned char button_normal_png[]={
|
||||
|
||||
|
||||
static const unsigned char button_pressed_png[]={
|
||||
0x89,0x50,0x4e,0x47,0xd,0xa,0x1a,0xa,0x0,0x0,0x0,0xd,0x49,0x48,0x44,0x52,0x0,0x0,0x0,0x10,0x0,0x0,0x0,0x10,0x8,0x3,0x0,0x0,0x0,0x28,0x2d,0xf,0x53,0x0,0x0,0x0,0x4,0x67,0x41,0x4d,0x41,0x0,0x0,0xb1,0x8f,0xb,0xfc,0x61,0x5,0x0,0x0,0x0,0x20,0x63,0x48,0x52,0x4d,0x0,0x0,0x7a,0x26,0x0,0x0,0x80,0x84,0x0,0x0,0xfa,0x0,0x0,0x0,0x80,0xe8,0x0,0x0,0x75,0x30,0x0,0x0,0xea,0x60,0x0,0x0,0x3a,0x98,0x0,0x0,0x17,0x70,0x9c,0xba,0x51,0x3c,0x0,0x0,0x0,0x93,0x50,0x4c,0x54,0x45,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x31,0x2f,0x37,0x46,0x43,0x4f,0x47,0x44,0x50,0x55,0x52,0x5f,0x55,0x52,0x60,0x3d,0x3a,0x45,0x56,0x52,0x60,0x56,0x52,0x60,0x43,0x40,0x4c,0x42,0x40,0x4b,0x3a,0x38,0x41,0x36,0x34,0x3d,0x44,0x42,0x4e,0x36,0x34,0x3e,0x46,0x42,0x4f,0x38,0x35,0x3f,0x47,0x45,0x50,0x39,0x37,0x40,0x49,0x46,0x53,0x3a,0x38,0x42,0x4a,0x47,0x54,0x3b,0x39,0x43,0x4b,0x49,0x55,0x3c,0x3a,0x44,0x4e,0x4a,0x58,0x3e,0x3b,0x46,0x50,0x4d,0x5a,0x3f,0x3d,0x48,0x3f,0x3d,0x47,0x45,0x42,0x4d,0x41,0x3e,0x49,0x40,0x3e,0x48,0x52,0x4e,0x5c,0x51,0x4e,0x5b,0xff,0xff,0xff,0x32,0xd2,0xb4,0xc,0x0,0x0,0x0,0x16,0x74,0x52,0x4e,0x53,0x4,0xa,0x11,0x19,0x1f,0x22,0x24,0x15,0x25,0x34,0x3f,0x46,0x47,0x48,0x77,0xef,0xef,0xef,0xef,0x77,0xef,0xed,0x6b,0x28,0x52,0x7a,0x0,0x0,0x0,0x1,0x62,0x4b,0x47,0x44,0x30,0xae,0xdc,0x2d,0xe4,0x0,0x0,0x0,0x9,0x70,0x48,0x59,0x73,0x0,0x0,0xb,0x13,0x0,0x0,0xb,0x13,0x1,0x0,0x9a,0x9c,0x18,0x0,0x0,0x0,0x7,0x74,0x49,0x4d,0x45,0x7,0xe0,0x6,0x16,0x12,0x2b,0x5,0x39,0x1a,0x32,0x39,0x0,0x0,0x0,0x95,0x49,0x44,0x41,0x54,0x18,0xd3,0x65,0xcf,0xdb,0x12,0x42,0x50,0x14,0x6,0xe0,0xb5,0x8f,0xf6,0x11,0xa5,0x24,0x9,0x49,0x22,0xd2,0xfb,0xbf,0x5d,0x9b,0x31,0xfb,0xa2,0xbe,0xcb,0x7f,0x66,0x1d,0x7e,0x0,0x84,0x9,0x65,0xdc,0x61,0x94,0x60,0x4,0x80,0x2,0x21,0x95,0x36,0xd6,0x1a,0xad,0xa4,0x8,0x10,0x60,0x11,0x46,0xe9,0x69,0x95,0x46,0xa1,0xc0,0x40,0x64,0x9c,0x9d,0x37,0x59,0x2c,0x9,0x50,0x95,0x5f,0xbc,0x5c,0x51,0x60,0xba,0xb8,0x7a,0x85,0x66,0xc0,0x4d,0x59,0x79,0xa5,0xe1,0xc0,0x6d,0x7d,0xf3,0x6a,0xbb,0x4,0xcd,0xdd,0x6b,0x96,0xc0,0xb4,0xf,0xaf,0x75,0x23,0x4c,0x77,0x4f,0xaf,0x73,0x4b,0xa9,0xea,0x87,0xd7,0x66,0xe8,0xdd,0x59,0x22,0x77,0xe3,0xf4,0x5e,0x4d,0xe3,0xde,0x3d,0x86,0x45,0x72,0x98,0x3f,0xab,0xf9,0x98,0xb8,0xd7,0xff,0xca,0xfd,0xd6,0xff,0x2,0x86,0xd,0x15,0x51,0x39,0x7d,0xa8,0x8e,0x0,0x0,0x0,0x25,0x74,0x45,0x58,0x74,0x64,0x61,0x74,0x65,0x3a,0x63,0x72,0x65,0x61,0x74,0x65,0x0,0x32,0x30,0x31,0x36,0x2d,0x30,0x36,0x2d,0x32,0x32,0x54,0x32,0x30,0x3a,0x33,0x39,0x3a,0x32,0x36,0x2b,0x30,0x32,0x3a,0x30,0x30,0xc9,0xad,0xc8,0x52,0x0,0x0,0x0,0x25,0x74,0x45,0x58,0x74,0x64,0x61,0x74,0x65,0x3a,0x6d,0x6f,0x64,0x69,0x66,0x79,0x0,0x32,0x30,0x31,0x36,0x2d,0x30,0x36,0x2d,0x32,0x32,0x54,0x32,0x30,0x3a,0x33,0x39,0x3a,0x32,0x36,0x2b,0x30,0x32,0x3a,0x30,0x30,0xb8,0xf0,0x70,0xee,0x0,0x0,0x0,0x0,0x49,0x45,0x4e,0x44,0xae,0x42,0x60,0x82
|
||||
0x89,0x50,0x4e,0x47,0xd,0xa,0x1a,0xa,0x0,0x0,0x0,0xd,0x49,0x48,0x44,0x52,0x0,0x0,0x0,0x10,0x0,0x0,0x0,0x10,0x8,0x6,0x0,0x0,0x0,0x1f,0xf3,0xff,0x61,0x0,0x0,0x0,0x9,0x70,0x48,0x59,0x73,0x0,0x0,0xb,0x13,0x0,0x0,0xb,0x13,0x1,0x0,0x9a,0x9c,0x18,0x0,0x0,0x0,0x7,0x74,0x49,0x4d,0x45,0x7,0xe0,0x7,0x1c,0xc,0x14,0x2b,0xf9,0x77,0x52,0x64,0x0,0x0,0x1,0x92,0x49,0x44,0x41,0x54,0x38,0xcb,0x8d,0x93,0x4d,0x4e,0x1b,0x41,0x10,0x85,0xbf,0xea,0xaa,0x1e,0x20,0x83,0xf9,0x9,0x36,0xb2,0x85,0x72,0x84,0x8,0x65,0x11,0xe5,0xc,0x9c,0x80,0x73,0x10,0xee,0xc1,0x41,0x38,0x1,0xab,0xec,0xa3,0x2c,0x2,0x8a,0xe0,0x2,0x51,0xc2,0x48,0x46,0x42,0x64,0xc6,0x91,0x21,0xf4,0x74,0x16,0x2e,0xb0,0x71,0x2,0xf2,0x93,0x9e,0xba,0x16,0xdd,0xaf,0x5e,0xa9,0x5f,0x9,0x60,0x40,0x1,0x2c,0x1,0xcb,0x5e,0x2b,0x10,0x78,0x8a,0x16,0x48,0xc0,0x1d,0x30,0x6,0x6e,0x81,0x3b,0xf3,0x87,0x25,0xb0,0x9,0xac,0x7b,0xbd,0xc,0x88,0x13,0x20,0x3b,0xc7,0xc0,0x8,0xb8,0x1,0xae,0x81,0x91,0xf9,0xe5,0xad,0x77,0xbb,0x1f,0xf6,0x7b,0xdd,0xfe,0x41,0x4a,0x69,0x2d,0x93,0xf9,0x1f,0x4,0x41,0x55,0x7f,0xd,0xaf,0xaa,0xa3,0xaf,0x67,0x9f,0x8f,0x81,0x64,0xde,0xb1,0xbb,0xdd,0x1b,0x7c,0xac,0x9b,0x9b,0xce,0xd9,0xb7,0x2f,0xfc,0xf3,0x5e,0xa6,0xe5,0xee,0xdb,0xf7,0x6b,0xdb,0xbd,0xc1,0x21,0xf0,0x9,0x18,0x5,0x60,0x5,0xd8,0x48,0x6d,0xdb,0x39,0xbf,0x38,0xc5,0x34,0x62,0x36,0x47,0x9d,0xf2,0xfc,0xe2,0x94,0xd4,0xb6,0x1d,0x60,0x3,0x58,0x31,0x20,0x2,0x65,0x40,0x88,0x31,0x92,0xbd,0xbb,0x8,0xe4,0x3c,0x39,0x67,0x91,0x33,0x84,0x89,0xa5,0x12,0x88,0xf,0x3f,0x10,0x45,0x5,0xb3,0xc8,0x73,0x10,0x11,0xb2,0xab,0x8b,0xa,0xde,0xb8,0x30,0x9f,0xb0,0x8,0xa2,0xc4,0x58,0xb0,0x8,0x82,0x28,0xde,0x58,0xcc,0xff,0x5c,0x45,0x64,0x61,0x1,0x99,0xcc,0xa5,0x80,0x4e,0x5,0xc2,0xcb,0x2,0xe2,0x41,0x10,0x40,0xc2,0x53,0x81,0xc,0xa8,0x8a,0x52,0x2c,0xe8,0x40,0x27,0x23,0x28,0x90,0xcd,0xe3,0x79,0x1b,0x34,0x50,0x14,0x4b,0xf0,0x4c,0x88,0x66,0xbd,0x4,0xd,0x78,0x94,0x93,0x79,0x3c,0x9b,0x18,0x63,0x7a,0xbd,0xb9,0xa5,0xcd,0xa8,0x7e,0xb4,0x3a,0x2f,0x15,0x10,0xca,0x72,0x95,0x18,0x8b,0x4,0x34,0xc0,0xd8,0x80,0x1a,0x18,0x56,0xd5,0xcf,0x93,0x41,0x7f,0x67,0xaf,0xb3,0xba,0x1e,0x5e,0x8a,0xb2,0x99,0xb5,0x97,0xd5,0x8f,0x13,0x60,0x8,0xd4,0x2,0x74,0x9d,0x6f,0x80,0xbe,0x2f,0x55,0xe9,0x26,0xc2,0xcc,0x26,0x66,0x5f,0xa4,0x6b,0xa0,0x2,0xbe,0x3,0x57,0xe6,0x56,0x1e,0x66,0x1a,0x2,0xaf,0x3c,0x24,0x36,0x67,0xe0,0x1e,0xf8,0x3,0xfc,0xf6,0x6d,0xac,0x81,0xe6,0x2f,0x7c,0x22,0x6d,0x74,0x25,0xb,0xb3,0xa2,0x0,0x0,0x0,0x0,0x49,0x45,0x4e,0x44,0xae,0x42,0x60,0x82
|
||||
};
|
||||
|
||||
|
||||
@ -540,12 +540,12 @@ static const unsigned char vslider_bg_png[]={
|
||||
|
||||
|
||||
static const unsigned char vslider_grabber_png[]={
|
||||
0x89,0x50,0x4e,0x47,0xd,0xa,0x1a,0xa,0x0,0x0,0x0,0xd,0x49,0x48,0x44,0x52,0x0,0x0,0x0,0x10,0x0,0x0,0x0,0x10,0x8,0x4,0x0,0x0,0x0,0xb5,0xfa,0x37,0xea,0x0,0x0,0x0,0x4,0x67,0x41,0x4d,0x41,0x0,0x0,0xb1,0x8f,0xb,0xfc,0x61,0x5,0x0,0x0,0x0,0x20,0x63,0x48,0x52,0x4d,0x0,0x0,0x7a,0x26,0x0,0x0,0x80,0x84,0x0,0x0,0xfa,0x0,0x0,0x0,0x80,0xe8,0x0,0x0,0x75,0x30,0x0,0x0,0xea,0x60,0x0,0x0,0x3a,0x98,0x0,0x0,0x17,0x70,0x9c,0xba,0x51,0x3c,0x0,0x0,0x0,0x2,0x62,0x4b,0x47,0x44,0x0,0xff,0x87,0x8f,0xcc,0xbf,0x0,0x0,0x0,0x9,0x70,0x48,0x59,0x73,0x0,0x0,0xb,0x13,0x0,0x0,0xb,0x13,0x1,0x0,0x9a,0x9c,0x18,0x0,0x0,0x0,0x7,0x74,0x49,0x4d,0x45,0x7,0xe0,0x6,0x16,0x12,0x2b,0x5,0x39,0x1a,0x32,0x39,0x0,0x0,0x0,0xf8,0x49,0x44,0x41,0x54,0x28,0xcf,0xbd,0x90,0xaf,0x4b,0x43,0x51,0x18,0x86,0x9f,0xf3,0x43,0xcf,0x76,0xae,0xde,0xb9,0xc9,0x1c,0x82,0x86,0xa1,0x16,0x15,0x87,0xc3,0x20,0x68,0x30,0x58,0xb4,0x5a,0x6c,0x62,0x37,0x88,0xff,0x81,0xc5,0xe4,0x5f,0x70,0x8b,0x75,0x45,0xb3,0xc1,0xa6,0x41,0x4,0x19,0xc,0x19,0x18,0x4,0x8d,0xb,0x82,0xdb,0xd8,0x81,0x29,0xf7,0x1e,0x8b,0x6e,0x43,0x30,0xea,0x93,0x5e,0xf8,0x5e,0x5e,0x78,0x3e,0xf8,0x73,0xc4,0x40,0x92,0x18,0x46,0x9,0x18,0xc6,0xd3,0xa1,0x89,0x23,0xee,0x17,0x86,0xc8,0x14,0xa7,0x97,0x57,0x4a,0xdb,0xe1,0xa2,0xc9,0x4b,0xdf,0xac,0x3d,0x9d,0x47,0x15,0x5e,0x89,0x5,0x20,0xb0,0x23,0xc5,0x83,0xc3,0xc2,0xa6,0x99,0xea,0xaa,0xf,0x62,0xc0,0xa0,0x1b,0xf,0x27,0xd1,0x19,0x6d,0x8d,0x66,0x6c,0x75,0x6d,0xf7,0x54,0xcd,0x3a,0x1c,0xc9,0xd7,0x60,0x8c,0x2d,0xcc,0xec,0x70,0x41,0x5b,0x63,0x8e,0xf6,0xe6,0x8f,0xdf,0x82,0x4e,0xef,0x8,0xe0,0xe9,0x92,0x5d,0x22,0x0,0x89,0x48,0x59,0xd4,0xef,0x16,0x8a,0xe4,0xba,0xde,0xaa,0x2e,0x94,0x53,0xb9,0x4,0x8f,0xef,0x29,0xa5,0x71,0x77,0x57,0x15,0x5a,0x8a,0x4,0xf7,0xfc,0x72,0x73,0x39,0xc7,0xf8,0x44,0x90,0x11,0x2,0x24,0x92,0x34,0xba,0xf1,0x18,0xdd,0xdf,0xf2,0xde,0xd7,0xc,0x75,0x7e,0x6b,0x7d,0x63,0x5f,0x4c,0x9a,0x9c,0xfa,0xa1,0xf9,0x8d,0xc4,0x12,0x62,0xd1,0x83,0x8f,0xfa,0x7,0x3e,0x1,0x87,0xd0,0x4a,0x12,0xcf,0xee,0xfb,0xd8,0x0,0x0,0x0,0x19,0x74,0x45,0x58,0x74,0x43,0x6f,0x6d,0x6d,0x65,0x6e,0x74,0x0,0x43,0x72,0x65,0x61,0x74,0x65,0x64,0x20,0x77,0x69,0x74,0x68,0x20,0x47,0x49,0x4d,0x50,0x57,0x81,0xe,0x17,0x0,0x0,0x0,0x25,0x74,0x45,0x58,0x74,0x64,0x61,0x74,0x65,0x3a,0x63,0x72,0x65,0x61,0x74,0x65,0x0,0x32,0x30,0x31,0x36,0x2d,0x30,0x36,0x2d,0x32,0x32,0x54,0x32,0x30,0x3a,0x33,0x39,0x3a,0x32,0x36,0x2b,0x30,0x32,0x3a,0x30,0x30,0xc9,0xad,0xc8,0x52,0x0,0x0,0x0,0x25,0x74,0x45,0x58,0x74,0x64,0x61,0x74,0x65,0x3a,0x6d,0x6f,0x64,0x69,0x66,0x79,0x0,0x32,0x30,0x31,0x36,0x2d,0x30,0x36,0x2d,0x32,0x32,0x54,0x32,0x30,0x3a,0x33,0x39,0x3a,0x32,0x36,0x2b,0x30,0x32,0x3a,0x30,0x30,0xb8,0xf0,0x70,0xee,0x0,0x0,0x0,0x0,0x49,0x45,0x4e,0x44,0xae,0x42,0x60,0x82
|
||||
0x89,0x50,0x4e,0x47,0xd,0xa,0x1a,0xa,0x0,0x0,0x0,0xd,0x49,0x48,0x44,0x52,0x0,0x0,0x0,0x10,0x0,0x0,0x0,0x10,0x8,0x4,0x0,0x0,0x0,0xb5,0xfa,0x37,0xea,0x0,0x0,0x0,0x2,0x62,0x4b,0x47,0x44,0x0,0xb7,0xff,0x88,0x5,0x1d,0x0,0x0,0x0,0x9,0x70,0x48,0x59,0x73,0x0,0x0,0xb,0x13,0x0,0x0,0xb,0x13,0x1,0x0,0x9a,0x9c,0x18,0x0,0x0,0x0,0x7,0x74,0x49,0x4d,0x45,0x7,0xe1,0x1,0x12,0x1,0x36,0x8,0x50,0xb9,0xa7,0x53,0x0,0x0,0x0,0x19,0x74,0x45,0x58,0x74,0x43,0x6f,0x6d,0x6d,0x65,0x6e,0x74,0x0,0x43,0x72,0x65,0x61,0x74,0x65,0x64,0x20,0x77,0x69,0x74,0x68,0x20,0x47,0x49,0x4d,0x50,0x57,0x81,0xe,0x17,0x0,0x0,0x0,0xf6,0x49,0x44,0x41,0x54,0x28,0xcf,0xbd,0x90,0xb1,0x4a,0x42,0x51,0x0,0x86,0xbf,0x73,0x8e,0x71,0xe5,0x9a,0x5c,0x41,0xd0,0x66,0x6b,0x33,0x1c,0x7c,0x80,0xa0,0xa5,0x17,0x8,0xa2,0x2d,0x84,0xf0,0x1,0xa2,0x25,0xf1,0x9,0x9a,0x1c,0xda,0x5b,0xb2,0x47,0xa8,0xa5,0xc1,0xa0,0x51,0x88,0xa2,0x29,0xa,0xc1,0x84,0x8,0x43,0xf4,0x96,0x17,0xcf,0xed,0xde,0x73,0x9c,0xcc,0x5c,0xda,0xea,0x9f,0x3f,0xfe,0x9f,0xef,0x87,0x3f,0x8f,0x0,0x40,0xe1,0xe2,0x91,0x42,0x10,0x32,0xe6,0x3,0x8d,0xc1,0xce,0x1,0x45,0xb6,0xba,0xbb,0xba,0xed,0x95,0x8c,0xd0,0x7d,0xff,0xe1,0xee,0xe2,0xb6,0xdd,0x79,0x61,0xc4,0xd7,0xc,0x48,0x57,0x2b,0xeb,0xb5,0x28,0xaf,0x1,0xc5,0x12,0x4e,0xac,0x7b,0x6f,0x57,0x27,0x8d,0xcf,0xe,0x1,0x56,0x1,0xb9,0x9d,0xba,0x28,0x6,0x18,0xc,0x31,0x21,0x5a,0xda,0x4c,0xb6,0xbc,0xb9,0x35,0x7c,0xea,0xbd,0x13,0x4a,0x20,0xe5,0x95,0xf4,0x6c,0x12,0x30,0x84,0xf8,0x44,0x6b,0xfb,0xcd,0x83,0x3d,0x1c,0xf9,0x8b,0x80,0x4a,0xba,0x88,0x4,0x30,0x1e,0xdd,0x3b,0x1b,0xf1,0x77,0x87,0x24,0x81,0x8b,0x79,0x3e,0x3b,0x6a,0x5d,0x33,0x51,0x80,0x2d,0x38,0x2b,0x65,0xb5,0x6c,0x91,0x28,0x92,0xa4,0xad,0xec,0x76,0xcf,0x8f,0xf,0x1f,0xdb,0xc,0x31,0xb,0x9a,0xb1,0xd0,0x3,0xfb,0xda,0x3a,0xbd,0xbc,0x89,0xfa,0xf8,0x73,0xcd,0x9f,0x47,0x45,0x4,0xf8,0x4,0x18,0xfe,0x2f,0x53,0x8,0x62,0x5c,0xcf,0x1f,0x5f,0xcb,0x2c,0x0,0x0,0x0,0x0,0x49,0x45,0x4e,0x44,0xae,0x42,0x60,0x82
|
||||
};
|
||||
|
||||
|
||||
static const unsigned char vslider_grabber_hl_png[]={
|
||||
0x89,0x50,0x4e,0x47,0xd,0xa,0x1a,0xa,0x0,0x0,0x0,0xd,0x49,0x48,0x44,0x52,0x0,0x0,0x0,0x10,0x0,0x0,0x0,0x10,0x8,0x3,0x0,0x0,0x0,0x28,0x2d,0xf,0x53,0x0,0x0,0x0,0x4,0x67,0x41,0x4d,0x41,0x0,0x0,0xb1,0x8f,0xb,0xfc,0x61,0x5,0x0,0x0,0x0,0x20,0x63,0x48,0x52,0x4d,0x0,0x0,0x7a,0x26,0x0,0x0,0x80,0x84,0x0,0x0,0xfa,0x0,0x0,0x0,0x80,0xe8,0x0,0x0,0x75,0x30,0x0,0x0,0xea,0x60,0x0,0x0,0x3a,0x98,0x0,0x0,0x17,0x70,0x9c,0xba,0x51,0x3c,0x0,0x0,0x0,0xc6,0x50,0x4c,0x54,0x45,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x17,0x2a,0x29,0x3a,0x69,0x69,0x5b,0xa6,0xa5,0x61,0xb3,0xbc,0x63,0xb7,0xc8,0x65,0xbb,0xca,0x60,0xaf,0xb1,0x48,0x83,0x83,0x0,0x0,0x0,0x0,0x0,0x0,0x8,0xf,0xf,0x55,0x9b,0x9a,0x60,0xb2,0xbd,0x5e,0xb1,0xcd,0x61,0xb3,0xc2,0x0,0x0,0x0,0x27,0x48,0x47,0x62,0xb4,0xbd,0x51,0x93,0x92,0x68,0xc0,0xcf,0x0,0x0,0x0,0x56,0x9d,0x9c,0x68,0xc1,0xcf,0x2d,0x52,0x52,0x63,0xb7,0xbf,0x52,0x96,0x95,0x62,0xb3,0xbf,0x5e,0xb0,0xcd,0x0,0x0,0x0,0x3,0x5,0x5,0x36,0x63,0x63,0x63,0xb4,0xb6,0x60,0xb1,0xbc,0x63,0xb7,0xc7,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x55,0xa3,0xc8,0x4f,0x98,0xc4,0x4b,0x93,0xc2,0x4c,0x94,0xc2,0x54,0xa2,0xc8,0x5a,0xab,0xcb,0x4e,0x97,0xc4,0x49,0x8f,0xc0,0x47,0x8c,0xbf,0x48,0x8e,0xc0,0x52,0x9e,0xc6,0x51,0x9d,0xc6,0x5a,0xac,0xcc,0x53,0x9f,0xc7,0x4d,0x96,0xc3,0x4b,0x92,0xc2,0xff,0xff,0xff,0xec,0x37,0x7,0xf6,0x0,0x0,0x0,0x31,0x74,0x52,0x4e,0x53,0x0,0x1,0x3,0xa,0x17,0x22,0x28,0x27,0x1c,0xd,0x5,0x14,0x31,0x65,0xaf,0xdb,0xf0,0xef,0xc1,0x6e,0x16,0xb,0x2c,0x9c,0xe0,0xfc,0xe8,0x4,0x4f,0xdb,0x73,0xf4,0xc,0x7d,0xf7,0x55,0xdc,0x95,0xe0,0xfe,0x13,0x28,0x64,0xc5,0xde,0xf0,0x2,0x1a,0x24,0x77,0x58,0x79,0x88,0x0,0x0,0x0,0x1,0x62,0x4b,0x47,0x44,0x41,0x89,0xde,0x6c,0x4e,0x0,0x0,0x0,0x9,0x70,0x48,0x59,0x73,0x0,0x0,0xb,0x13,0x0,0x0,0xb,0x13,0x1,0x0,0x9a,0x9c,0x18,0x0,0x0,0x0,0x7,0x74,0x49,0x4d,0x45,0x7,0xe0,0x6,0x16,0x12,0x2b,0x5,0x39,0x1a,0x32,0x39,0x0,0x0,0x0,0x7d,0x49,0x44,0x41,0x54,0x18,0xd3,0x63,0x60,0x20,0xf,0x30,0x32,0x31,0xb3,0xb0,0xb2,0xb1,0x73,0x70,0x32,0x41,0xf8,0x5c,0xdc,0x3c,0xbc,0x7c,0xfc,0x2,0x82,0x42,0xc2,0x22,0x20,0x11,0x46,0x51,0x31,0x71,0x9,0x49,0x43,0x23,0x63,0x13,0x53,0x29,0x61,0x4e,0x6,0x6,0x69,0x6e,0x19,0x59,0x33,0x73,0xb,0x4b,0x20,0xb0,0x32,0x15,0xe2,0x60,0x60,0x10,0x95,0x93,0xb7,0x6,0x73,0x81,0xc0,0x44,0x90,0x9d,0x81,0x41,0x41,0x51,0xc9,0x6,0x45,0x40,0x5a,0x44,0x59,0xc5,0x16,0x59,0xb,0x3,0xa3,0x82,0x98,0xaa,0x9a,0xba,0x9d,0xbd,0x3,0xd4,0x50,0x90,0xb5,0x1a,0x9a,0x5a,0xda,0x3a,0xba,0x30,0x6b,0x41,0x40,0x4f,0x41,0xdf,0x0,0xc9,0x61,0x24,0x2,0x0,0x9d,0x96,0x10,0xf9,0x6,0xb2,0x58,0x28,0x0,0x0,0x0,0x19,0x74,0x45,0x58,0x74,0x43,0x6f,0x6d,0x6d,0x65,0x6e,0x74,0x0,0x43,0x72,0x65,0x61,0x74,0x65,0x64,0x20,0x77,0x69,0x74,0x68,0x20,0x47,0x49,0x4d,0x50,0x57,0x81,0xe,0x17,0x0,0x0,0x0,0x25,0x74,0x45,0x58,0x74,0x64,0x61,0x74,0x65,0x3a,0x63,0x72,0x65,0x61,0x74,0x65,0x0,0x32,0x30,0x31,0x36,0x2d,0x30,0x36,0x2d,0x32,0x32,0x54,0x32,0x30,0x3a,0x33,0x39,0x3a,0x32,0x36,0x2b,0x30,0x32,0x3a,0x30,0x30,0xc9,0xad,0xc8,0x52,0x0,0x0,0x0,0x25,0x74,0x45,0x58,0x74,0x64,0x61,0x74,0x65,0x3a,0x6d,0x6f,0x64,0x69,0x66,0x79,0x0,0x32,0x30,0x31,0x36,0x2d,0x30,0x36,0x2d,0x32,0x32,0x54,0x32,0x30,0x3a,0x33,0x39,0x3a,0x32,0x36,0x2b,0x30,0x32,0x3a,0x30,0x30,0xb8,0xf0,0x70,0xee,0x0,0x0,0x0,0x0,0x49,0x45,0x4e,0x44,0xae,0x42,0x60,0x82
|
||||
0x89,0x50,0x4e,0x47,0xd,0xa,0x1a,0xa,0x0,0x0,0x0,0xd,0x49,0x48,0x44,0x52,0x0,0x0,0x0,0x10,0x0,0x0,0x0,0x10,0x8,0x3,0x0,0x0,0x0,0x28,0x2d,0xf,0x53,0x0,0x0,0x0,0xc3,0x50,0x4c,0x54,0x45,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x17,0x2a,0x29,0x3a,0x69,0x69,0x5b,0xa6,0xa5,0x61,0xb3,0xbc,0x63,0xb7,0xc8,0x65,0xbb,0xca,0x60,0xaf,0xb1,0x48,0x83,0x83,0x0,0x0,0x0,0x0,0x0,0x0,0x8,0xf,0xf,0x55,0x9b,0x9a,0x60,0xb2,0xbd,0x5e,0xb1,0xcd,0x61,0xb3,0xc2,0x0,0x0,0x0,0x27,0x48,0x47,0x62,0xb4,0xbd,0x51,0x93,0x92,0x68,0xc0,0xcf,0x0,0x0,0x0,0x56,0x9d,0x9c,0x68,0xc1,0xcf,0x2d,0x52,0x52,0x63,0xb7,0xbf,0x52,0x96,0x95,0x62,0xb3,0xbf,0x5e,0xb0,0xcd,0x0,0x0,0x0,0x3,0x5,0x5,0x36,0x63,0x63,0x63,0xb4,0xb6,0x60,0xb1,0xbc,0x63,0xb7,0xc7,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x55,0xa3,0xc8,0x4f,0x98,0xc4,0x4b,0x93,0xc2,0x4c,0x94,0xc2,0x54,0xa2,0xc8,0x5a,0xab,0xcb,0x4e,0x97,0xc4,0x49,0x8f,0xc0,0x47,0x8c,0xbf,0x48,0x8e,0xc0,0x52,0x9e,0xc6,0x51,0x9d,0xc6,0x5a,0xac,0xcc,0x53,0x9f,0xc7,0x4d,0x96,0xc3,0x4b,0x92,0xc2,0xff,0xff,0xff,0x76,0xbd,0x27,0x7a,0x0,0x0,0x0,0x1,0x74,0x52,0x4e,0x53,0x0,0x40,0xe6,0xd8,0x66,0x0,0x0,0x0,0x1,0x62,0x4b,0x47,0x44,0x0,0x88,0x5,0x1d,0x48,0x0,0x0,0x0,0x9,0x70,0x48,0x59,0x73,0x0,0x0,0xb,0x13,0x0,0x0,0xb,0x13,0x1,0x0,0x9a,0x9c,0x18,0x0,0x0,0x0,0x7,0x74,0x49,0x4d,0x45,0x7,0xe1,0x1,0x12,0x1,0x36,0x11,0x34,0xd2,0xf,0x93,0x0,0x0,0x0,0x19,0x74,0x45,0x58,0x74,0x43,0x6f,0x6d,0x6d,0x65,0x6e,0x74,0x0,0x43,0x72,0x65,0x61,0x74,0x65,0x64,0x20,0x77,0x69,0x74,0x68,0x20,0x47,0x49,0x4d,0x50,0x57,0x81,0xe,0x17,0x0,0x0,0x0,0x48,0x49,0x44,0x41,0x54,0x18,0xd3,0x63,0x60,0xa0,0x12,0x10,0x14,0xe0,0xe7,0xe3,0x45,0xe2,0x4b,0x9a,0x18,0x1b,0x19,0x1a,0x48,0x88,0x8b,0xc1,0xe4,0x4d,0x2c,0x2d,0x80,0xc0,0xdc,0xcc,0x54,0x6,0x22,0x20,0x60,0x6c,0x1,0x1,0xe6,0x56,0x72,0x68,0x2,0xd6,0x8a,0xa8,0x5a,0x6c,0x94,0x11,0x86,0xda,0xdb,0xd9,0xaa,0xa9,0xaa,0x20,0x59,0xab,0xa3,0xad,0xc5,0x40,0x3d,0x0,0x0,0xbf,0x8e,0xc,0xed,0xed,0xc7,0x67,0x72,0x0,0x0,0x0,0x0,0x49,0x45,0x4e,0x44,0xae,0x42,0x60,0x82
|
||||
};
|
||||
|
||||
|
||||
|
Before Width: | Height: | Size: 554 B After Width: | Height: | Size: 394 B |
Before Width: | Height: | Size: 701 B After Width: | Height: | Size: 439 B |
@ -5,3 +5,5 @@ Import('env')
|
||||
env.add_source_files(env.servers_sources, "*.cpp")
|
||||
|
||||
Export('env')
|
||||
|
||||
SConscript("effects/SCsub")
|
||||
|
@ -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;
|
||||
|
||||
};
|
||||
|
||||
|
@ -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();
|
||||
};
|
||||
|
86
servers/audio/audio_stream.cpp
Normal file
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
@ -34,47 +34,65 @@
|
||||
|
||||
class AudioStreamPlayback : public Reference {
|
||||
|
||||
GDCLASS( AudioStreamPlayback, Reference );
|
||||
protected:
|
||||
static void _bind_methods();
|
||||
GDCLASS( AudioStreamPlayback, Reference )
|
||||
|
||||
public:
|
||||
|
||||
|
||||
virtual void play(float p_from_pos=0)=0;
|
||||
virtual void start(float p_from_pos=0.0)=0;
|
||||
virtual void stop()=0;
|
||||
virtual bool is_playing() const=0;
|
||||
|
||||
virtual void set_loop(bool p_enable)=0;
|
||||
virtual bool has_loop() const=0;
|
||||
|
||||
virtual void set_loop_restart_time(float p_time)=0;
|
||||
|
||||
virtual int get_loop_count() 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 int mix(int16_t* p_bufer,int p_frames)=0;
|
||||
virtual void mix(AudioFrame* p_bufer,float p_rate_scale,int p_frames)=0;
|
||||
|
||||
virtual float get_length() const=0;
|
||||
virtual String get_stream_name() const=0;
|
||||
virtual float get_length() const=0; //if supported, otherwise return 0
|
||||
|
||||
virtual int get_channels() const=0;
|
||||
virtual int get_mix_rate() const=0;
|
||||
virtual int get_minimum_buffer_size() const=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
|
||||
GDCLASS( AudioStream, Resource )
|
||||
OBJ_SAVE_TYPE( AudioStream ) //children are all saved as AudioStream, so they can be exchanged
|
||||
|
||||
|
||||
protected:
|
||||
static void _bind_methods();
|
||||
public:
|
||||
|
||||
virtual Ref<AudioStreamPlayback> instance_playback()=0;
|
||||
virtual String get_stream_name() const=0;
|
||||
|
||||
|
||||
};
|
7
servers/audio/effects/SCsub
Normal file
@ -0,0 +1,7 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
Import('env')
|
||||
|
||||
env.add_source_files(env.servers_sources, "*.cpp")
|
||||
|
||||
Export('env')
|
50
servers/audio/effects/audio_effect_amplify.cpp
Normal file
@ -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;
|
||||
}
|
40
servers/audio/effects/audio_effect_amplify.h
Normal file
@ -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
|
122
servers/audio/effects/audio_effect_eq.cpp
Normal file
@ -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);
|
||||
}
|
||||
}
|
72
servers/audio/effects/audio_effect_eq.h
Normal file
@ -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
|
151
servers/audio/effects/audio_effect_filter.cpp
Normal file
@ -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;
|
||||
}
|
125
servers/audio/effects/audio_effect_filter.h
Normal file
@ -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
|
182
servers/audio/effects/audio_effect_reverb.cpp
Normal file
@ -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;
|
||||
|
||||
}
|
76
servers/audio/effects/audio_effect_reverb.h
Normal file
@ -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
|
218
servers/audio/effects/eq.cpp
Normal file
@ -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()
|
||||
{
|
||||
}
|
||||
|
||||
|
106
servers/audio/effects/eq.h
Normal file
@ -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
|
363
servers/audio/effects/reverb.cpp
Normal file
@ -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();
|
||||
}
|
||||
|
||||
|
111
servers/audio/effects/reverb.h
Normal file
@ -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
|
@ -42,12 +42,16 @@ void AudioDriver::set_singleton() {
|
||||
|
||||
void AudioDriver::audio_server_process(int p_frames,int32_t *p_buffer,bool p_update_mix_time) {
|
||||
|
||||
AudioServer * audio_server = static_cast<AudioServer*>(AudioServer::get_singleton());
|
||||
|
||||
if (p_update_mix_time)
|
||||
update_mix_time(p_frames);
|
||||
// audio_server->driver_process(p_frames,p_buffer);
|
||||
|
||||
if (AudioServer::get_singleton())
|
||||
AudioServer::get_singleton()->_driver_process(p_frames,p_buffer);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void AudioDriver::update_mix_time(int p_frames) {
|
||||
|
||||
_mix_amount+=p_frames;
|
||||
@ -74,7 +78,6 @@ AudioDriver *AudioDriverManager::drivers[MAX_DRIVERS];
|
||||
int AudioDriverManager::driver_count=0;
|
||||
|
||||
|
||||
|
||||
void AudioDriverManager::add_driver(AudioDriver *p_driver) {
|
||||
|
||||
ERR_FAIL_COND(driver_count>=MAX_DRIVERS);
|
||||
@ -97,13 +100,286 @@ AudioDriver *AudioDriverManager::get_driver(int p_driver) {
|
||||
//////////////////////////////////////////////
|
||||
//////////////////////////////////////////////
|
||||
|
||||
void AudioServer::_driver_process(int p_frames,int32_t* p_buffer) {
|
||||
|
||||
int todo=p_frames;
|
||||
|
||||
while(todo) {
|
||||
|
||||
if (to_mix==0) {
|
||||
_mix_step();
|
||||
}
|
||||
|
||||
int to_copy = MIN(to_mix,todo);
|
||||
|
||||
Bus *master = buses[0];
|
||||
|
||||
int from = buffer_size-to_mix;
|
||||
int from_buf=p_frames-todo;
|
||||
|
||||
//master master, send to output
|
||||
int cs = master->channels.size();
|
||||
for(int k=0;k<cs;k++) {
|
||||
|
||||
if (master->channels[k].active) {
|
||||
|
||||
AudioFrame *buf = master->channels[k].buffer.ptr();
|
||||
|
||||
for(int j=0;j<to_copy;j++) {
|
||||
|
||||
float l = CLAMP(buf[from+j].l,-1.0,1.0);
|
||||
int32_t vl = l*((1<<20)-1);
|
||||
p_buffer[(from_buf+j)*(cs*2)+k*2+0]=vl<<11;
|
||||
|
||||
float r = CLAMP(buf[from+j].r,-1.0,1.0);
|
||||
int32_t vr = r*((1<<20)-1);
|
||||
p_buffer[(from_buf+j)*(cs*2)+k*2+1]=vr<<11;
|
||||
}
|
||||
|
||||
} else {
|
||||
for(int j=0;j<to_copy;j++) {
|
||||
|
||||
p_buffer[(from_buf+j)*(cs*2)+k*2+0]=0;
|
||||
p_buffer[(from_buf+j)*(cs*2)+k*2+1]=0;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
todo-=to_copy;
|
||||
to_mix-=to_copy;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void AudioServer::_mix_step() {
|
||||
|
||||
for(int i=0;i<buses.size();i++) {
|
||||
Bus *bus = buses[i];
|
||||
bus->index_cache=i; //might be moved around by editor, so..
|
||||
for(int k=0;k<bus->channels.size();k++) {
|
||||
|
||||
bus->channels[k].used=false;
|
||||
}
|
||||
}
|
||||
|
||||
//make callbacks for mixing the audio
|
||||
for (Set<CallbackItem>::Element *E=callbacks.front();E;E=E->next()) {
|
||||
|
||||
E->get().callback(E->get().userdata);
|
||||
}
|
||||
|
||||
for(int i=buses.size()-1;i>=0;i--) {
|
||||
//go bus by bus
|
||||
Bus *bus = buses[i];
|
||||
|
||||
|
||||
for(int k=0;k<bus->channels.size();k++) {
|
||||
|
||||
if (bus->channels[k].active && !bus->channels[k].used) {
|
||||
//buffer was not used, but it's still active, so it must be cleaned
|
||||
AudioFrame *buf = bus->channels[k].buffer.ptr();
|
||||
|
||||
for(uint32_t j=0;j<buffer_size;j++) {
|
||||
|
||||
buf[j]=AudioFrame(0,0);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
//process effects
|
||||
for(int j=0;j<bus->effects.size();j++) {
|
||||
|
||||
if (!bus->effects[j].enabled)
|
||||
continue;
|
||||
|
||||
for(int k=0;k<bus->channels.size();k++) {
|
||||
|
||||
if (!bus->channels[k].active)
|
||||
continue;
|
||||
bus->channels[k].effect_instances[j]->process(bus->channels[k].buffer.ptr(),temp_buffer[k].ptr(),buffer_size);
|
||||
}
|
||||
|
||||
//swap buffers, so internal buffer always has the right data
|
||||
for(int k=0;k<bus->channels.size();k++) {
|
||||
|
||||
if (!buses[i]->channels[k].active)
|
||||
continue;
|
||||
SWAP(bus->channels[k].buffer,temp_buffer[k]);
|
||||
}
|
||||
}
|
||||
|
||||
//process send
|
||||
|
||||
Bus *send=NULL;
|
||||
|
||||
if (i>0) {
|
||||
//everything has a send save for master bus
|
||||
if (!bus_map.has(bus->send)) {
|
||||
send=buses[0];
|
||||
} else {
|
||||
send=bus_map[bus->send];
|
||||
if (send->index_cache>=bus->index_cache) { //invalid, send to master
|
||||
send=buses[0];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
for(int k=0;k<bus->channels.size();k++) {
|
||||
|
||||
if (!bus->channels[k].active)
|
||||
continue;
|
||||
|
||||
AudioFrame *buf = bus->channels[k].buffer.ptr();
|
||||
|
||||
|
||||
AudioFrame peak = AudioFrame(0,0);
|
||||
for(uint32_t j=0;j<buffer_size;j++) {
|
||||
float l = ABS(buf[j].l);
|
||||
if (l>peak.l) {
|
||||
peak.l=l;
|
||||
}
|
||||
float r = ABS(buf[j].r);
|
||||
if (r>peak.r) {
|
||||
peak.r=r;
|
||||
}
|
||||
}
|
||||
|
||||
bus->channels[k].peak_volume=AudioFrame(Math::linear2db(peak.l+0.0000000001),Math::linear2db(peak.r+0.0000000001));
|
||||
|
||||
|
||||
if (!bus->channels[k].used) {
|
||||
//see if any audio is contained, because channel was not used
|
||||
|
||||
|
||||
if (MAX(peak.r,peak.l) > Math::db2linear(channel_disable_treshold_db)) {
|
||||
bus->channels[k].last_mix_with_audio=mix_frames;
|
||||
} else if (mix_frames-bus->channels[k].last_mix_with_audio > channel_disable_frames ) {
|
||||
bus->channels[k].active=false;
|
||||
continue; //went inactive, dont mix.
|
||||
}
|
||||
}
|
||||
|
||||
if (send) {
|
||||
//if not master bus, send
|
||||
AudioFrame *target_buf = thread_get_channel_mix_buffer(send->index_cache,k);
|
||||
|
||||
for(uint32_t j=0;j<buffer_size;j++) {
|
||||
target_buf[j]+=buf[j];
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
mix_frames+=buffer_size;
|
||||
to_mix=buffer_size;
|
||||
|
||||
}
|
||||
|
||||
AudioFrame *AudioServer::thread_get_channel_mix_buffer(int p_bus,int p_buffer) {
|
||||
|
||||
ERR_FAIL_INDEX_V(p_bus,buses.size(),NULL);
|
||||
ERR_FAIL_INDEX_V(p_buffer,buses[p_bus]->channels.size(),NULL);
|
||||
|
||||
AudioFrame *data = buses[p_bus]->channels[p_buffer].buffer.ptr();
|
||||
|
||||
|
||||
if (!buses[p_bus]->channels[p_buffer].used) {
|
||||
buses[p_bus]->channels[p_buffer].used=true;
|
||||
buses[p_bus]->channels[p_buffer].active=true;
|
||||
buses[p_bus]->channels[p_buffer].last_mix_with_audio=mix_frames;
|
||||
for(uint32_t i=0;i<buffer_size;i++) {
|
||||
data[i]=AudioFrame(0,0);
|
||||
}
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
int AudioServer::thread_get_mix_buffer_size() const {
|
||||
|
||||
return buffer_size;
|
||||
}
|
||||
|
||||
int AudioServer::thread_find_bus_index(const StringName& p_name) {
|
||||
|
||||
if (bus_map.has(p_name)) {
|
||||
return bus_map[p_name]->index_cache;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void AudioServer::set_bus_count(int p_count) {
|
||||
|
||||
ERR_FAIL_COND(p_count<1);
|
||||
ERR_FAIL_INDEX(p_count,256);
|
||||
lock();
|
||||
int cb = buses.size();
|
||||
|
||||
if (p_count<buses.size()) {
|
||||
for(int i=p_count;i<buses.size();i++) {
|
||||
bus_map.erase(buses[i]->name);
|
||||
memdelete(buses[i]);
|
||||
}
|
||||
}
|
||||
|
||||
buses.resize(p_count);
|
||||
|
||||
for(int i=cb;i<buses.size();i++) {
|
||||
|
||||
String attempt="New Bus";
|
||||
int attempts=1;
|
||||
while(true) {
|
||||
|
||||
bool name_free=true;
|
||||
for(int j=0;j<i;j++) {
|
||||
|
||||
if (buses[j]->name==attempt) {
|
||||
name_free=false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!name_free) {
|
||||
attempts++;
|
||||
attempt="New Bus " +itos(attempts);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
buses[i]=memnew(Bus);
|
||||
buses[i]->channels.resize(_get_channel_count());
|
||||
for(int j=0;j<_get_channel_count();j++) {
|
||||
buses[i]->channels[j].buffer.resize(buffer_size);
|
||||
}
|
||||
buses[i]->name=attempt;
|
||||
buses[i]->solo=false;
|
||||
buses[i]->mute=false;
|
||||
buses[i]->bypass=false;
|
||||
buses[i]->volume_db=0;
|
||||
if (i>0) {
|
||||
buses[i]->send="Master";
|
||||
}
|
||||
|
||||
bus_map[attempt]=buses[i];
|
||||
|
||||
}
|
||||
|
||||
unlock();
|
||||
|
||||
emit_signal("bus_layout_changed");
|
||||
}
|
||||
|
||||
int AudioServer::get_bus_count() const {
|
||||
@ -111,43 +387,139 @@ int AudioServer::get_bus_count() const {
|
||||
return buses.size();
|
||||
}
|
||||
|
||||
void AudioServer::set_bus_mode(int p_bus,BusMode p_mode) {
|
||||
|
||||
ERR_FAIL_INDEX(p_bus,buses.size());
|
||||
|
||||
}
|
||||
AudioServer::BusMode AudioServer::get_bus_mode(int p_bus) const {
|
||||
|
||||
ERR_FAIL_INDEX_V(p_bus,buses.size(),BUS_MODE_STEREO);
|
||||
|
||||
return buses[p_bus].mode;
|
||||
}
|
||||
|
||||
void AudioServer::set_bus_name(int p_bus,const String& p_name) {
|
||||
|
||||
ERR_FAIL_INDEX(p_bus,buses.size());
|
||||
buses[p_bus].name=p_name;
|
||||
if (p_bus==0 && p_name!="Master")
|
||||
return; //bus 0 is always master
|
||||
lock();
|
||||
|
||||
if (buses[p_bus]->name==p_name) {
|
||||
unlock();
|
||||
return;
|
||||
}
|
||||
|
||||
String attempt=p_name;
|
||||
int attempts=1;
|
||||
|
||||
while(true) {
|
||||
|
||||
bool name_free=true;
|
||||
for(int i=0;i<buses.size();i++) {
|
||||
|
||||
if (buses[i]->name==attempt) {
|
||||
name_free=false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (name_free) {
|
||||
break;
|
||||
}
|
||||
|
||||
attempts++;
|
||||
attempt=p_name+" "+itos(attempts);
|
||||
}
|
||||
bus_map.erase(buses[p_bus]->name);
|
||||
buses[p_bus]->name=attempt;
|
||||
bus_map[attempt]=buses[p_bus];
|
||||
unlock();
|
||||
|
||||
emit_signal("bus_layout_changed");
|
||||
|
||||
}
|
||||
String AudioServer::get_bus_name(int p_bus) const {
|
||||
|
||||
ERR_FAIL_INDEX_V(p_bus,buses.size(),String());
|
||||
return buses[p_bus].name;
|
||||
return buses[p_bus]->name;
|
||||
}
|
||||
|
||||
void AudioServer::set_bus_volume_db(int p_bus,float p_volume_db) {
|
||||
|
||||
ERR_FAIL_INDEX(p_bus,buses.size());
|
||||
buses[p_bus].volume_db=p_volume_db;
|
||||
buses[p_bus]->volume_db=p_volume_db;
|
||||
|
||||
}
|
||||
float AudioServer::get_bus_volume_db(int p_bus) const {
|
||||
|
||||
ERR_FAIL_INDEX_V(p_bus,buses.size(),0);
|
||||
return buses[p_bus].volume_db;
|
||||
return buses[p_bus]->volume_db;
|
||||
|
||||
}
|
||||
|
||||
void AudioServer::set_bus_send(int p_bus,const StringName& p_send) {
|
||||
|
||||
ERR_FAIL_INDEX(p_bus,buses.size());
|
||||
|
||||
buses[p_bus]->send=p_send;
|
||||
|
||||
}
|
||||
|
||||
StringName AudioServer::get_bus_send(int p_bus) const {
|
||||
|
||||
ERR_FAIL_INDEX_V(p_bus,buses.size(),StringName());
|
||||
return buses[p_bus]->send;
|
||||
|
||||
}
|
||||
|
||||
|
||||
void AudioServer::set_bus_solo(int p_bus,bool p_enable) {
|
||||
|
||||
ERR_FAIL_INDEX(p_bus,buses.size());
|
||||
|
||||
buses[p_bus]->solo=p_enable;
|
||||
|
||||
}
|
||||
|
||||
bool AudioServer::is_bus_solo(int p_bus) const{
|
||||
|
||||
ERR_FAIL_INDEX_V(p_bus,buses.size(),false);
|
||||
|
||||
return buses[p_bus]->solo;
|
||||
|
||||
}
|
||||
|
||||
void AudioServer::set_bus_mute(int p_bus,bool p_enable){
|
||||
|
||||
ERR_FAIL_INDEX(p_bus,buses.size());
|
||||
|
||||
buses[p_bus]->mute=p_enable;
|
||||
}
|
||||
bool AudioServer::is_bus_mute(int p_bus) const{
|
||||
|
||||
ERR_FAIL_INDEX_V(p_bus,buses.size(),false);
|
||||
|
||||
return buses[p_bus]->mute;
|
||||
|
||||
}
|
||||
|
||||
void AudioServer::set_bus_bypass_effects(int p_bus,bool p_enable){
|
||||
|
||||
ERR_FAIL_INDEX(p_bus,buses.size());
|
||||
|
||||
buses[p_bus]->bypass=p_enable;
|
||||
}
|
||||
bool AudioServer::is_bus_bypassing_effects(int p_bus) const{
|
||||
|
||||
ERR_FAIL_INDEX_V(p_bus,buses.size(),false);
|
||||
|
||||
return buses[p_bus]->bypass;
|
||||
|
||||
}
|
||||
|
||||
|
||||
void AudioServer::_update_bus_effects(int p_bus) {
|
||||
|
||||
for(int i=0;i<buses[p_bus]->channels.size();i++) {
|
||||
buses[p_bus]->channels[i].effect_instances.resize(buses[p_bus]->effects.size());
|
||||
for(int j=0;j<buses[p_bus]->effects.size();j++) {
|
||||
buses[p_bus]->channels[i].effect_instances[j]=buses[p_bus]->effects[j].effect->instance();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void AudioServer::add_bus_effect(int p_bus,const Ref<AudioEffect>& p_effect,int p_at_pos) {
|
||||
|
||||
ERR_FAIL_COND(p_effect.is_null());
|
||||
@ -160,12 +532,14 @@ void AudioServer::add_bus_effect(int p_bus,const Ref<AudioEffect>& p_effect,int
|
||||
//fx.instance=p_effect->instance();
|
||||
fx.enabled=true;
|
||||
|
||||
if (p_at_pos>=buses[p_bus].effects.size() || p_at_pos<0) {
|
||||
buses[p_bus].effects.push_back(fx);
|
||||
if (p_at_pos>=buses[p_bus]->effects.size() || p_at_pos<0) {
|
||||
buses[p_bus]->effects.push_back(fx);
|
||||
} else {
|
||||
buses[p_bus].effects.insert(p_at_pos,fx);
|
||||
buses[p_bus]->effects.insert(p_at_pos,fx);
|
||||
}
|
||||
|
||||
_update_bus_effects(p_bus);
|
||||
|
||||
unlock();
|
||||
}
|
||||
|
||||
@ -176,7 +550,8 @@ void AudioServer::remove_bus_effect(int p_bus,int p_effect) {
|
||||
|
||||
lock();
|
||||
|
||||
buses[p_bus].effects.remove(p_effect);
|
||||
buses[p_bus]->effects.remove(p_effect);
|
||||
_update_bus_effects(p_bus);
|
||||
|
||||
unlock();
|
||||
}
|
||||
@ -185,52 +560,117 @@ int AudioServer::get_bus_effect_count(int p_bus) {
|
||||
|
||||
ERR_FAIL_INDEX_V(p_bus,buses.size(),0);
|
||||
|
||||
return buses[p_bus].effects.size();
|
||||
return buses[p_bus]->effects.size();
|
||||
|
||||
}
|
||||
|
||||
Ref<AudioEffect> AudioServer::get_bus_effect(int p_bus,int p_effect) {
|
||||
|
||||
ERR_FAIL_INDEX_V(p_bus,buses.size(),Ref<AudioEffect>());
|
||||
ERR_FAIL_INDEX_V(p_effect,buses[p_bus].effects.size(),Ref<AudioEffect>());
|
||||
ERR_FAIL_INDEX_V(p_effect,buses[p_bus]->effects.size(),Ref<AudioEffect>());
|
||||
|
||||
return buses[p_bus].effects[p_effect].effect;
|
||||
return buses[p_bus]->effects[p_effect].effect;
|
||||
|
||||
}
|
||||
|
||||
void AudioServer::swap_bus_effects(int p_bus,int p_effect,int p_by_effect) {
|
||||
|
||||
ERR_FAIL_INDEX(p_bus,buses.size());
|
||||
ERR_FAIL_INDEX(p_effect,buses[p_bus].effects.size());
|
||||
ERR_FAIL_INDEX(p_by_effect,buses[p_bus].effects.size());
|
||||
ERR_FAIL_INDEX(p_effect,buses[p_bus]->effects.size());
|
||||
ERR_FAIL_INDEX(p_by_effect,buses[p_bus]->effects.size());
|
||||
|
||||
lock();
|
||||
SWAP( buses[p_bus].effects[p_effect], buses[p_bus].effects[p_by_effect] );
|
||||
SWAP( buses[p_bus]->effects[p_effect], buses[p_bus]->effects[p_by_effect] );
|
||||
_update_bus_effects(p_bus);
|
||||
unlock();
|
||||
}
|
||||
|
||||
void AudioServer::set_bus_effect_enabled(int p_bus,int p_effect,bool p_enabled) {
|
||||
|
||||
ERR_FAIL_INDEX(p_bus,buses.size());
|
||||
ERR_FAIL_INDEX(p_effect,buses[p_bus].effects.size());
|
||||
buses[p_bus].effects[p_effect].enabled=p_enabled;
|
||||
ERR_FAIL_INDEX(p_effect,buses[p_bus]->effects.size());
|
||||
buses[p_bus]->effects[p_effect].enabled=p_enabled;
|
||||
|
||||
}
|
||||
bool AudioServer::is_bus_effect_enabled(int p_bus,int p_effect) const {
|
||||
|
||||
ERR_FAIL_INDEX_V(p_bus,buses.size(),false);
|
||||
ERR_FAIL_INDEX_V(p_effect,buses[p_bus].effects.size(),false);
|
||||
return buses[p_bus].effects[p_effect].enabled;
|
||||
ERR_FAIL_INDEX_V(p_effect,buses[p_bus]->effects.size(),false);
|
||||
return buses[p_bus]->effects[p_effect].enabled;
|
||||
|
||||
}
|
||||
|
||||
void AudioServer::move_bus(int p_bus,int p_to_bus) {
|
||||
|
||||
ERR_FAIL_COND(p_bus<1 || p_bus>=buses.size());
|
||||
ERR_FAIL_COND(p_bus<1 || p_to_bus>=buses.size());
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
float AudioServer::get_bus_peak_volume_left_db(int p_bus,int p_channel) const {
|
||||
|
||||
ERR_FAIL_INDEX_V(p_bus,buses.size(),0);
|
||||
ERR_FAIL_INDEX_V(p_channel,buses[p_bus]->channels.size(),0);
|
||||
|
||||
return buses[p_bus]->channels[p_channel].peak_volume.l;
|
||||
|
||||
}
|
||||
float AudioServer::get_bus_peak_volume_right_db(int p_bus,int p_channel) const {
|
||||
|
||||
ERR_FAIL_INDEX_V(p_bus,buses.size(),0);
|
||||
ERR_FAIL_INDEX_V(p_channel,buses[p_bus]->channels.size(),0);
|
||||
|
||||
return buses[p_bus]->channels[p_channel].peak_volume.r;
|
||||
|
||||
}
|
||||
|
||||
bool AudioServer::is_bus_channel_active(int p_bus,int p_channel) const {
|
||||
|
||||
ERR_FAIL_INDEX_V(p_bus,buses.size(),false);
|
||||
ERR_FAIL_INDEX_V(p_channel,buses[p_bus]->channels.size(),false);
|
||||
|
||||
return buses[p_bus]->channels[p_channel].active;
|
||||
|
||||
}
|
||||
|
||||
void AudioServer::init() {
|
||||
|
||||
channel_disable_treshold_db=GLOBAL_DEF("audio/channel_disable_treshold_db",-60.0);
|
||||
channel_disable_frames=float(GLOBAL_DEF("audio/channel_disable_time",2.0))*get_mix_rate();
|
||||
buffer_size=1024; //harcoded for now
|
||||
switch( get_speaker_mode() ) {
|
||||
case SPEAKER_MODE_STEREO: {
|
||||
temp_buffer.resize(1);
|
||||
} break;
|
||||
case SPEAKER_SURROUND_51: {
|
||||
temp_buffer.resize(3);
|
||||
} break;
|
||||
case SPEAKER_SURROUND_71: {
|
||||
temp_buffer.resize(4);
|
||||
} break;
|
||||
}
|
||||
|
||||
for(int i=0;i<temp_buffer.size();i++) {
|
||||
temp_buffer[i].resize(buffer_size);
|
||||
}
|
||||
|
||||
mix_count=0;
|
||||
set_bus_count(1);;
|
||||
set_bus_name(0,"Master");
|
||||
|
||||
|
||||
if (AudioDriver::get_singleton())
|
||||
AudioDriver::get_singleton()->start();
|
||||
|
||||
}
|
||||
void AudioServer::finish() {
|
||||
|
||||
for(int i=0;i<buses.size();i++) {
|
||||
memdelete(buses[i]);
|
||||
}
|
||||
|
||||
buses.clear();
|
||||
}
|
||||
void AudioServer::update() {
|
||||
@ -282,18 +722,130 @@ double AudioServer::get_output_delay() const {
|
||||
AudioServer* AudioServer::singleton=NULL;
|
||||
|
||||
|
||||
void AudioServer::_bind_methods() {
|
||||
|
||||
|
||||
void* AudioServer::audio_data_alloc(uint32_t p_data_len,const uint8_t *p_from_data) {
|
||||
|
||||
void * ad = memalloc( p_data_len );
|
||||
ERR_FAIL_COND_V(!ad,NULL);
|
||||
if (p_from_data) {
|
||||
copymem(ad,p_from_data,p_data_len);
|
||||
}
|
||||
|
||||
audio_data_lock->lock();
|
||||
audio_data[ad]=p_data_len;
|
||||
audio_data_total_mem+=p_data_len;
|
||||
audio_data_max_mem=MAX(audio_data_total_mem,audio_data_max_mem);
|
||||
audio_data_lock->unlock();
|
||||
|
||||
return ad;
|
||||
}
|
||||
|
||||
void AudioServer::audio_data_free(void* p_data) {
|
||||
|
||||
audio_data_lock->lock();
|
||||
if (!audio_data.has(p_data)) {
|
||||
audio_data_lock->unlock();
|
||||
ERR_FAIL();
|
||||
}
|
||||
|
||||
audio_data_total_mem-=audio_data[p_data];
|
||||
audio_data.erase(p_data);
|
||||
memfree(p_data);
|
||||
audio_data_lock->unlock();
|
||||
|
||||
|
||||
}
|
||||
|
||||
size_t AudioServer::audio_data_get_total_memory_usage() const{
|
||||
|
||||
return audio_data_total_mem;
|
||||
}
|
||||
size_t AudioServer::audio_data_get_max_memory_usage() const{
|
||||
|
||||
return audio_data_max_mem;
|
||||
|
||||
}
|
||||
|
||||
void AudioServer::add_callback(AudioCallback p_callback,void *p_userdata) {
|
||||
lock();
|
||||
CallbackItem ci;
|
||||
ci.callback=p_callback;
|
||||
ci.userdata=p_userdata;
|
||||
callbacks.insert(ci);
|
||||
unlock();
|
||||
}
|
||||
|
||||
void AudioServer::remove_callback(AudioCallback p_callback,void *p_userdata) {
|
||||
|
||||
lock();
|
||||
CallbackItem ci;
|
||||
ci.callback=p_callback;
|
||||
ci.userdata=p_userdata;
|
||||
callbacks.erase(ci);
|
||||
unlock();
|
||||
|
||||
}
|
||||
|
||||
void AudioServer::_bind_methods() {
|
||||
|
||||
|
||||
ClassDB::bind_method(_MD("set_bus_count","amount"),&AudioServer::set_bus_count);
|
||||
ClassDB::bind_method(_MD("get_bus_count"),&AudioServer::get_bus_count);
|
||||
|
||||
ClassDB::bind_method(_MD("set_bus_name","bus_idx","name"),&AudioServer::set_bus_name);
|
||||
ClassDB::bind_method(_MD("get_bus_name","bus_idx"),&AudioServer::get_bus_name);
|
||||
|
||||
ClassDB::bind_method(_MD("set_bus_volume_db","bus_idx","volume_db"),&AudioServer::set_bus_volume_db);
|
||||
ClassDB::bind_method(_MD("get_bus_volume_db","bus_idx"),&AudioServer::get_bus_volume_db);
|
||||
|
||||
ClassDB::bind_method(_MD("set_bus_send","bus_idx","send"),&AudioServer::set_bus_send);
|
||||
ClassDB::bind_method(_MD("get_bus_send","bus_idx"),&AudioServer::get_bus_send);
|
||||
|
||||
ClassDB::bind_method(_MD("set_bus_solo","bus_idx","enable"),&AudioServer::set_bus_solo);
|
||||
ClassDB::bind_method(_MD("is_bus_solo","bus_idx"),&AudioServer::is_bus_solo);
|
||||
|
||||
ClassDB::bind_method(_MD("set_bus_mute","bus_idx","enable"),&AudioServer::set_bus_mute);
|
||||
ClassDB::bind_method(_MD("is_bus_mute","bus_idx"),&AudioServer::is_bus_mute);
|
||||
|
||||
ClassDB::bind_method(_MD("set_bus_bypass_effects","bus_idx","enable"),&AudioServer::set_bus_bypass_effects);
|
||||
ClassDB::bind_method(_MD("is_bus_bypassing_effects","bus_idx"),&AudioServer::is_bus_bypassing_effects);
|
||||
|
||||
ClassDB::bind_method(_MD("add_bus_effect","bus_idx","effect:AudioEffect"),&AudioServer::add_bus_effect);
|
||||
ClassDB::bind_method(_MD("remove_bus_effect","bus_idx","effect_idx"),&AudioServer::remove_bus_effect);
|
||||
|
||||
ClassDB::bind_method(_MD("get_bus_effect_count","bus_idx"),&AudioServer::add_bus_effect);
|
||||
ClassDB::bind_method(_MD("get_bus_effect:AudioEffect","bus_idx","effect_idx"),&AudioServer::get_bus_effect);
|
||||
ClassDB::bind_method(_MD("swap_bus_effects","bus_idx","effect_idx","by_effect_idx"),&AudioServer::swap_bus_effects);
|
||||
|
||||
ClassDB::bind_method(_MD("set_bus_effect_enabled","bus_idx","effect_idx","enabled"),&AudioServer::set_bus_effect_enabled);
|
||||
ClassDB::bind_method(_MD("is_bus_effect_enabled","bus_idx","effect_idx"),&AudioServer::is_bus_effect_enabled);
|
||||
|
||||
ClassDB::bind_method(_MD("get_bus_peak_volume_left_db","bus_idx","channel"),&AudioServer::get_bus_peak_volume_left_db);
|
||||
ClassDB::bind_method(_MD("get_bus_peak_volume_right_db","bus_idx","channel"),&AudioServer::get_bus_peak_volume_right_db);
|
||||
|
||||
ClassDB::bind_method(_MD("lock"),&AudioServer::lock);
|
||||
ClassDB::bind_method(_MD("unlock"),&AudioServer::unlock);
|
||||
|
||||
ClassDB::bind_method(_MD("get_speaker_mode"),&AudioServer::get_speaker_mode);
|
||||
ClassDB::bind_method(_MD("get_mix_rate"),&AudioServer::get_mix_rate);
|
||||
|
||||
ADD_SIGNAL(MethodInfo("bus_layout_changed") );
|
||||
}
|
||||
|
||||
AudioServer::AudioServer() {
|
||||
|
||||
singleton=this;
|
||||
audio_data_total_mem=0;
|
||||
audio_data_max_mem=0;
|
||||
audio_data_lock=Mutex::create();
|
||||
mix_frames=0;
|
||||
to_mix=0;
|
||||
|
||||
}
|
||||
|
||||
AudioServer::~AudioServer() {
|
||||
|
||||
|
||||
memdelete(audio_data_lock);
|
||||
}
|
||||
|
||||
|
@ -100,63 +100,148 @@ public:
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
class AudioServer : public Object {
|
||||
|
||||
GDCLASS( AudioServer, Object )
|
||||
public:
|
||||
enum BusMode {
|
||||
BUS_MODE_STEREO,
|
||||
BUS_MODE_SURROUND
|
||||
};
|
||||
|
||||
//re-expose this her, as AudioDriver is not exposed to script
|
||||
enum SpeakerMode {
|
||||
SPEAKER_MODE_STEREO,
|
||||
SPEAKER_SURROUND_51,
|
||||
SPEAKER_SURROUND_71,
|
||||
};
|
||||
|
||||
enum {
|
||||
AUDIO_DATA_INVALID_ID=-1
|
||||
};
|
||||
|
||||
typedef void (*AudioCallback)(void* p_userdata);
|
||||
|
||||
private:
|
||||
uint32_t buffer_size;
|
||||
uint64_t mix_count;
|
||||
uint64_t mix_frames;
|
||||
|
||||
float channel_disable_treshold_db;
|
||||
uint32_t channel_disable_frames;
|
||||
|
||||
int to_mix;
|
||||
|
||||
struct Bus {
|
||||
|
||||
String name;
|
||||
BusMode mode;
|
||||
Vector<AudioFrame> buffer;
|
||||
StringName name;
|
||||
bool solo;
|
||||
bool mute;
|
||||
bool bypass;
|
||||
|
||||
//Each channel is a stereo pair.
|
||||
struct Channel {
|
||||
bool used;
|
||||
bool active;
|
||||
AudioFrame peak_volume;
|
||||
Vector<AudioFrame> buffer;
|
||||
Vector<Ref<AudioEffectInstance> > effect_instances;
|
||||
uint64_t last_mix_with_audio;
|
||||
Channel() { last_mix_with_audio=0; used=false; active=false; peak_volume=AudioFrame(0,0); }
|
||||
};
|
||||
|
||||
Vector<Channel> channels;
|
||||
|
||||
|
||||
struct Effect {
|
||||
Ref<AudioEffect> effect;
|
||||
Ref<AudioEffectInstance> instance;
|
||||
bool enabled;
|
||||
};
|
||||
|
||||
Vector<Effect> effects;
|
||||
|
||||
float volume_db;
|
||||
StringName send;
|
||||
int index_cache;
|
||||
};
|
||||
|
||||
|
||||
Vector<Bus> buses;
|
||||
Vector< Vector<AudioFrame> >temp_buffer; //temp_buffer for each level
|
||||
Vector<Bus*> buses;
|
||||
Map<StringName,Bus*> bus_map;
|
||||
|
||||
_FORCE_INLINE_ int _get_channel_count() const {
|
||||
switch (AudioDriver::get_singleton()->get_speaker_mode()) {
|
||||
case AudioDriver::SPEAKER_MODE_STEREO: return 1;
|
||||
case AudioDriver::SPEAKER_SURROUND_51: return 3;
|
||||
case AudioDriver::SPEAKER_SURROUND_71: return 4;
|
||||
|
||||
}
|
||||
ERR_FAIL_V(1);
|
||||
}
|
||||
|
||||
|
||||
static void _bind_methods();
|
||||
void _update_bus_effects(int p_bus);
|
||||
|
||||
|
||||
static AudioServer* singleton;
|
||||
|
||||
// TODO create an audiodata pool to optimize memory
|
||||
|
||||
|
||||
Map<void*,uint32_t> audio_data;
|
||||
size_t audio_data_total_mem;
|
||||
size_t audio_data_max_mem;
|
||||
|
||||
Mutex *audio_data_lock;
|
||||
|
||||
void _mix_step();
|
||||
|
||||
struct CallbackItem {
|
||||
|
||||
AudioCallback callback;
|
||||
void *userdata;
|
||||
|
||||
bool operator<(const CallbackItem& p_item) const {
|
||||
return (callback==p_item.callback ? userdata < p_item.userdata : callback < p_item.callback);
|
||||
}
|
||||
};
|
||||
|
||||
Set<CallbackItem> callbacks;
|
||||
|
||||
|
||||
|
||||
friend class AudioDriver;
|
||||
void _driver_process(int p_frames, int32_t *p_buffer);
|
||||
protected:
|
||||
|
||||
static void _bind_methods();
|
||||
public:
|
||||
|
||||
//do not use from outside audio thread
|
||||
AudioFrame *thread_get_channel_mix_buffer(int p_bus,int p_buffer);
|
||||
int thread_get_mix_buffer_size() const;
|
||||
int thread_find_bus_index(const StringName& p_name);
|
||||
|
||||
|
||||
void set_bus_count(int p_count);
|
||||
int get_bus_count() const;
|
||||
|
||||
void set_bus_mode(int p_bus,BusMode p_mode);
|
||||
BusMode get_bus_mode(int p_bus) const;
|
||||
|
||||
void set_bus_name(int p_bus,const String& p_name);
|
||||
String get_bus_name(int p_bus) const;
|
||||
|
||||
void set_bus_volume_db(int p_bus,float p_volume_db);
|
||||
float get_bus_volume_db(int p_bus) const;
|
||||
|
||||
|
||||
void set_bus_send(int p_bus,const StringName& p_send);
|
||||
StringName get_bus_send(int p_bus) const;
|
||||
|
||||
void set_bus_solo(int p_bus,bool p_enable);
|
||||
bool is_bus_solo(int p_bus) const;
|
||||
|
||||
void set_bus_mute(int p_bus,bool p_enable);
|
||||
bool is_bus_mute(int p_bus) const;
|
||||
|
||||
void set_bus_bypass_effects(int p_bus,bool p_enable);
|
||||
bool is_bus_bypassing_effects(int p_bus) const;
|
||||
|
||||
void add_bus_effect(int p_bus,const Ref<AudioEffect>& p_effect,int p_at_pos=-1);
|
||||
void remove_bus_effect(int p_bus,int p_effect);
|
||||
|
||||
@ -168,6 +253,13 @@ public:
|
||||
void set_bus_effect_enabled(int p_bus,int p_effect,bool p_enabled);
|
||||
bool is_bus_effect_enabled(int p_bus,int p_effect) const;
|
||||
|
||||
void move_bus(int p_bus,int p_to_bus);
|
||||
|
||||
float get_bus_peak_volume_left_db(int p_bus,int p_channel) const;
|
||||
float get_bus_peak_volume_right_db(int p_bus,int p_channel) const;
|
||||
|
||||
bool is_bus_channel_active(int p_bus,int p_channel) const;
|
||||
|
||||
virtual void init();
|
||||
virtual void finish();
|
||||
virtual void update();
|
||||
@ -188,11 +280,21 @@ public:
|
||||
virtual double get_mix_time() const; //useful for video -> audio sync
|
||||
virtual double get_output_delay() const;
|
||||
|
||||
void* audio_data_alloc(uint32_t p_data_len, const uint8_t *p_from_data=NULL);
|
||||
void audio_data_free(void* p_data);
|
||||
|
||||
size_t audio_data_get_total_memory_usage() const;
|
||||
size_t audio_data_get_max_memory_usage() const;
|
||||
|
||||
|
||||
void add_callback(AudioCallback p_callback,void *p_userdata);
|
||||
void remove_callback(AudioCallback p_callback,void *p_userdata);
|
||||
|
||||
AudioServer();
|
||||
virtual ~AudioServer();
|
||||
};
|
||||
|
||||
VARIANT_ENUM_CAST( AudioServer::BusMode )
|
||||
|
||||
VARIANT_ENUM_CAST( AudioServer::SpeakerMode )
|
||||
|
||||
typedef AudioServer AS;
|
||||
|
@ -35,6 +35,12 @@
|
||||
#include "physics_2d_server.h"
|
||||
#include "script_debugger_remote.h"
|
||||
#include "visual/shader_types.h"
|
||||
#include "audio/audio_stream.h"
|
||||
#include "audio/audio_effect.h"
|
||||
#include "audio/effects/audio_effect_amplify.h"
|
||||
#include "audio/effects/audio_effect_reverb.h"
|
||||
#include "audio/effects/audio_effect_filter.h"
|
||||
#include "audio/effects/audio_effect_eq.h"
|
||||
|
||||
static void _debugger_get_resource_usage(List<ScriptDebuggerRemote::ResourceUsage>* r_usage) {
|
||||
|
||||
@ -67,6 +73,26 @@ void register_server_types() {
|
||||
|
||||
shader_types = memnew( ShaderTypes );
|
||||
|
||||
ClassDB::register_virtual_class<AudioStream>();
|
||||
ClassDB::register_virtual_class<AudioStreamPlayback>();
|
||||
ClassDB::register_virtual_class<AudioEffect>();
|
||||
|
||||
ClassDB::register_class<AudioEffectAmplify>();
|
||||
|
||||
ClassDB::register_class<AudioEffectReverb>();
|
||||
|
||||
ClassDB::register_class<AudioEffectLowPass>();
|
||||
ClassDB::register_class<AudioEffectHighPass>();
|
||||
ClassDB::register_class<AudioEffectBandPass>();
|
||||
ClassDB::register_class<AudioEffectNotchPass>();
|
||||
ClassDB::register_class<AudioEffectBandLimit>();
|
||||
ClassDB::register_class<AudioEffectLowShelf>();
|
||||
ClassDB::register_class<AudioEffectHighShelf>();
|
||||
|
||||
ClassDB::register_class<AudioEffectEQ6>();
|
||||
ClassDB::register_class<AudioEffectEQ10>();
|
||||
ClassDB::register_class<AudioEffectEQ21>();
|
||||
|
||||
|
||||
ClassDB::register_virtual_class<Physics2DDirectBodyState>();
|
||||
ClassDB::register_virtual_class<Physics2DDirectSpaceState>();
|
||||
|
5399
thirdparty/stb_vorbis/stb_vorbis.c
vendored
Normal file
619
tools/editor/editor_audio_buses.cpp
Normal file
@ -0,0 +1,619 @@
|
||||
#include "editor_audio_buses.h"
|
||||
#include "editor_node.h"
|
||||
#include "servers/audio_server.h"
|
||||
|
||||
|
||||
void EditorAudioBus::_notification(int p_what) {
|
||||
|
||||
if (p_what==NOTIFICATION_READY) {
|
||||
|
||||
vu_l->set_under_texture(get_icon("BusVuEmpty","EditorIcons"));
|
||||
vu_l->set_progress_texture(get_icon("BusVuFull","EditorIcons"));
|
||||
vu_r->set_under_texture(get_icon("BusVuEmpty","EditorIcons"));
|
||||
vu_r->set_progress_texture(get_icon("BusVuFull","EditorIcons"));
|
||||
scale->set_texture( get_icon("BusVuDb","EditorIcons"));
|
||||
|
||||
disabled_vu = get_icon("BusVuFrozen","EditorIcons");
|
||||
|
||||
prev_active=true;
|
||||
update_bus();
|
||||
set_process(true);
|
||||
}
|
||||
|
||||
if (p_what==NOTIFICATION_DRAW) {
|
||||
|
||||
if (has_focus()) {
|
||||
draw_style_box(get_stylebox("focus","Button"),Rect2(Vector2(),get_size()));
|
||||
}
|
||||
}
|
||||
|
||||
if (p_what==NOTIFICATION_PROCESS) {
|
||||
|
||||
float real_peak[2]={-100,-100};
|
||||
bool activity_found=false;
|
||||
|
||||
int cc;
|
||||
switch(AudioServer::get_singleton()->get_speaker_mode()) {
|
||||
case AudioServer::SPEAKER_MODE_STEREO: cc = 1; break;
|
||||
case AudioServer::SPEAKER_SURROUND_51: cc = 4; break;
|
||||
case AudioServer::SPEAKER_SURROUND_71: cc = 5; break;
|
||||
}
|
||||
|
||||
for(int i=0;i<cc;i++) {
|
||||
if (AudioServer::get_singleton()->is_bus_channel_active(get_index(),i)) {
|
||||
activity_found=true;
|
||||
real_peak[0]=MAX(real_peak[0],AudioServer::get_singleton()->get_bus_peak_volume_left_db(get_index(),i));
|
||||
real_peak[1]=MAX(real_peak[1],AudioServer::get_singleton()->get_bus_peak_volume_right_db(get_index(),i));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (real_peak[0]>peak_l) {
|
||||
peak_l = real_peak[0];
|
||||
} else {
|
||||
peak_l-=get_process_delta_time()*60.0;
|
||||
}
|
||||
|
||||
if (real_peak[1]>peak_r) {
|
||||
peak_r = real_peak[1];
|
||||
} else {
|
||||
peak_r-=get_process_delta_time()*60.0;
|
||||
|
||||
}
|
||||
|
||||
vu_l->set_value(peak_l);
|
||||
vu_r->set_value(peak_r);
|
||||
|
||||
if (activity_found!=prev_active) {
|
||||
if (activity_found) {
|
||||
vu_l->set_over_texture(Ref<Texture>());
|
||||
vu_r->set_over_texture(Ref<Texture>());
|
||||
} else {
|
||||
vu_l->set_over_texture(disabled_vu);
|
||||
vu_r->set_over_texture(disabled_vu);
|
||||
|
||||
}
|
||||
|
||||
prev_active=activity_found;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (p_what==NOTIFICATION_VISIBILITY_CHANGED) {
|
||||
|
||||
peak_l=-100;
|
||||
peak_r=-100;
|
||||
prev_active=true;
|
||||
|
||||
set_process(is_visible_in_tree());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void EditorAudioBus::update_send() {
|
||||
|
||||
send->clear();
|
||||
if (get_index()==0) {
|
||||
send->set_disabled(true);
|
||||
send->set_text("Speakers");
|
||||
} else {
|
||||
send->set_disabled(false);
|
||||
StringName current_send = AudioServer::get_singleton()->get_bus_send(get_index());
|
||||
int current_send_index=0; //by default to master
|
||||
|
||||
for(int i=0;i<get_index();i++) {
|
||||
StringName send_name = AudioServer::get_singleton()->get_bus_name(i);
|
||||
send->add_item(send_name);
|
||||
if (send_name==current_send) {
|
||||
current_send_index=i;
|
||||
}
|
||||
}
|
||||
|
||||
send->select(current_send_index);
|
||||
}
|
||||
}
|
||||
|
||||
void EditorAudioBus::update_bus() {
|
||||
|
||||
if (updating_bus)
|
||||
return;
|
||||
|
||||
updating_bus=true;
|
||||
|
||||
int index = get_index();
|
||||
|
||||
slider->set_value(AudioServer::get_singleton()->get_bus_volume_db(index));
|
||||
track_name->set_text(AudioServer::get_singleton()->get_bus_name(index));
|
||||
if (get_index()==0)
|
||||
track_name->set_editable(false);
|
||||
|
||||
solo->set_pressed( AudioServer::get_singleton()->is_bus_solo(index));
|
||||
mute->set_pressed( AudioServer::get_singleton()->is_bus_mute(index));
|
||||
bypass->set_pressed( AudioServer::get_singleton()->is_bus_bypassing_effects(index));
|
||||
// effects..
|
||||
effects->clear();
|
||||
|
||||
TreeItem *root = effects->create_item();
|
||||
for(int i=0;i<AudioServer::get_singleton()->get_bus_effect_count(index);i++) {
|
||||
|
||||
Ref<AudioEffect> afx = AudioServer::get_singleton()->get_bus_effect(index,i);
|
||||
|
||||
TreeItem *fx = effects->create_item(root);
|
||||
fx->set_cell_mode(0,TreeItem::CELL_MODE_CHECK);
|
||||
fx->set_editable(0,true);
|
||||
fx->set_checked(0,AudioServer::get_singleton()->is_bus_effect_enabled(index,i));
|
||||
fx->set_text(0,afx->get_name());
|
||||
fx->set_metadata(0,i);
|
||||
|
||||
}
|
||||
|
||||
TreeItem *add = effects->create_item(root);
|
||||
add->set_cell_mode(0,TreeItem::CELL_MODE_CUSTOM);
|
||||
add->set_editable(0,true);
|
||||
add->set_selectable(0,false);
|
||||
add->set_text(0,"Add Effect");
|
||||
|
||||
update_send();
|
||||
|
||||
updating_bus=false;
|
||||
|
||||
}
|
||||
|
||||
|
||||
void EditorAudioBus::_name_changed(const String& p_new_name) {
|
||||
|
||||
if (p_new_name==AudioServer::get_singleton()->get_bus_name(get_index()))
|
||||
return;
|
||||
|
||||
String attempt=p_new_name;
|
||||
int attempts=1;
|
||||
|
||||
while(true) {
|
||||
|
||||
bool name_free=true;
|
||||
for(int i=0;i<AudioServer::get_singleton()->get_bus_count();i++) {
|
||||
|
||||
if (AudioServer::get_singleton()->get_bus_name(i)==attempt) {
|
||||
name_free=false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (name_free) {
|
||||
break;
|
||||
}
|
||||
|
||||
attempts++;
|
||||
attempt=p_new_name+" "+itos(attempts);
|
||||
}
|
||||
updating_bus=true;
|
||||
|
||||
UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo();
|
||||
|
||||
StringName current = AudioServer::get_singleton()->get_bus_name(get_index());
|
||||
ur->create_action("Rename Audio Bus");
|
||||
ur->add_do_method(AudioServer::get_singleton(),"set_bus_name",get_index(),attempt);
|
||||
ur->add_undo_method(AudioServer::get_singleton(),"set_bus_name",get_index(),current);
|
||||
|
||||
for(int i=0;i<AudioServer::get_singleton()->get_bus_count();i++) {
|
||||
if (AudioServer::get_singleton()->get_bus_send(i)==current) {
|
||||
ur->add_do_method(AudioServer::get_singleton(),"set_bus_send",i,attempt);
|
||||
ur->add_undo_method(AudioServer::get_singleton(),"set_bus_send",i,current);
|
||||
}
|
||||
}
|
||||
|
||||
ur->add_do_method(buses,"_update_bus",get_index());
|
||||
ur->add_undo_method(buses,"_update_bus",get_index());
|
||||
|
||||
|
||||
ur->add_do_method(buses,"_update_sends");
|
||||
ur->add_undo_method(buses,"_update_sends");
|
||||
ur->commit_action();
|
||||
|
||||
updating_bus=false;
|
||||
|
||||
}
|
||||
|
||||
void EditorAudioBus::_volume_db_changed(float p_db){
|
||||
|
||||
if (updating_bus)
|
||||
return;
|
||||
|
||||
updating_bus=true;
|
||||
|
||||
print_line("new volume: "+rtos(p_db));
|
||||
UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo();
|
||||
ur->create_action("Change Audio Bus Volume",UndoRedo::MERGE_ENDS);
|
||||
ur->add_do_method(AudioServer::get_singleton(),"set_bus_volume_db",get_index(),p_db);
|
||||
ur->add_undo_method(AudioServer::get_singleton(),"set_bus_volume_db",get_index(),AudioServer::get_singleton()->get_bus_volume_db(get_index()));
|
||||
ur->add_do_method(buses,"_update_bus",get_index());
|
||||
ur->add_undo_method(buses,"_update_bus",get_index());
|
||||
ur->commit_action();
|
||||
|
||||
updating_bus=false;
|
||||
|
||||
}
|
||||
void EditorAudioBus::_solo_toggled(){
|
||||
|
||||
updating_bus=true;
|
||||
|
||||
UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo();
|
||||
ur->create_action("Toggle Audio Bus Solo");
|
||||
ur->add_do_method(AudioServer::get_singleton(),"set_bus_solo",get_index(),solo->is_pressed());
|
||||
ur->add_undo_method(AudioServer::get_singleton(),"set_bus_solo",get_index(),AudioServer::get_singleton()->is_bus_solo(get_index()));
|
||||
ur->add_do_method(buses,"_update_bus",get_index());
|
||||
ur->add_undo_method(buses,"_update_bus",get_index());
|
||||
ur->commit_action();
|
||||
|
||||
updating_bus=false;
|
||||
|
||||
}
|
||||
void EditorAudioBus::_mute_toggled(){
|
||||
|
||||
updating_bus=true;
|
||||
|
||||
UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo();
|
||||
ur->create_action("Toggle Audio Bus Mute");
|
||||
ur->add_do_method(AudioServer::get_singleton(),"set_bus_mute",get_index(),mute->is_pressed());
|
||||
ur->add_undo_method(AudioServer::get_singleton(),"set_bus_mute",get_index(),AudioServer::get_singleton()->is_bus_mute(get_index()));
|
||||
ur->add_do_method(buses,"_update_bus",get_index());
|
||||
ur->add_undo_method(buses,"_update_bus",get_index());
|
||||
ur->commit_action();
|
||||
|
||||
updating_bus=false;
|
||||
|
||||
}
|
||||
void EditorAudioBus::_bypass_toggled(){
|
||||
|
||||
updating_bus=true;
|
||||
|
||||
UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo();
|
||||
ur->create_action("Toggle Audio Bus Bypass Effects");
|
||||
ur->add_do_method(AudioServer::get_singleton(),"set_bus_bypass_effects",get_index(),bypass->is_pressed());
|
||||
ur->add_undo_method(AudioServer::get_singleton(),"set_bus_bypass_effects",get_index(),AudioServer::get_singleton()->is_bus_bypassing_effects(get_index()));
|
||||
ur->add_do_method(buses,"_update_bus",get_index());
|
||||
ur->add_undo_method(buses,"_update_bus",get_index());
|
||||
ur->commit_action();
|
||||
|
||||
updating_bus=false;
|
||||
|
||||
|
||||
}
|
||||
|
||||
void EditorAudioBus::_send_selected(int p_which) {
|
||||
|
||||
updating_bus=true;
|
||||
|
||||
UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo();
|
||||
ur->create_action("Select Audio Bus Send");
|
||||
ur->add_do_method(AudioServer::get_singleton(),"set_bus_send",get_index(),send->get_item_text(p_which));
|
||||
ur->add_undo_method(AudioServer::get_singleton(),"set_bus_send",get_index(),AudioServer::get_singleton()->get_bus_send(get_index()));
|
||||
ur->add_do_method(buses,"_update_bus",get_index());
|
||||
ur->add_undo_method(buses,"_update_bus",get_index());
|
||||
ur->commit_action();
|
||||
|
||||
updating_bus=false;
|
||||
}
|
||||
|
||||
void EditorAudioBus::_effect_selected() {
|
||||
|
||||
TreeItem *effect = effects->get_selected();
|
||||
if (!effect)
|
||||
return;
|
||||
updating_bus=true;
|
||||
|
||||
if (effect->get_metadata(0)!=Variant()) {
|
||||
|
||||
int index = effect->get_metadata(0);
|
||||
Ref<AudioEffect> effect = AudioServer::get_singleton()->get_bus_effect(get_index(),index);
|
||||
if (effect.is_valid()) {
|
||||
EditorNode::get_singleton()->push_item(effect.ptr());
|
||||
}
|
||||
}
|
||||
|
||||
updating_bus=false;
|
||||
|
||||
}
|
||||
|
||||
void EditorAudioBus::_effect_edited() {
|
||||
|
||||
if (updating_bus)
|
||||
return;
|
||||
|
||||
TreeItem *effect = effects->get_edited();
|
||||
if (!effect)
|
||||
return;
|
||||
|
||||
if (effect->get_metadata(0)==Variant()) {
|
||||
Rect2 area = effects->get_item_rect(effect);
|
||||
|
||||
effect_options->set_pos(effects->get_global_pos()+area.pos+Vector2(0,area.size.y));
|
||||
effect_options->popup();
|
||||
//add effect
|
||||
} else {
|
||||
int index = effect->get_metadata(0);
|
||||
updating_bus=true;
|
||||
|
||||
UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo();
|
||||
ur->create_action("Select Audio Bus Send");
|
||||
ur->add_do_method(AudioServer::get_singleton(),"set_bus_effect_enabled",get_index(),index,effect->is_checked(0));
|
||||
ur->add_undo_method(AudioServer::get_singleton(),"set_bus_effect_enabled",get_index(),index,AudioServer::get_singleton()->is_bus_effect_enabled(get_index(),index));
|
||||
ur->add_do_method(buses,"_update_bus",get_index());
|
||||
ur->add_undo_method(buses,"_update_bus",get_index());
|
||||
ur->commit_action();
|
||||
|
||||
updating_bus=false;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void EditorAudioBus::_effect_add(int p_which) {
|
||||
|
||||
if (updating_bus)
|
||||
return;
|
||||
|
||||
StringName name = effect_options->get_item_metadata(p_which);
|
||||
|
||||
Object *fx = ClassDB::instance(name);
|
||||
ERR_FAIL_COND(!fx);
|
||||
AudioEffect *afx = fx->cast_to<AudioEffect>();
|
||||
ERR_FAIL_COND(!afx);
|
||||
Ref<AudioEffect> afxr = Ref<AudioEffect>(afx);
|
||||
|
||||
afxr->set_name(effect_options->get_item_text(p_which));
|
||||
|
||||
UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo();
|
||||
ur->create_action("Add Audio Bus Effect");
|
||||
ur->add_do_method(AudioServer::get_singleton(),"add_bus_effect",get_index(),afxr,-1);
|
||||
ur->add_undo_method(AudioServer::get_singleton(),"remove_bus_effect",get_index(),AudioServer::get_singleton()->get_bus_effect_count(get_index()));
|
||||
ur->add_do_method(buses,"_update_bus",get_index());
|
||||
ur->add_undo_method(buses,"_update_bus",get_index());
|
||||
ur->commit_action();
|
||||
}
|
||||
|
||||
void EditorAudioBus::_bind_methods() {
|
||||
|
||||
ClassDB::bind_method("update_bus",&EditorAudioBus::update_bus);
|
||||
ClassDB::bind_method("update_send",&EditorAudioBus::update_send);
|
||||
ClassDB::bind_method("_name_changed",&EditorAudioBus::_name_changed);
|
||||
ClassDB::bind_method("_volume_db_changed",&EditorAudioBus::_volume_db_changed);
|
||||
ClassDB::bind_method("_solo_toggled",&EditorAudioBus::_solo_toggled);
|
||||
ClassDB::bind_method("_mute_toggled",&EditorAudioBus::_mute_toggled);
|
||||
ClassDB::bind_method("_bypass_toggled",&EditorAudioBus::_bypass_toggled);
|
||||
ClassDB::bind_method("_name_focus_exit",&EditorAudioBus::_name_focus_exit);
|
||||
ClassDB::bind_method("_send_selected",&EditorAudioBus::_send_selected);
|
||||
ClassDB::bind_method("_effect_edited",&EditorAudioBus::_effect_edited);
|
||||
ClassDB::bind_method("_effect_selected",&EditorAudioBus::_effect_selected);
|
||||
ClassDB::bind_method("_effect_add",&EditorAudioBus::_effect_add);
|
||||
}
|
||||
|
||||
EditorAudioBus::EditorAudioBus(EditorAudioBuses *p_buses) {
|
||||
|
||||
buses=p_buses;
|
||||
updating_bus=false;
|
||||
|
||||
VBoxContainer *vb = memnew( VBoxContainer );
|
||||
add_child(vb);
|
||||
|
||||
set_v_size_flags(SIZE_EXPAND_FILL);
|
||||
|
||||
track_name = memnew( LineEdit );
|
||||
vb->add_child(track_name);
|
||||
track_name->connect("text_entered",this,"_name_changed");
|
||||
track_name->connect("focus_exited",this,"_name_focus_exit");
|
||||
|
||||
HBoxContainer *hbc = memnew( HBoxContainer);
|
||||
vb->add_child(hbc);
|
||||
hbc->add_spacer();
|
||||
solo = memnew( ToolButton );
|
||||
solo->set_text("S");
|
||||
solo->set_toggle_mode(true);
|
||||
solo->set_modulate(Color(0.8,1.2,0.8));
|
||||
solo->set_focus_mode(FOCUS_NONE);
|
||||
solo->connect("pressed",this,"_solo_toggled");
|
||||
hbc->add_child(solo);
|
||||
mute = memnew( ToolButton );
|
||||
mute->set_text("M");
|
||||
mute->set_toggle_mode(true);
|
||||
mute->set_modulate(Color(1.2,0.8,0.8));
|
||||
mute->set_focus_mode(FOCUS_NONE);
|
||||
mute->connect("pressed",this,"_mute_toggled");
|
||||
hbc->add_child(mute);
|
||||
bypass = memnew( ToolButton );
|
||||
bypass->set_text("B");
|
||||
bypass->set_toggle_mode(true);
|
||||
bypass->set_modulate(Color(1.1,1.1,0.8));
|
||||
bypass->set_focus_mode(FOCUS_NONE);
|
||||
bypass->connect("pressed",this,"_bypass_toggled");
|
||||
hbc->add_child(bypass);
|
||||
hbc->add_spacer();
|
||||
|
||||
HBoxContainer *hb = memnew( HBoxContainer );
|
||||
vb->add_child(hb);
|
||||
slider = memnew( VSlider );
|
||||
slider->set_min(-80);
|
||||
slider->set_max(24);
|
||||
slider->set_step(0.1);
|
||||
|
||||
slider->connect("value_changed",this,"_volume_db_changed");
|
||||
hb->add_child(slider);
|
||||
vu_l = memnew( TextureProgress );
|
||||
vu_l->set_fill_mode(TextureProgress::FILL_BOTTOM_TO_TOP);
|
||||
hb->add_child(vu_l);
|
||||
vu_l->set_min(-80);
|
||||
vu_l->set_max(24);
|
||||
vu_l->set_step(0.1);
|
||||
|
||||
vu_r = memnew( TextureProgress );
|
||||
vu_r->set_fill_mode(TextureProgress::FILL_BOTTOM_TO_TOP);
|
||||
hb->add_child(vu_r);
|
||||
vu_r->set_min(-80);
|
||||
vu_r->set_max(24);
|
||||
vu_r->set_step(0.1);
|
||||
|
||||
scale = memnew( TextureRect );
|
||||
hb->add_child(scale);
|
||||
|
||||
add_child(hb);
|
||||
|
||||
effects = memnew( Tree );
|
||||
effects->set_hide_root(true);
|
||||
effects->set_custom_minimum_size(Size2(0,90)*EDSCALE);
|
||||
effects->set_hide_folding(true);
|
||||
vb->add_child(effects);
|
||||
effects->connect("item_edited",this,"_effect_edited");
|
||||
effects->connect("cell_selected",this,"_effect_selected");
|
||||
effects->set_edit_checkbox_cell_only_when_checkbox_is_pressed(true);
|
||||
|
||||
|
||||
send = memnew( OptionButton );
|
||||
send->set_clip_text(true);
|
||||
send->connect("item_selected",this,"_send_selected");
|
||||
vb->add_child(send);
|
||||
|
||||
set_focus_mode(FOCUS_CLICK);
|
||||
|
||||
effect_options = memnew( PopupMenu );
|
||||
effect_options->connect("index_pressed",this,"_effect_add");
|
||||
add_child(effect_options);
|
||||
List<StringName> effects;
|
||||
ClassDB::get_inheriters_from_class("AudioEffect",&effects);
|
||||
effects.sort_custom<StringName::AlphCompare>();
|
||||
for (List<StringName>::Element *E=effects.front();E;E=E->next()) {
|
||||
if (!ClassDB::can_instance(E->get()))
|
||||
continue;
|
||||
|
||||
Ref<Texture> icon;
|
||||
if (has_icon(E->get(),"EditorIcons")) {
|
||||
icon = get_icon(E->get(),"EditorIcons");
|
||||
}
|
||||
String name = E->get().operator String().replace("AudioEffect","");
|
||||
effect_options->add_item(name);
|
||||
effect_options->set_item_metadata(effect_options->get_item_count()-1,E->get());
|
||||
effect_options->set_item_icon(effect_options->get_item_count()-1,icon);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
void EditorAudioBuses::_update_buses() {
|
||||
|
||||
while(bus_hb->get_child_count()>0) {
|
||||
memdelete(bus_hb->get_child(0));
|
||||
}
|
||||
|
||||
for(int i=0;i<AudioServer::get_singleton()->get_bus_count();i++) {
|
||||
|
||||
EditorAudioBus *audio_bus = memnew( EditorAudioBus(this) );
|
||||
if (i==0) {
|
||||
audio_bus->set_self_modulate(Color(1,0.9,0.9));
|
||||
}
|
||||
bus_hb->add_child(audio_bus);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
void EditorAudioBuses::register_editor() {
|
||||
|
||||
EditorAudioBuses * audio_buses = memnew( EditorAudioBuses );
|
||||
EditorNode::get_singleton()->add_bottom_panel_item("Audio",audio_buses);
|
||||
}
|
||||
|
||||
void EditorAudioBuses::_notification(int p_what) {
|
||||
|
||||
if (p_what==NOTIFICATION_READY) {
|
||||
_update_buses();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void EditorAudioBuses::_add_bus() {
|
||||
|
||||
UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo();
|
||||
|
||||
//need to simulate new name, so we can undi :(
|
||||
ur->create_action("Add Audio Bus");
|
||||
ur->add_do_method(AudioServer::get_singleton(),"set_bus_count",AudioServer::get_singleton()->get_bus_count()+1);
|
||||
ur->add_undo_method(AudioServer::get_singleton(),"set_bus_count",AudioServer::get_singleton()->get_bus_count());
|
||||
ur->add_do_method(this,"_update_buses");
|
||||
ur->add_undo_method(this,"_update_buses");
|
||||
ur->commit_action();
|
||||
|
||||
}
|
||||
|
||||
void EditorAudioBuses::_update_bus(int p_index) {
|
||||
|
||||
if (p_index>=bus_hb->get_child_count())
|
||||
return;
|
||||
|
||||
bus_hb->get_child(p_index)->call("update_bus");
|
||||
}
|
||||
|
||||
void EditorAudioBuses::_update_sends() {
|
||||
|
||||
for(int i=0;i<bus_hb->get_child_count();i++) {
|
||||
bus_hb->get_child(i)->call("update_send");
|
||||
}
|
||||
}
|
||||
|
||||
void EditorAudioBuses::_bind_methods() {
|
||||
|
||||
ClassDB::bind_method("_add_bus",&EditorAudioBuses::_add_bus);
|
||||
ClassDB::bind_method("_update_buses",&EditorAudioBuses::_update_buses);
|
||||
ClassDB::bind_method("_update_bus",&EditorAudioBuses::_update_bus);
|
||||
ClassDB::bind_method("_update_sends",&EditorAudioBuses::_update_sends);
|
||||
}
|
||||
|
||||
EditorAudioBuses::EditorAudioBuses()
|
||||
{
|
||||
|
||||
top_hb = memnew( HBoxContainer );
|
||||
add_child(top_hb);
|
||||
|
||||
add = memnew( Button );
|
||||
top_hb->add_child(add);;
|
||||
add->set_text(TTR("Add"));
|
||||
|
||||
add->connect("pressed",this,"_add_bus");
|
||||
|
||||
Ref<ButtonGroup> bg;
|
||||
bg.instance();
|
||||
|
||||
buses = memnew( ToolButton );
|
||||
top_hb->add_child(buses);
|
||||
buses->set_text(TTR("Buses"));
|
||||
buses->set_button_group(bg);
|
||||
buses->set_toggle_mode(true);
|
||||
buses->set_pressed(true);
|
||||
|
||||
groups = memnew( ToolButton );
|
||||
top_hb->add_child(groups);
|
||||
groups->set_text(TTR("Groups"));
|
||||
groups->set_button_group(bg);
|
||||
groups->set_toggle_mode(true);
|
||||
|
||||
bus_scroll = memnew( ScrollContainer );
|
||||
bus_scroll->set_v_size_flags(SIZE_EXPAND_FILL);
|
||||
bus_scroll->set_enable_h_scroll(true);
|
||||
bus_scroll->set_enable_v_scroll(false);
|
||||
add_child(bus_scroll);
|
||||
bus_hb = memnew( HBoxContainer );
|
||||
bus_scroll->add_child(bus_hb);
|
||||
|
||||
group_scroll = memnew( ScrollContainer );
|
||||
group_scroll->set_v_size_flags(SIZE_EXPAND_FILL);
|
||||
group_scroll->set_enable_h_scroll(true);
|
||||
group_scroll->set_enable_v_scroll(false);
|
||||
add_child(group_scroll);
|
||||
group_hb = memnew( HBoxContainer );
|
||||
group_scroll->add_child(group_hb);
|
||||
|
||||
group_scroll->hide();
|
||||
|
||||
|
||||
set_v_size_flags(SIZE_EXPAND_FILL);
|
||||
|
||||
|
||||
}
|
106
tools/editor/editor_audio_buses.h
Normal file
@ -0,0 +1,106 @@
|
||||
#ifndef EDITORAUDIOBUSES_H
|
||||
#define EDITORAUDIOBUSES_H
|
||||
|
||||
|
||||
#include "scene/gui/box_container.h"
|
||||
#include "scene/gui/button.h"
|
||||
#include "scene/gui/tool_button.h"
|
||||
#include "scene/gui/scroll_container.h"
|
||||
#include "scene/gui/panel_container.h"
|
||||
#include "scene/gui/slider.h"
|
||||
#include "scene/gui/texture_progress.h"
|
||||
#include "scene/gui/texture_rect.h"
|
||||
#include "scene/gui/line_edit.h"
|
||||
#include "scene/gui/tree.h"
|
||||
#include "scene/gui/option_button.h"
|
||||
|
||||
class EditorAudioBuses;
|
||||
|
||||
class EditorAudioBus : public PanelContainer {
|
||||
|
||||
GDCLASS( EditorAudioBus, PanelContainer )
|
||||
|
||||
bool prev_active;
|
||||
float peak_l;
|
||||
float peak_r;
|
||||
|
||||
Ref<Texture> disabled_vu;
|
||||
LineEdit *track_name;
|
||||
VSlider *slider;
|
||||
TextureProgress *vu_l;
|
||||
TextureProgress *vu_r;
|
||||
TextureRect *scale;
|
||||
OptionButton *send;
|
||||
|
||||
PopupMenu *effect_options;
|
||||
|
||||
Button *solo;
|
||||
Button *mute;
|
||||
Button *bypass;
|
||||
|
||||
Tree *effects;
|
||||
|
||||
bool updating_bus;
|
||||
|
||||
void _name_changed(const String& p_new_name);
|
||||
void _name_focus_exit() { _name_changed(track_name->get_text()); }
|
||||
void _volume_db_changed(float p_db);
|
||||
void _solo_toggled();
|
||||
void _mute_toggled();
|
||||
void _bypass_toggled();
|
||||
void _send_selected(int p_which);
|
||||
void _effect_edited();
|
||||
void _effect_add(int p_which);
|
||||
void _effect_selected();
|
||||
|
||||
friend class EditorAudioBuses;
|
||||
|
||||
EditorAudioBuses *buses;
|
||||
|
||||
protected:
|
||||
|
||||
static void _bind_methods();
|
||||
void _notification(int p_what);
|
||||
public:
|
||||
|
||||
void update_bus();
|
||||
void update_send();
|
||||
|
||||
EditorAudioBus(EditorAudioBuses *p_buses=NULL);
|
||||
};
|
||||
|
||||
|
||||
class EditorAudioBuses : public VBoxContainer {
|
||||
|
||||
GDCLASS(EditorAudioBuses,VBoxContainer)
|
||||
|
||||
HBoxContainer *top_hb;
|
||||
|
||||
Button *add;
|
||||
ToolButton *buses;
|
||||
ToolButton *groups;
|
||||
ScrollContainer *bus_scroll;
|
||||
HBoxContainer *bus_hb;
|
||||
ScrollContainer *group_scroll;
|
||||
HBoxContainer *group_hb;
|
||||
|
||||
void _add_bus();
|
||||
void _update_buses();
|
||||
void _update_bus(int p_index);
|
||||
void _update_sends();
|
||||
|
||||
|
||||
protected:
|
||||
|
||||
static void _bind_methods();
|
||||
void _notification(int p_what);
|
||||
public:
|
||||
|
||||
|
||||
|
||||
static void register_editor();
|
||||
|
||||
EditorAudioBuses();
|
||||
};
|
||||
|
||||
#endif // EDITORAUDIOBUSES_H
|
@ -115,6 +115,7 @@
|
||||
|
||||
#include "plugins/editor_preview_plugins.h"
|
||||
#include "editor_initialize_ssl.h"
|
||||
#include "editor_audio_buses.h"
|
||||
#include "script_editor_debugger.h"
|
||||
|
||||
EditorNode *EditorNode::singleton=NULL;
|
||||
@ -1937,7 +1938,7 @@ void EditorNode::_run(bool p_current,const String& p_custom) {
|
||||
|
||||
List<String> breakpoints;
|
||||
editor_data.get_editor_breakpoints(&breakpoints);
|
||||
|
||||
|
||||
args = GlobalConfig::get_singleton()->get("editor/main_run_args");
|
||||
|
||||
Error error = editor_run.run(run_filename,args,breakpoints,current_filename);
|
||||
@ -2802,10 +2803,10 @@ void EditorNode::_menu_option_confirm(int p_option,bool p_confirmed) {
|
||||
update_menu->get_popup()->set_item_checked(1,true);
|
||||
OS::get_singleton()->set_low_processor_usage_mode(true);
|
||||
} break;
|
||||
case SETTINGS_UPDATE_SPINNER_HIDE: {
|
||||
case SETTINGS_UPDATE_SPINNER_HIDE: {
|
||||
update_menu->set_icon(gui_base->get_icon("Collapse","EditorIcons"));
|
||||
update_menu->get_popup()->toggle_item_checked(3);
|
||||
} break;
|
||||
update_menu->get_popup()->toggle_item_checked(3);
|
||||
} break;
|
||||
case SETTINGS_PREFERENCES: {
|
||||
|
||||
settings_config_dialog->popup_edit_settings();
|
||||
@ -2930,16 +2931,7 @@ void EditorNode::_menu_option_confirm(int p_option,bool p_confirmed) {
|
||||
|
||||
default: {
|
||||
|
||||
if (p_option>=TOOL_MENU_BASE) {
|
||||
int idx = p_option - TOOL_MENU_BASE;
|
||||
|
||||
if (tool_menu_items[idx].submenu != "")
|
||||
break;
|
||||
|
||||
Object *handler = ObjectDB::get_instance(tool_menu_items[idx].handler);
|
||||
ERR_FAIL_COND(!handler);
|
||||
handler->call(tool_menu_items[idx].callback, tool_menu_items[idx].ud);
|
||||
} else if (p_option>=OBJECT_METHOD_BASE) {
|
||||
if (p_option>=OBJECT_METHOD_BASE) {
|
||||
|
||||
ERR_FAIL_COND(!current);
|
||||
|
||||
@ -5274,100 +5266,6 @@ void EditorNode::add_plugin_init_callback(EditorPluginInitializeCallback p_callb
|
||||
|
||||
EditorPluginInitializeCallback EditorNode::plugin_init_callbacks[EditorNode::MAX_INIT_CALLBACKS];
|
||||
|
||||
void EditorNode::_tool_menu_insert_item(const ToolMenuItem& p_item) {
|
||||
|
||||
int idx = tool_menu_items.size();
|
||||
|
||||
String cat;
|
||||
if (p_item.name.find("/") >= 0) {
|
||||
cat = p_item.name.get_slice("/", 0);
|
||||
} else {
|
||||
idx = 0;
|
||||
cat = "";
|
||||
}
|
||||
|
||||
for (int i = tool_menu_items.size() - 1; i >= 0; i--) {
|
||||
String name = tool_menu_items[i].name;
|
||||
|
||||
if (name.begins_with(cat) && (cat != "" || name.find("/") < 0)) {
|
||||
idx = i + 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
tool_menu_items.insert(idx, p_item);
|
||||
}
|
||||
|
||||
void EditorNode::_rebuild_tool_menu() const {
|
||||
|
||||
if (_initializing_tool_menu)
|
||||
return;
|
||||
|
||||
PopupMenu *menu = tool_menu->get_popup();
|
||||
menu->clear();
|
||||
|
||||
for (int i = 0; i < tool_menu_items.size(); i++) {
|
||||
menu->add_item(tool_menu_items[i].name.get_slice("/", 1), TOOL_MENU_BASE + i);
|
||||
|
||||
if (tool_menu_items[i].submenu != "")
|
||||
menu->set_item_submenu(i, tool_menu_items[i].submenu);
|
||||
}
|
||||
}
|
||||
|
||||
void EditorNode::add_tool_menu_item(const String& p_name, Object *p_handler, const String& p_callback, const Variant& p_ud) {
|
||||
|
||||
ERR_FAIL_COND(!p_handler);
|
||||
|
||||
ToolMenuItem tmi;
|
||||
tmi.name = p_name;
|
||||
tmi.submenu = "";
|
||||
tmi.ud = p_ud;
|
||||
tmi.handler = p_handler->get_instance_ID();
|
||||
tmi.callback = p_callback;
|
||||
_tool_menu_insert_item(tmi);
|
||||
|
||||
_rebuild_tool_menu();
|
||||
}
|
||||
|
||||
void EditorNode::add_tool_submenu_item(const String& p_name, PopupMenu *p_submenu) {
|
||||
|
||||
ERR_FAIL_COND(!p_submenu);
|
||||
ERR_FAIL_COND(p_submenu->get_parent() != NULL);
|
||||
|
||||
ToolMenuItem tmi;
|
||||
tmi.name = p_name;
|
||||
tmi.submenu = p_submenu->get_name();
|
||||
tmi.ud = Variant();
|
||||
tmi.handler = -1;
|
||||
tmi.callback = "";
|
||||
_tool_menu_insert_item(tmi);
|
||||
|
||||
tool_menu->get_popup()->add_child(p_submenu);
|
||||
|
||||
_rebuild_tool_menu();
|
||||
}
|
||||
|
||||
void EditorNode::remove_tool_menu_item(const String& p_name) {
|
||||
|
||||
for (int i = 0; i < tool_menu_items.size(); i++) {
|
||||
if (tool_menu_items[i].name == p_name) {
|
||||
String submenu = tool_menu_items[i].submenu;
|
||||
|
||||
if (submenu != "") {
|
||||
Node *n = tool_menu->get_popup()->get_node(submenu);
|
||||
|
||||
if (n) {
|
||||
tool_menu->get_popup()->remove_child(n);
|
||||
memdelete(n);
|
||||
}
|
||||
}
|
||||
|
||||
tool_menu_items.remove(i);
|
||||
}
|
||||
}
|
||||
|
||||
_rebuild_tool_menu();
|
||||
}
|
||||
|
||||
int EditorNode::build_callback_count=0;
|
||||
|
||||
@ -5511,8 +5409,6 @@ EditorNode::EditorNode() {
|
||||
docks_visible = true;
|
||||
|
||||
|
||||
_initializing_tool_menu = true;
|
||||
|
||||
FileAccess::set_backup_save(true);
|
||||
|
||||
PathRemap::get_singleton()->clear_remaps(); //editor uses no remaps
|
||||
@ -5972,9 +5868,10 @@ EditorNode::EditorNode() {
|
||||
|
||||
//tool_menu->set_icon(gui_base->get_icon("Save","EditorIcons"));
|
||||
left_menu_hb->add_child( tool_menu );
|
||||
tool_menu->get_popup()->connect("id_pressed", this, "_menu_option");
|
||||
|
||||
add_tool_menu_item(TTR("Orphan Resource Explorer"), this, "_menu_option", TOOLS_ORPHAN_RESOURCES);
|
||||
p=tool_menu->get_popup();
|
||||
p->connect("id_pressed",this,"_menu_option");
|
||||
p->add_item(TTR("Orphan Resource Explorer"),TOOLS_ORPHAN_RESOURCES);
|
||||
|
||||
export_button = memnew( ToolButton );
|
||||
export_button->set_tooltip(TTR("Export the project to many platforms."));
|
||||
@ -6658,6 +6555,9 @@ EditorNode::EditorNode() {
|
||||
add_editor_plugin( memnew( SpatialEditorPlugin(this) ) );
|
||||
add_editor_plugin( memnew( ScriptEditorPlugin(this) ) );
|
||||
|
||||
|
||||
EditorAudioBuses::register_editor();
|
||||
|
||||
ScriptTextEditor::register_editor(); //register one for text scripts
|
||||
|
||||
if (StreamPeerSSL::is_available()) {
|
||||
@ -6855,8 +6755,7 @@ EditorNode::EditorNode() {
|
||||
_initializing_addons=false;
|
||||
}
|
||||
|
||||
_initializing_tool_menu = false;
|
||||
_rebuild_tool_menu();
|
||||
|
||||
|
||||
_load_docks();
|
||||
|
||||
|
@ -136,7 +136,7 @@ void EditorPlugin::add_control_to_container(CustomControlContainer p_location,Co
|
||||
|
||||
void EditorPlugin::add_tool_menu_item(const String& p_name, Object *p_handler, const String& p_callback, const Variant& p_ud) {
|
||||
|
||||
EditorNode::get_singleton()->add_tool_menu_item(p_name, p_handler, p_callback, p_ud);
|
||||
//EditorNode::get_singleton()->add_tool_menu_item(p_name, p_handler, p_callback, p_ud);
|
||||
}
|
||||
|
||||
void EditorPlugin::add_tool_submenu_item(const String& p_name, Object *p_submenu) {
|
||||
@ -144,12 +144,12 @@ void EditorPlugin::add_tool_submenu_item(const String& p_name, Object *p_submenu
|
||||
ERR_FAIL_NULL(p_submenu);
|
||||
PopupMenu *submenu = p_submenu->cast_to<PopupMenu>();
|
||||
ERR_FAIL_NULL(submenu);
|
||||
EditorNode::get_singleton()->add_tool_submenu_item(p_name, submenu);
|
||||
//EditorNode::get_singleton()->add_tool_submenu_item(p_name, submenu);
|
||||
}
|
||||
|
||||
void EditorPlugin::remove_tool_menu_item(const String& p_name) {
|
||||
|
||||
EditorNode::get_singleton()->remove_tool_menu_item(p_name);
|
||||
//EditorNode::get_singleton()->remove_tool_menu_item(p_name);
|
||||
}
|
||||
|
||||
Ref<SpatialEditorGizmo> EditorPlugin::create_spatial_gizmo(Spatial* p_spatial) {
|
||||
@ -371,9 +371,9 @@ void EditorPlugin::_bind_methods() {
|
||||
ClassDB::bind_method(_MD("add_control_to_dock","slot","control:Control"),&EditorPlugin::add_control_to_dock);
|
||||
ClassDB::bind_method(_MD("remove_control_from_docks","control:Control"),&EditorPlugin::remove_control_from_docks);
|
||||
ClassDB::bind_method(_MD("remove_control_from_bottom_panel","control:Control"),&EditorPlugin::remove_control_from_bottom_panel);
|
||||
ClassDB::bind_method(_MD("add_tool_menu_item", "name", "handler", "callback", "ud"),&EditorPlugin::add_tool_menu_item,DEFVAL(Variant()));
|
||||
//ClassDB::bind_method(_MD("add_tool_menu_item", "name", "handler", "callback", "ud"),&EditorPlugin::add_tool_menu_item,DEFVAL(Variant()));
|
||||
ClassDB::bind_method(_MD("add_tool_submenu_item", "name", "submenu:PopupMenu"),&EditorPlugin::add_tool_submenu_item);
|
||||
ClassDB::bind_method(_MD("remove_tool_menu_item", "name"),&EditorPlugin::remove_tool_menu_item);
|
||||
//ClassDB::bind_method(_MD("remove_tool_menu_item", "name"),&EditorPlugin::remove_tool_menu_item);
|
||||
ClassDB::bind_method(_MD("add_custom_type","type","base","script:Script","icon:Texture"),&EditorPlugin::add_custom_type);
|
||||
ClassDB::bind_method(_MD("remove_custom_type","type"),&EditorPlugin::remove_custom_type);
|
||||
ClassDB::bind_method(_MD("get_editor_viewport:Control"), &EditorPlugin::get_editor_viewport);
|
||||
|
BIN
tools/editor/icons/icon_audio_effect_amplify.png
Normal file
After Width: | Height: | Size: 379 B |
BIN
tools/editor/icons/icon_bus_vu_db.png
Normal file
After Width: | Height: | Size: 1.1 KiB |
BIN
tools/editor/icons/icon_bus_vu_empty.png
Normal file
After Width: | Height: | Size: 1.6 KiB |
BIN
tools/editor/icons/icon_bus_vu_frozen.png
Normal file
After Width: | Height: | Size: 267 B |
BIN
tools/editor/icons/icon_bus_vu_full.png
Normal file
After Width: | Height: | Size: 2.4 KiB |
BIN
tools/editor/icons/icon_vu_db.png
Normal file
After Width: | Height: | Size: 1015 B |