/*************************************************************************/ /* audio_server.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_server.h" #include "globals.h" #include "os/os.h" #include "servers/audio/effects/audio_effect_compressor.h" #include "io/resource_loader.h" #include "os/file_access.h" #ifdef TOOLS_ENABLED #define MARK_EDITED set_edited(true); #else #define MARK_EDITED #endif AudioDriver *AudioDriver::singleton=NULL; AudioDriver *AudioDriver::get_singleton() { return singleton; } void AudioDriver::set_singleton() { singleton=this; } void AudioDriver::audio_server_process(int p_frames,int32_t *p_buffer,bool p_update_mix_time) { if (p_update_mix_time) update_mix_time(p_frames); 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; _last_mix_time=OS::get_singleton()->get_ticks_usec(); } double AudioDriver::get_mix_time() const { double total = (OS::get_singleton()->get_ticks_usec() - _last_mix_time) / 1000000.0; total+=_mix_amount/(double)get_mix_rate(); return total; } AudioDriver::AudioDriver() { _last_mix_time=0; _mix_amount=0; } AudioDriver *AudioDriverManager::drivers[MAX_DRIVERS]; int AudioDriverManager::driver_count=0; void AudioDriverManager::add_driver(AudioDriver *p_driver) { ERR_FAIL_COND(driver_count>=MAX_DRIVERS); drivers[driver_count++]=p_driver; } int AudioDriverManager::get_driver_count() { return driver_count; } AudioDriver *AudioDriverManager::get_driver(int p_driver) { ERR_FAIL_INDEX_V(p_driver,driver_count,NULL); return drivers[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;kchannels[k].active) { AudioFrame *buf = master->channels[k].buffer.ptr(); for(int j=0;jindex_cache=i; //might be moved around by editor, so.. for(int k=0;kchannels.size();k++) { bus->channels[k].used=false; } } //make callbacks for mixing the audio for (Set::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;kchannels.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;jeffects.size();j++) { if (!bus->effects[j].enabled) continue; for(int k=0;kchannels.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;kchannels.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;kchannels.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;jpeak.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;jchannels.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;iindex_cache; } else { return 0; } } void AudioServer::set_bus_count(int p_count) { ERR_FAIL_COND(p_count<1); ERR_FAIL_INDEX(p_count,256); MARK_EDITED lock(); int cb = buses.size(); if (p_countname); memdelete(buses[i]); } } buses.resize(p_count); for(int i=cb;iname==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"); } void AudioServer::remove_bus(int p_index) { ERR_FAIL_INDEX(p_index,buses.size()); ERR_FAIL_COND(p_index==0); MARK_EDITED lock(); bus_map.erase(buses[p_index]->name); memdelete(buses[p_index]); buses.remove(p_index); unlock(); } void AudioServer::add_bus(int p_at_pos) { MARK_EDITED if (p_at_pos>=buses.size()) { p_at_pos=-1; } else if (p_at_pos==0) { if (buses.size()>1) p_at_pos=1; else p_at_pos=-1; } String attempt="New Bus"; int attempts=1; while(true) { bool name_free=true; for(int j=0;jname==attempt) { name_free=false; break; } } if (!name_free) { attempts++; attempt="New Bus " +itos(attempts); } else { break; } } Bus* bus =memnew(Bus); bus->channels.resize(_get_channel_count()); for(int j=0;j<_get_channel_count();j++) { bus->channels[j].buffer.resize(buffer_size); } bus->name=attempt; bus->solo=false; bus->mute=false; bus->bypass=false; bus->volume_db=0; bus_map[attempt]=bus; if (p_at_pos==-1) buses.push_back(bus); else buses.insert(p_at_pos,bus); } void AudioServer::move_bus(int p_bus,int p_to_pos) { ERR_FAIL_COND(p_bus<1 || p_bus>=buses.size()); ERR_FAIL_COND(p_to_pos!=-1 && (p_to_pos<1 || p_to_pos>buses.size())); MARK_EDITED if (p_bus==p_to_pos) return; Bus *bus = buses[p_bus]; buses.remove(p_bus); if (p_to_pos==-1) { buses.push_back(bus); } else if (p_to_posname==p_name) { unlock(); return; } String attempt=p_name; int attempts=1; while(true) { bool name_free=true; for(int i=0;iname==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; } void AudioServer::set_bus_volume_db(int p_bus,float p_volume_db) { ERR_FAIL_INDEX(p_bus,buses.size()); MARK_EDITED 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; } void AudioServer::set_bus_send(int p_bus,const StringName& p_send) { ERR_FAIL_INDEX(p_bus,buses.size()); MARK_EDITED 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()); MARK_EDITED 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()); MARK_EDITED 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()); MARK_EDITED 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;ichannels.size();i++) { buses[p_bus]->channels[i].effect_instances.resize(buses[p_bus]->effects.size()); for(int j=0;jeffects.size();j++) { Ref fx = buses[p_bus]->effects[j].effect->instance(); if (fx->cast_to()) { fx->cast_to()->set_current_channel(i); } buses[p_bus]->channels[i].effect_instances[j]=fx; } } } void AudioServer::add_bus_effect(int p_bus,const Ref& p_effect,int p_at_pos) { ERR_FAIL_COND(p_effect.is_null()); ERR_FAIL_INDEX(p_bus,buses.size()); MARK_EDITED lock(); Bus::Effect fx; fx.effect=p_effect; //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); } else { buses[p_bus]->effects.insert(p_at_pos,fx); } _update_bus_effects(p_bus); unlock(); } void AudioServer::remove_bus_effect(int p_bus,int p_effect) { ERR_FAIL_INDEX(p_bus,buses.size()); MARK_EDITED lock(); buses[p_bus]->effects.remove(p_effect); _update_bus_effects(p_bus); unlock(); } int AudioServer::get_bus_effect_count(int p_bus) { ERR_FAIL_INDEX_V(p_bus,buses.size(),0); return buses[p_bus]->effects.size(); } Ref AudioServer::get_bus_effect(int p_bus,int p_effect) { ERR_FAIL_INDEX_V(p_bus,buses.size(),Ref()); ERR_FAIL_INDEX_V(p_effect,buses[p_bus]->effects.size(),Ref()); 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()); MARK_EDITED lock(); 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()); MARK_EDITED 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; } 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;istart(); #ifdef TOOLS_ENABLED set_edited(false); //avoid editors from thinking this was edited #endif } void AudioServer::load_default_bus_layout() { if (FileAccess::exists("res://default_bus_layout.tres")) { Ref default_layout = ResourceLoader::load("res://default_bus_layout.tres"); if (default_layout.is_valid()) { set_bus_layout(default_layout); } } } void AudioServer::finish() { for(int i=0;ilock(); } void AudioServer::unlock() { AudioDriver::get_singleton()->unlock(); } AudioServer::SpeakerMode AudioServer::get_speaker_mode() const { return (AudioServer::SpeakerMode)AudioDriver::get_singleton()->get_speaker_mode(); } float AudioServer::get_mix_rate() const { return AudioDriver::get_singleton()->get_mix_rate(); } float AudioServer::read_output_peak_db() const { return 0; } AudioServer *AudioServer::get_singleton() { return singleton; } double AudioServer::get_mix_time() const { return 0; } double AudioServer::get_output_delay() const { return 0; } AudioServer* AudioServer::singleton=NULL; 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::set_bus_layout(const Ref &p_state) { ERR_FAIL_COND(p_state.is_null() || p_state->buses.size()==0); lock(); for(int i=0;ibuses.size()); bus_map.clear(); for(int i=0;ibuses.size();i++) { Bus * bus = memnew(Bus); if (i==0) { bus->name="Master"; } else { bus->name=p_state->buses[i].name; bus->send=p_state->buses[i].send; } bus->solo=p_state->buses[i].solo; bus->mute=p_state->buses[i].mute; bus->bypass=p_state->buses[i].bypass; bus->volume_db=p_state->buses[i].volume_db; for(int j=0;jbuses[i].effects.size();j++) { Ref fx = p_state->buses[i].effects[j].effect; if (fx.is_valid()) { Bus::Effect bfx; bfx.effect=fx; bfx.enabled=p_state->buses[i].effects[j].enabled; bus->effects.push_back(bfx); } } bus_map[bus->name]=bus; buses[i]=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); } _update_bus_effects(i); } #ifdef TOOLS_ENABLED set_edited(false); #endif unlock(); } Ref AudioServer::generate_bus_layout() const { Ref state; state.instance(); state->buses.resize( buses.size() ); for(int i=0;ibuses[i].name=buses[i]->name; state->buses[i].send=buses[i]->send; state->buses[i].mute=buses[i]->mute; state->buses[i].solo=buses[i]->solo; state->buses[i].bypass=buses[i]->bypass; state->buses[i].volume_db=buses[i]->volume_db; for(int j=0;jeffects.size();j++) { AudioBusLayout::Bus::Effect fx; fx.effect=buses[i]->effects[j].effect; fx.enabled=buses[i]->effects[j].enabled; state->buses[i].effects.push_back(fx); } } return state; } 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("remove_bus","index"),&AudioServer::remove_bus); ClassDB::bind_method(_MD("add_bus","at_pos"),&AudioServer::add_bus,DEFVAL(-1)); ClassDB::bind_method(_MD("move_bus","index","to_index"),&AudioServer::move_bus); 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,DEFVAL(-1)); 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); ClassDB::bind_method(_MD("set_state","state:AudioServerState"),&AudioServer::set_bus_layout); ClassDB::bind_method(_MD("generate_state:AudioServerState"),&AudioServer::generate_bus_layout); 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); } ///////////////////////////////// bool AudioBusLayout::_set(const StringName& p_name, const Variant& p_value) { String s = p_name; if (s.begins_with("bus/")) { int index = s.get_slice("/",1).to_int(); if (buses.size()<=index) { buses.resize(index+1); } Bus &bus = buses[index]; String what = s.get_slice("/",2); if (what=="name") { bus.name=p_value; } else if (what=="solo") { bus.solo=p_value; } else if (what=="mute") { bus.mute=p_value; } else if (what=="bypass_fx") { bus.bypass=p_value; } else if (what=="volume_db") { bus.volume_db=p_value; } else if (what=="send") { bus.send=p_value; } else if (what=="effect") { int which = s.get_slice("/",3).to_int(); if (bus.effects.size()<=which) { bus.effects.resize(which+1); } Bus::Effect &fx = bus.effects[which]; String fxwhat = s.get_slice("/",4); if (fxwhat=="effect") { fx.effect=p_value; } else if (fxwhat=="enabled") { fx.enabled=p_value; } else { return false; } return true; } else { return false; } return true; } return false; } bool AudioBusLayout::_get(const StringName& p_name,Variant &r_ret) const{ String s = p_name; if (s.begins_with("bus/")) { int index = s.get_slice("/",1).to_int(); if (index<0 || index>=buses.size()) return false; const Bus &bus = buses[index]; String what = s.get_slice("/",2); if (what=="name") { r_ret=bus.name; } else if (what=="solo") { r_ret=bus.solo; } else if (what=="mute") { r_ret=bus.mute; } else if (what=="bypass_fx") { r_ret=bus.bypass; } else if (what=="volume_db") { r_ret=bus.volume_db; } else if (what=="send") { r_ret=bus.send; } else if (what=="effect") { int which = s.get_slice("/",3).to_int(); if (which<0 || which>=bus.effects.size()) { return false; } const Bus::Effect &fx = bus.effects[which]; String fxwhat = s.get_slice("/",4); if (fxwhat=="effect") { r_ret=fx.effect; } else if (fxwhat=="enabled") { r_ret=fx.enabled; } else { return false; } return true; } else { return false; } return true; } return false; } void AudioBusLayout::_get_property_list( List *p_list) const{ for(int i=0;ipush_back(PropertyInfo(Variant::STRING,"bus/"+itos(i)+"/name",PROPERTY_HINT_NONE,"",PROPERTY_USAGE_NOEDITOR)); p_list->push_back(PropertyInfo(Variant::BOOL,"bus/"+itos(i)+"/solo",PROPERTY_HINT_NONE,"",PROPERTY_USAGE_NOEDITOR)); p_list->push_back(PropertyInfo(Variant::BOOL,"bus/"+itos(i)+"/mute",PROPERTY_HINT_NONE,"",PROPERTY_USAGE_NOEDITOR)); p_list->push_back(PropertyInfo(Variant::BOOL,"bus/"+itos(i)+"/bypass_fx",PROPERTY_HINT_NONE,"",PROPERTY_USAGE_NOEDITOR)); p_list->push_back(PropertyInfo(Variant::REAL,"bus/"+itos(i)+"/volume_db",PROPERTY_HINT_NONE,"",PROPERTY_USAGE_NOEDITOR)); p_list->push_back(PropertyInfo(Variant::REAL,"bus/"+itos(i)+"/send",PROPERTY_HINT_NONE,"",PROPERTY_USAGE_NOEDITOR)); for(int j=0;jpush_back(PropertyInfo(Variant::OBJECT,"bus/"+itos(i)+"/effect/"+itos(j)+"/effect",PROPERTY_HINT_NONE,"",PROPERTY_USAGE_NOEDITOR)); p_list->push_back(PropertyInfo(Variant::BOOL,"bus/"+itos(i)+"/effect/"+itos(j)+"/enabled",PROPERTY_HINT_NONE,"",PROPERTY_USAGE_NOEDITOR)); } } } AudioBusLayout::AudioBusLayout() { buses.resize(1); buses[0].name="Master"; }