WIP new AudioServer, with buses, effects, etc.

This commit is contained in:
Juan Linietsky 2017-01-21 19:00:25 -03:00
parent c4d6e54e93
commit 0aa7242624
55 changed files with 9486 additions and 249 deletions

View file

@ -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

View file

@ -486,6 +486,7 @@ Resource::Resource() {
#endif
subindex=0;
local_to_scene=false;
local_scene=NULL;
}

View file

@ -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
View 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")

View 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");
}

View 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

View file

@ -0,0 +1,7 @@
def can_build(platform):
return True
def configure(env):
pass

View file

@ -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 );
}

View 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();

View file

@ -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);

View 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() {
}

View 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

View file

@ -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);
}

View file

@ -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;
}

View file

@ -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);
}

View file

@ -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;

View file

@ -28,6 +28,7 @@
/*************************************************************************/
#include "video_player.h"
#include "os/os.h"
#include "servers/audio_server.h"
/*
int VideoPlayer::InternalStream::get_channel_count() const {

View file

@ -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();
}
}

View file

@ -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

View file

@ -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

View file

@ -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
};

Binary file not shown.

Before

Width:  |  Height:  |  Size: 554 B

After

Width:  |  Height:  |  Size: 394 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 701 B

After

Width:  |  Height:  |  Size: 439 B

View file

@ -5,3 +5,5 @@ Import('env')
env.add_source_files(env.servers_sources, "*.cpp")
Export('env')
SConscript("effects/SCsub")

View file

@ -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;
};

View file

@ -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();
};

View 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);
}
}
}

View file

@ -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;
};

View file

@ -0,0 +1,7 @@
#!/usr/bin/env python
Import('env')
env.add_source_files(env.servers_sources, "*.cpp")
Export('env')

View 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;
}

View 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

View 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);
}
}

View 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

View 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;
}

View 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

View 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;
}

View 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

View 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
View 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

View 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();
}

View 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

View file

@ -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);
}

View file

@ -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;

View file

@ -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

File diff suppressed because it is too large Load diff

View 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);
}

View 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

View file

@ -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();

View file

@ -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);

Binary file not shown.

After

Width:  |  Height:  |  Size: 379 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 267 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1,015 B