/*************************************************************************/ /* audio_stream_player_3d.cpp */ /*************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ /* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ /* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* 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_player_3d.h" #include "engine.h" #include "scene/3d/area.h" #include "scene/3d/camera.h" #include "scene/main/viewport.h" void AudioStreamPlayer3D::_mix_audio() { if (!stream_playback.is_valid()) { return; } if (!active) { return; } bool started = false; if (setseek >= 0.0) { stream_playback->start(setseek); setseek = -1.0; //reset seek started = true; } //get data AudioFrame *buffer = mix_buffer.ptrw(); int buffer_size = mix_buffer.size(); //mix if (output_count > 0 || out_of_range_mode == OUT_OF_RANGE_MIX) { float output_pitch_scale = 0.0; if (output_count) { //used for doppler, not realistic but good enough for (int i = 0; i < output_count; i++) { output_pitch_scale += outputs[i].pitch_scale; } output_pitch_scale /= float(output_count); } else { output_pitch_scale = 1.0; } stream_playback->mix(buffer, pitch_scale * output_pitch_scale, buffer_size); } //write all outputs for (int i = 0; i < output_count; i++) { Output current = outputs[i]; //see if current output exists, to keep volume ramp bool found = false; for (int j = i; j < prev_output_count; j++) { if (prev_outputs[j].viewport == current.viewport) { if (j != i) { SWAP(prev_outputs[j], prev_outputs[i]); } found = true; break; } } bool interpolate_filter = !started; ; if (!found) { //create new if was not used before if (prev_output_count < MAX_OUTPUTS) { prev_outputs[prev_output_count] = prev_outputs[i]; //may be owned by another viewport prev_output_count++; } prev_outputs[i] = current; interpolate_filter = false; } //mix! int buffers = AudioServer::get_singleton()->get_channel_count(); for (int k = 0; k < buffers; k++) { AudioFrame vol_inc = (current.vol[k] - prev_outputs[i].vol[k]) / float(buffer_size); AudioFrame vol = current.vol[k]; AudioFrame *target = AudioServer::get_singleton()->thread_get_channel_mix_buffer(current.bus_index, k); current.filter.set_mode(AudioFilterSW::HIGHSHELF); current.filter.set_sampling_rate(AudioServer::get_singleton()->get_mix_rate()); current.filter.set_cutoff(attenuation_filter_cutoff_hz); current.filter.set_resonance(1); current.filter.set_stages(1); current.filter.set_gain(current.filter_gain); if (interpolate_filter) { current.filter_process[k * 2 + 0] = prev_outputs[i].filter_process[k * 2 + 0]; current.filter_process[k * 2 + 1] = prev_outputs[i].filter_process[k * 2 + 1]; current.filter_process[k * 2 + 0].set_filter(¤t.filter, false); current.filter_process[k * 2 + 1].set_filter(¤t.filter, false); current.filter_process[k * 2 + 0].update_coeffs(buffer_size); current.filter_process[k * 2 + 1].update_coeffs(buffer_size); for (int j = 0; j < buffer_size; j++) { AudioFrame f = buffer[j] * vol; current.filter_process[k * 2 + 0].process_one_interp(f.l); current.filter_process[k * 2 + 1].process_one_interp(f.r); target[j] += f; vol += vol_inc; } } else { current.filter_process[k * 2 + 0].set_filter(¤t.filter); current.filter_process[k * 2 + 1].set_filter(¤t.filter); current.filter_process[k * 2 + 0].update_coeffs(); current.filter_process[k * 2 + 1].update_coeffs(); for (int j = 0; j < buffer_size; j++) { AudioFrame f = buffer[j] * vol; current.filter_process[k * 2 + 0].process_one(f.l); current.filter_process[k * 2 + 1].process_one(f.r); target[j] += f; vol += vol_inc; } } if (current.reverb_bus_index >= 0) { AudioFrame *rtarget = AudioServer::get_singleton()->thread_get_channel_mix_buffer(current.reverb_bus_index, k); if (current.reverb_bus_index == prev_outputs[i].reverb_bus_index) { AudioFrame rvol_inc = (current.reverb_vol[k] - prev_outputs[i].reverb_vol[k]) / float(buffer_size); AudioFrame rvol = prev_outputs[i].reverb_vol[k]; for (int j = 0; j < buffer_size; j++) { rtarget[j] += buffer[j] * rvol; rvol += rvol_inc; } } else { AudioFrame rvol = current.reverb_vol[k]; for (int j = 0; j < buffer_size; j++) { rtarget[j] += buffer[j] * rvol; } } } } prev_outputs[i] = current; } prev_output_count = output_count; //stream is no longer active, disable this. if (!stream_playback->is_playing()) { active = false; } output_ready = false; } float AudioStreamPlayer3D::_get_attenuation_db(float p_distance) const { float att = 0; switch (attenuation_model) { case ATTENUATION_INVERSE_DISTANCE: { att = Math::linear2db(1.0 / ((p_distance / unit_size) + 000001)); } break; case ATTENUATION_INVERSE_SQUARE_DISTANCE: { float d = (p_distance / unit_size); d *= d; att = Math::linear2db(1.0 / (d + 0.00001)); } break; case ATTENUATION_LOGARITHMIC: { att = -20 * Math::log(p_distance / unit_size + 000001); } break; default: { ERR_PRINT("Unknown attenuation type"); break; } } att += unit_db; if (att > max_db) { att = max_db; } return att; } void _update_sound() { } void AudioStreamPlayer3D::_notification(int p_what) { if (p_what == NOTIFICATION_ENTER_TREE) { velocity_tracker->reset(get_global_transform().origin); AudioServer::get_singleton()->add_callback(_mix_audios, this); if (autoplay && !Engine::get_singleton()->is_editor_hint()) { play(); } } if (p_what == NOTIFICATION_EXIT_TREE) { AudioServer::get_singleton()->remove_callback(_mix_audios, this); } if (p_what == NOTIFICATION_TRANSFORM_CHANGED) { if (doppler_tracking != DOPPLER_TRACKING_DISABLED) { velocity_tracker->update_position(get_global_transform().origin); } } if (p_what == NOTIFICATION_INTERNAL_PHYSICS_PROCESS) { //update anything related to position first, if possible of course if (!output_ready) { Vector3 linear_velocity; //compute linear velocity for doppler if (doppler_tracking != DOPPLER_TRACKING_DISABLED) { linear_velocity = velocity_tracker->get_tracked_linear_velocity(); } Ref world = get_world(); ERR_FAIL_COND(world.is_null()); int new_output_count = 0; Vector3 global_pos = get_global_transform().origin; int bus_index = AudioServer::get_singleton()->thread_find_bus_index(bus); //check if any area is diverting sound into a bus PhysicsDirectSpaceState *space_state = PhysicsServer::get_singleton()->space_get_direct_state(world->get_space()); PhysicsDirectSpaceState::ShapeResult sr[MAX_INTERSECT_AREAS]; int areas = space_state->intersect_point(global_pos, sr, MAX_INTERSECT_AREAS, Set(), area_mask); Area *area = NULL; for (int i = 0; i < areas; i++) { if (!sr[i].collider) continue; Area *tarea = Object::cast_to(sr[i].collider); if (!tarea) continue; if (!tarea->is_overriding_audio_bus() && !tarea->is_using_reverb_bus()) continue; area = tarea; break; } List cameras; world->get_camera_list(&cameras); for (List::Element *E = cameras.front(); E; E = E->next()) { Camera *camera = E->get(); Viewport *vp = camera->get_viewport(); if (!vp->is_audio_listener()) continue; Vector3 local_pos = camera->get_global_transform().orthonormalized().affine_inverse().xform(global_pos); float dist = local_pos.length(); Vector3 area_sound_pos; Vector3 cam_area_pos; if (area && area->is_using_reverb_bus() && area->get_reverb_uniformity() > 0) { area_sound_pos = space_state->get_closest_point_to_object_volume(area->get_rid(), camera->get_global_transform().origin); cam_area_pos = camera->get_global_transform().affine_inverse().xform(area_sound_pos); } if (max_distance > 0) { float total_max = max_distance; if (area && area->is_using_reverb_bus() && area->get_reverb_uniformity() > 0) { total_max = MAX(total_max, cam_area_pos.length()); } if (total_max > max_distance) { continue; //can't hear this sound in this camera } } float multiplier = Math::db2linear(_get_attenuation_db(dist)); if (max_distance > 0) { multiplier *= MAX(0, 1.0 - (dist / max_distance)); } Output output; output.bus_index = bus_index; output.reverb_bus_index = -1; //no reverb by default output.viewport = vp; float db_att = (1.0 - MIN(1.0, multiplier)) * attenuation_filter_db; if (emission_angle_enabled) { Vector3 camtopos = global_pos - camera->get_global_transform().origin; float c = camtopos.normalized().dot(get_global_transform().basis.get_axis(2).normalized()); //it's z negative float angle = Math::rad2deg(Math::acos(c)); if (angle > emission_angle) db_att -= -emission_angle_filter_attenuation_db; } output.filter_gain = Math::db2linear(db_att); Vector3 flat_pos = local_pos; flat_pos.y = 0; flat_pos.normalize(); unsigned int cc = AudioServer::get_singleton()->get_channel_count(); if (cc == 1) { // Stereo pair float c = flat_pos.x * 0.5 + 0.5; output.vol[0].l = 1.0 - c; output.vol[0].r = c; } else { Vector3 camtopos = global_pos - camera->get_global_transform().origin; float c = camtopos.normalized().dot(get_global_transform().basis.get_axis(2).normalized()); //it's z negative float angle = Math::rad2deg(Math::acos(c)); float av = angle * (flat_pos.x < 0 ? -1 : 1) / 180.0; if (cc >= 1) { // Stereo pair float fl = Math::abs(1.0 - Math::abs(-0.8 - av)); float fr = Math::abs(1.0 - Math::abs(0.8 - av)); output.vol[0].l = fl; output.vol[0].r = fr; } if (cc >= 2) { // Center pair float center = 1.0 - Math::sin(Math::acos(c)); output.vol[1].l = center; output.vol[1].r = center; } if (cc >= 3) { // Side pair float sl = Math::abs(1.0 - Math::abs(-0.4 - av)); float sr = Math::abs(1.0 - Math::abs(0.4 - av)); output.vol[2].l = sl; output.vol[2].r = sr; } if (cc >= 4) { // Rear pair float rl = Math::abs(1.0 - Math::abs(-0.2 - av)); float rr = Math::abs(1.0 - Math::abs(0.2 - av)); output.vol[3].l = rl; output.vol[3].r = rr; } } for (int k = 0; k < cc; k++) { output.vol[k] *= multiplier; } bool filled_reverb = false; int vol_index_max = AudioServer::get_singleton()->get_speaker_mode() + 1; if (area) { if (area->is_overriding_audio_bus()) { //override audio bus StringName bus_name = area->get_audio_bus(); output.bus_index = AudioServer::get_singleton()->thread_find_bus_index(bus_name); } if (area->is_using_reverb_bus()) { filled_reverb = true; StringName bus_name = area->get_reverb_bus(); output.reverb_bus_index = AudioServer::get_singleton()->thread_find_bus_index(bus_name); float uniformity = area->get_reverb_uniformity(); float area_send = area->get_reverb_amount(); if (uniformity > 0.0) { float distance = cam_area_pos.length(); float attenuation = Math::db2linear(_get_attenuation_db(distance)); //float dist_att_db = -20 * Math::log(dist + 0.00001); //logarithmic attenuation, like in real life float center_val[3] = { 0.5, 0.25, 0.16666 }; AudioFrame center_frame(center_val[vol_index_max - 1], center_val[vol_index_max - 1]); if (attenuation < 1.0) { //pan the uniform sound Vector3 rev_pos = cam_area_pos; rev_pos.y = 0; rev_pos.normalize(); if (cc >= 1) { // Stereo pair float c = rev_pos.x * 0.5 + 0.5; output.reverb_vol[0].l = 1.0 - c; output.reverb_vol[0].r = c; } if (cc >= 3) { // Center pair + Side pair float xl = Vector3(-1, 0, -1).normalized().dot(rev_pos) * 0.5 + 0.5; float xr = Vector3(1, 0, -1).normalized().dot(rev_pos) * 0.5 + 0.5; output.reverb_vol[1].l = xl; output.reverb_vol[1].r = xr; output.reverb_vol[2].l = 1.0 - xr; output.reverb_vol[2].r = 1.0 - xl; } if (cc >= 4) { // Rear pair // FIXME: Not sure what math should be done here float c = rev_pos.x * 0.5 + 0.5; output.reverb_vol[3].l = 1.0 - c; output.reverb_vol[3].r = c; } for (int i = 0; i < vol_index_max; i++) { output.reverb_vol[i] = output.reverb_vol[i].linear_interpolate(center_frame, attenuation); } } else { for (int i = 0; i < vol_index_max; i++) { output.reverb_vol[i] = center_frame; } } for (int i = 0; i < vol_index_max; i++) { output.reverb_vol[i] = output.vol[i].linear_interpolate(output.reverb_vol[i] * attenuation, uniformity); output.reverb_vol[i] *= area_send; } } else { for (int i = 0; i < vol_index_max; i++) { output.reverb_vol[i] = output.vol[i] * area_send; } } } } if (doppler_tracking != DOPPLER_TRACKING_DISABLED) { Vector3 camera_velocity = camera->get_doppler_tracked_velocity(); Vector3 local_velocity = camera->get_global_transform().orthonormalized().basis.xform_inv(linear_velocity - camera_velocity); if (local_velocity == Vector3()) { output.pitch_scale = 1.0; } else { float approaching = local_pos.normalized().dot(local_velocity.normalized()); float velocity = local_velocity.length(); float speed_of_sound = 343.0; output.pitch_scale = speed_of_sound / (speed_of_sound + velocity * approaching); output.pitch_scale = CLAMP(output.pitch_scale, (1 / 8.0), 8.0); //avoid crazy stuff } } else { output.pitch_scale = 1.0; } if (!filled_reverb) { for (int i = 0; i < vol_index_max; i++) { output.reverb_vol[i] = AudioFrame(0, 0); } } outputs[new_output_count] = output; new_output_count++; if (new_output_count == MAX_OUTPUTS) break; } output_count = new_output_count; output_ready = true; } //start playing if requested if (setplay >= 0.0) { setseek = setplay; active = true; setplay = -1; //do not update, this makes it easier to animate (will shut off otherwise) ///_change_notify("playing"); //update property in editor } //stop playing if no longer active if (!active) { set_physics_process_internal(false); //do not update, this makes it easier to animate (will shut off otherwise) //_change_notify("playing"); //update property in editor emit_signal("finished"); } } } void AudioStreamPlayer3D::set_stream(Ref 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; } if (p_stream.is_valid()) { stream = p_stream; stream_playback = p_stream->instance_playback(); } AudioServer::get_singleton()->unlock(); if (p_stream.is_valid() && stream_playback.is_null()) { stream.unref(); } } Ref AudioStreamPlayer3D::get_stream() const { return stream; } void AudioStreamPlayer3D::set_unit_db(float p_volume) { unit_db = p_volume; } float AudioStreamPlayer3D::get_unit_db() const { return unit_db; } void AudioStreamPlayer3D::set_unit_size(float p_volume) { unit_size = p_volume; } float AudioStreamPlayer3D::get_unit_size() const { return unit_size; } void AudioStreamPlayer3D::set_max_db(float p_boost) { max_db = p_boost; } float AudioStreamPlayer3D::get_max_db() const { return max_db; } void AudioStreamPlayer3D::set_pitch_scale(float p_pitch_scale) { pitch_scale = p_pitch_scale; } float AudioStreamPlayer3D::get_pitch_scale() const { return pitch_scale; } void AudioStreamPlayer3D::play(float p_from_pos) { if (stream_playback.is_valid()) { setplay = p_from_pos; output_ready = false; set_physics_process_internal(true); } } void AudioStreamPlayer3D::seek(float p_seconds) { if (stream_playback.is_valid()) { setseek = p_seconds; } } void AudioStreamPlayer3D::stop() { if (stream_playback.is_valid()) { active = false; set_physics_process_internal(false); setplay = -1; } } bool AudioStreamPlayer3D::is_playing() const { if (stream_playback.is_valid()) { return active; // && stream_playback->is_playing(); } return false; } float AudioStreamPlayer3D::get_playback_position() { if (stream_playback.is_valid()) { return stream_playback->get_playback_position(); } return 0; } void AudioStreamPlayer3D::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 AudioStreamPlayer3D::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 AudioStreamPlayer3D::set_autoplay(bool p_enable) { autoplay = p_enable; } bool AudioStreamPlayer3D::is_autoplay_enabled() { return autoplay; } void AudioStreamPlayer3D::_set_playing(bool p_enable) { if (p_enable) play(); else stop(); } bool AudioStreamPlayer3D::_is_active() const { return active; } void AudioStreamPlayer3D::_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 AudioStreamPlayer3D::_bus_layout_changed() { _change_notify(); } void AudioStreamPlayer3D::set_max_distance(float p_metres) { ERR_FAIL_COND(p_metres < 0.0); max_distance = p_metres; } float AudioStreamPlayer3D::get_max_distance() const { return max_distance; } void AudioStreamPlayer3D::set_area_mask(uint32_t p_mask) { area_mask = p_mask; } uint32_t AudioStreamPlayer3D::get_area_mask() const { return area_mask; } void AudioStreamPlayer3D::set_emission_angle_enabled(bool p_enable) { emission_angle_enabled = p_enable; update_gizmo(); } bool AudioStreamPlayer3D::is_emission_angle_enabled() const { return emission_angle_enabled; } void AudioStreamPlayer3D::set_emission_angle(float p_angle) { ERR_FAIL_COND(p_angle < 0 || p_angle > 90); emission_angle = p_angle; update_gizmo(); } float AudioStreamPlayer3D::get_emission_angle() const { return emission_angle; } void AudioStreamPlayer3D::set_emission_angle_filter_attenuation_db(float p_angle_attenuation_db) { emission_angle_filter_attenuation_db = p_angle_attenuation_db; } float AudioStreamPlayer3D::get_emission_angle_filter_attenuation_db() const { return emission_angle_filter_attenuation_db; } void AudioStreamPlayer3D::set_attenuation_filter_cutoff_hz(float p_hz) { attenuation_filter_cutoff_hz = p_hz; } float AudioStreamPlayer3D::get_attenuation_filter_cutoff_hz() const { return attenuation_filter_cutoff_hz; } void AudioStreamPlayer3D::set_attenuation_filter_db(float p_db) { attenuation_filter_db = p_db; } float AudioStreamPlayer3D::get_attenuation_filter_db() const { return attenuation_filter_db; } void AudioStreamPlayer3D::set_attenuation_model(AttenuationModel p_model) { ERR_FAIL_INDEX(p_model, 3); attenuation_model = p_model; } AudioStreamPlayer3D::AttenuationModel AudioStreamPlayer3D::get_attenuation_model() const { return attenuation_model; } void AudioStreamPlayer3D::set_out_of_range_mode(OutOfRangeMode p_mode) { ERR_FAIL_INDEX(p_mode, 2); out_of_range_mode = p_mode; } AudioStreamPlayer3D::OutOfRangeMode AudioStreamPlayer3D::get_out_of_range_mode() const { return out_of_range_mode; } void AudioStreamPlayer3D::set_doppler_tracking(DopplerTracking p_tracking) { if (doppler_tracking == p_tracking) return; doppler_tracking = p_tracking; if (doppler_tracking != DOPPLER_TRACKING_DISABLED) { set_notify_transform(true); velocity_tracker->set_track_physics_step(doppler_tracking == DOPPLER_TRACKING_PHYSICS_STEP); velocity_tracker->reset(get_global_transform().origin); } else { set_notify_transform(false); } } AudioStreamPlayer3D::DopplerTracking AudioStreamPlayer3D::get_doppler_tracking() const { return doppler_tracking; } void AudioStreamPlayer3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_stream", "stream"), &AudioStreamPlayer3D::set_stream); ClassDB::bind_method(D_METHOD("get_stream"), &AudioStreamPlayer3D::get_stream); ClassDB::bind_method(D_METHOD("set_unit_db", "unit_db"), &AudioStreamPlayer3D::set_unit_db); ClassDB::bind_method(D_METHOD("get_unit_db"), &AudioStreamPlayer3D::get_unit_db); ClassDB::bind_method(D_METHOD("set_unit_size", "unit_size"), &AudioStreamPlayer3D::set_unit_size); ClassDB::bind_method(D_METHOD("get_unit_size"), &AudioStreamPlayer3D::get_unit_size); ClassDB::bind_method(D_METHOD("set_max_db", "max_db"), &AudioStreamPlayer3D::set_max_db); ClassDB::bind_method(D_METHOD("get_max_db"), &AudioStreamPlayer3D::get_max_db); ClassDB::bind_method(D_METHOD("set_pitch_scale", "pitch_scale"), &AudioStreamPlayer3D::set_pitch_scale); ClassDB::bind_method(D_METHOD("get_pitch_scale"), &AudioStreamPlayer3D::get_pitch_scale); ClassDB::bind_method(D_METHOD("play", "from_position"), &AudioStreamPlayer3D::play, DEFVAL(0.0)); ClassDB::bind_method(D_METHOD("seek", "to_position"), &AudioStreamPlayer3D::seek); ClassDB::bind_method(D_METHOD("stop"), &AudioStreamPlayer3D::stop); ClassDB::bind_method(D_METHOD("is_playing"), &AudioStreamPlayer3D::is_playing); ClassDB::bind_method(D_METHOD("get_playback_position"), &AudioStreamPlayer3D::get_playback_position); ClassDB::bind_method(D_METHOD("set_bus", "bus"), &AudioStreamPlayer3D::set_bus); ClassDB::bind_method(D_METHOD("get_bus"), &AudioStreamPlayer3D::get_bus); ClassDB::bind_method(D_METHOD("set_autoplay", "enable"), &AudioStreamPlayer3D::set_autoplay); ClassDB::bind_method(D_METHOD("is_autoplay_enabled"), &AudioStreamPlayer3D::is_autoplay_enabled); ClassDB::bind_method(D_METHOD("_set_playing", "enable"), &AudioStreamPlayer3D::_set_playing); ClassDB::bind_method(D_METHOD("_is_active"), &AudioStreamPlayer3D::_is_active); ClassDB::bind_method(D_METHOD("set_max_distance", "metres"), &AudioStreamPlayer3D::set_max_distance); ClassDB::bind_method(D_METHOD("get_max_distance"), &AudioStreamPlayer3D::get_max_distance); ClassDB::bind_method(D_METHOD("set_area_mask", "mask"), &AudioStreamPlayer3D::set_area_mask); ClassDB::bind_method(D_METHOD("get_area_mask"), &AudioStreamPlayer3D::get_area_mask); ClassDB::bind_method(D_METHOD("set_emission_angle", "degrees"), &AudioStreamPlayer3D::set_emission_angle); ClassDB::bind_method(D_METHOD("get_emission_angle"), &AudioStreamPlayer3D::get_emission_angle); ClassDB::bind_method(D_METHOD("set_emission_angle_enabled", "enabled"), &AudioStreamPlayer3D::set_emission_angle_enabled); ClassDB::bind_method(D_METHOD("is_emission_angle_enabled"), &AudioStreamPlayer3D::is_emission_angle_enabled); ClassDB::bind_method(D_METHOD("set_emission_angle_filter_attenuation_db", "db"), &AudioStreamPlayer3D::set_emission_angle_filter_attenuation_db); ClassDB::bind_method(D_METHOD("get_emission_angle_filter_attenuation_db"), &AudioStreamPlayer3D::get_emission_angle_filter_attenuation_db); ClassDB::bind_method(D_METHOD("set_attenuation_filter_cutoff_hz", "degrees"), &AudioStreamPlayer3D::set_attenuation_filter_cutoff_hz); ClassDB::bind_method(D_METHOD("get_attenuation_filter_cutoff_hz"), &AudioStreamPlayer3D::get_attenuation_filter_cutoff_hz); ClassDB::bind_method(D_METHOD("set_attenuation_filter_db", "db"), &AudioStreamPlayer3D::set_attenuation_filter_db); ClassDB::bind_method(D_METHOD("get_attenuation_filter_db"), &AudioStreamPlayer3D::get_attenuation_filter_db); ClassDB::bind_method(D_METHOD("set_attenuation_model", "model"), &AudioStreamPlayer3D::set_attenuation_model); ClassDB::bind_method(D_METHOD("get_attenuation_model"), &AudioStreamPlayer3D::get_attenuation_model); ClassDB::bind_method(D_METHOD("set_out_of_range_mode", "mode"), &AudioStreamPlayer3D::set_out_of_range_mode); ClassDB::bind_method(D_METHOD("get_out_of_range_mode"), &AudioStreamPlayer3D::get_out_of_range_mode); ClassDB::bind_method(D_METHOD("set_doppler_tracking", "mode"), &AudioStreamPlayer3D::set_doppler_tracking); ClassDB::bind_method(D_METHOD("get_doppler_tracking"), &AudioStreamPlayer3D::get_doppler_tracking); ClassDB::bind_method(D_METHOD("_bus_layout_changed"), &AudioStreamPlayer3D::_bus_layout_changed); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "stream", PROPERTY_HINT_RESOURCE_TYPE, "AudioStream"), "set_stream", "get_stream"); ADD_PROPERTY(PropertyInfo(Variant::INT, "attenuation_model", PROPERTY_HINT_ENUM, "Inverse,InverseSquare,Log"), "set_attenuation_model", "get_attenuation_model"); ADD_PROPERTY(PropertyInfo(Variant::REAL, "unit_db", PROPERTY_HINT_RANGE, "-80,80"), "set_unit_db", "get_unit_db"); ADD_PROPERTY(PropertyInfo(Variant::REAL, "unit_size", PROPERTY_HINT_RANGE, "0.1,100,0.1"), "set_unit_size", "get_unit_size"); ADD_PROPERTY(PropertyInfo(Variant::REAL, "max_db", PROPERTY_HINT_RANGE, "-24,6"), "set_max_db", "get_max_db"); ADD_PROPERTY(PropertyInfo(Variant::REAL, "pitch_scale", PROPERTY_HINT_RANGE, "0.01,32,0.01"), "set_pitch_scale", "get_pitch_scale"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "playing", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR), "_set_playing", "is_playing"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "autoplay"), "set_autoplay", "is_autoplay_enabled"); ADD_PROPERTY(PropertyInfo(Variant::REAL, "max_distance", PROPERTY_HINT_EXP_RANGE, "0,4096,1,or_greater"), "set_max_distance", "get_max_distance"); ADD_PROPERTY(PropertyInfo(Variant::INT, "out_of_range_mode", PROPERTY_HINT_ENUM, "Mix,Pause"), "set_out_of_range_mode", "get_out_of_range_mode"); ADD_PROPERTY(PropertyInfo(Variant::STRING, "bus", PROPERTY_HINT_ENUM, ""), "set_bus", "get_bus"); ADD_PROPERTY(PropertyInfo(Variant::INT, "area_mask", PROPERTY_HINT_LAYERS_2D_PHYSICS), "set_area_mask", "get_area_mask"); ADD_GROUP("Emission Angle", "emission_angle"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "emission_angle_enabled"), "set_emission_angle_enabled", "is_emission_angle_enabled"); ADD_PROPERTY(PropertyInfo(Variant::REAL, "emission_angle_degrees", PROPERTY_HINT_RANGE, "0.1,90,0.1"), "set_emission_angle", "get_emission_angle"); ADD_PROPERTY(PropertyInfo(Variant::REAL, "emission_angle_filter_attenuation_db", PROPERTY_HINT_RANGE, "-80,0,0.1"), "set_emission_angle_filter_attenuation_db", "get_emission_angle_filter_attenuation_db"); ADD_GROUP("Attenuation Filter", "attenuation_filter_"); ADD_PROPERTY(PropertyInfo(Variant::REAL, "attenuation_filter_cutoff_hz", PROPERTY_HINT_RANGE, "50,50000,1"), "set_attenuation_filter_cutoff_hz", "get_attenuation_filter_cutoff_hz"); ADD_PROPERTY(PropertyInfo(Variant::REAL, "attenuation_filter_db", PROPERTY_HINT_RANGE, "-80,0,0.1"), "set_attenuation_filter_db", "get_attenuation_filter_db"); ADD_GROUP("Doppler", "doppler_"); ADD_PROPERTY(PropertyInfo(Variant::INT, "doppler_tracking", PROPERTY_HINT_ENUM, "Disabled,Idle,Physics"), "set_doppler_tracking", "get_doppler_tracking"); BIND_ENUM_CONSTANT(ATTENUATION_INVERSE_DISTANCE); BIND_ENUM_CONSTANT(ATTENUATION_INVERSE_SQUARE_DISTANCE); BIND_ENUM_CONSTANT(ATTENUATION_LOGARITHMIC); BIND_ENUM_CONSTANT(OUT_OF_RANGE_MIX); BIND_ENUM_CONSTANT(OUT_OF_RANGE_PAUSE); BIND_ENUM_CONSTANT(DOPPLER_TRACKING_DISABLED); BIND_ENUM_CONSTANT(DOPPLER_TRACKING_IDLE_STEP); BIND_ENUM_CONSTANT(DOPPLER_TRACKING_PHYSICS_STEP); ADD_SIGNAL(MethodInfo("finished")); } AudioStreamPlayer3D::AudioStreamPlayer3D() { unit_db = 0; unit_size = 1; attenuation_model = ATTENUATION_INVERSE_DISTANCE; max_db = 3; pitch_scale = 1.0; autoplay = false; setseek = -1; active = false; output_count = 0; prev_output_count = 0; max_distance = 0; setplay = -1; output_ready = false; area_mask = 1; emission_angle = 45; emission_angle_enabled = false; emission_angle_filter_attenuation_db = -12; attenuation_filter_cutoff_hz = 5000; attenuation_filter_db = -24; out_of_range_mode = OUT_OF_RANGE_MIX; doppler_tracking = DOPPLER_TRACKING_DISABLED; velocity_tracker.instance(); AudioServer::get_singleton()->connect("bus_layout_changed", this, "_bus_layout_changed"); } AudioStreamPlayer3D::~AudioStreamPlayer3D() { }