| 1 | /**************************************************************************/ |
| 2 | /* audio_stream_player_3d.cpp */ |
| 3 | /**************************************************************************/ |
| 4 | /* This file is part of: */ |
| 5 | /* GODOT ENGINE */ |
| 6 | /* https://godotengine.org */ |
| 7 | /**************************************************************************/ |
| 8 | /* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ |
| 9 | /* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ |
| 10 | /* */ |
| 11 | /* Permission is hereby granted, free of charge, to any person obtaining */ |
| 12 | /* a copy of this software and associated documentation files (the */ |
| 13 | /* "Software"), to deal in the Software without restriction, including */ |
| 14 | /* without limitation the rights to use, copy, modify, merge, publish, */ |
| 15 | /* distribute, sublicense, and/or sell copies of the Software, and to */ |
| 16 | /* permit persons to whom the Software is furnished to do so, subject to */ |
| 17 | /* the following conditions: */ |
| 18 | /* */ |
| 19 | /* The above copyright notice and this permission notice shall be */ |
| 20 | /* included in all copies or substantial portions of the Software. */ |
| 21 | /* */ |
| 22 | /* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ |
| 23 | /* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ |
| 24 | /* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ |
| 25 | /* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ |
| 26 | /* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ |
| 27 | /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ |
| 28 | /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ |
| 29 | /**************************************************************************/ |
| 30 | |
| 31 | #include "audio_stream_player_3d.h" |
| 32 | |
| 33 | #include "core/config/project_settings.h" |
| 34 | #include "scene/3d/area_3d.h" |
| 35 | #include "scene/3d/audio_listener_3d.h" |
| 36 | #include "scene/3d/camera_3d.h" |
| 37 | #include "scene/main/viewport.h" |
| 38 | #include "scene/scene_string_names.h" |
| 39 | |
| 40 | // Based on "A Novel Multichannel Panning Method for Standard and Arbitrary Loudspeaker Configurations" by Ramy Sadek and Chris Kyriakakis (2004) |
| 41 | // Speaker-Placement Correction Amplitude Panning (SPCAP) |
| 42 | class Spcap { |
| 43 | private: |
| 44 | struct Speaker { |
| 45 | Vector3 direction; |
| 46 | real_t effective_number_of_speakers = 0; // precalculated |
| 47 | mutable real_t squared_gain = 0; // temporary |
| 48 | }; |
| 49 | |
| 50 | Vector<Speaker> speakers; |
| 51 | |
| 52 | public: |
| 53 | Spcap(unsigned int speaker_count, const Vector3 *speaker_directions) { |
| 54 | this->speakers.resize(speaker_count); |
| 55 | Speaker *w = this->speakers.ptrw(); |
| 56 | for (unsigned int speaker_num = 0; speaker_num < speaker_count; speaker_num++) { |
| 57 | w[speaker_num].direction = speaker_directions[speaker_num]; |
| 58 | w[speaker_num].squared_gain = 0.0; |
| 59 | w[speaker_num].effective_number_of_speakers = 0.0; |
| 60 | for (unsigned int other_speaker_num = 0; other_speaker_num < speaker_count; other_speaker_num++) { |
| 61 | w[speaker_num].effective_number_of_speakers += 0.5 * (1.0 + w[speaker_num].direction.dot(w[other_speaker_num].direction)); |
| 62 | } |
| 63 | } |
| 64 | } |
| 65 | |
| 66 | unsigned int get_speaker_count() const { |
| 67 | return (unsigned int)this->speakers.size(); |
| 68 | } |
| 69 | |
| 70 | Vector3 get_speaker_direction(unsigned int index) const { |
| 71 | return this->speakers.ptr()[index].direction; |
| 72 | } |
| 73 | |
| 74 | void calculate(const Vector3 &source_direction, real_t tightness, unsigned int volume_count, real_t *volumes) const { |
| 75 | const Speaker *r = this->speakers.ptr(); |
| 76 | real_t sum_squared_gains = 0.0; |
| 77 | for (unsigned int speaker_num = 0; speaker_num < (unsigned int)this->speakers.size(); speaker_num++) { |
| 78 | real_t initial_gain = 0.5 * powf(1.0 + r[speaker_num].direction.dot(source_direction), tightness) / r[speaker_num].effective_number_of_speakers; |
| 79 | r[speaker_num].squared_gain = initial_gain * initial_gain; |
| 80 | sum_squared_gains += r[speaker_num].squared_gain; |
| 81 | } |
| 82 | |
| 83 | for (unsigned int speaker_num = 0; speaker_num < MIN(volume_count, (unsigned int)this->speakers.size()); speaker_num++) { |
| 84 | volumes[speaker_num] = sqrtf(r[speaker_num].squared_gain / sum_squared_gains); |
| 85 | } |
| 86 | } |
| 87 | }; |
| 88 | |
| 89 | //TODO: hardcoded main speaker directions for 2, 3.1, 5.1 and 7.1 setups - these are simplified and could also be made configurable |
| 90 | static const Vector3 speaker_directions[7] = { |
| 91 | Vector3(-1.0, 0.0, -1.0).normalized(), // front-left |
| 92 | Vector3(1.0, 0.0, -1.0).normalized(), // front-right |
| 93 | Vector3(0.0, 0.0, -1.0).normalized(), // center |
| 94 | Vector3(-1.0, 0.0, 1.0).normalized(), // rear-left |
| 95 | Vector3(1.0, 0.0, 1.0).normalized(), // rear-right |
| 96 | Vector3(-1.0, 0.0, 0.0).normalized(), // side-left |
| 97 | Vector3(1.0, 0.0, 0.0).normalized(), // side-right |
| 98 | }; |
| 99 | |
| 100 | void AudioStreamPlayer3D::_calc_output_vol(const Vector3 &source_dir, real_t tightness, Vector<AudioFrame> &output) { |
| 101 | unsigned int speaker_count = 0; // only main speakers (no LFE) |
| 102 | switch (AudioServer::get_singleton()->get_speaker_mode()) { |
| 103 | case AudioServer::SPEAKER_MODE_STEREO: |
| 104 | speaker_count = 2; |
| 105 | break; |
| 106 | case AudioServer::SPEAKER_SURROUND_31: |
| 107 | speaker_count = 3; |
| 108 | break; |
| 109 | case AudioServer::SPEAKER_SURROUND_51: |
| 110 | speaker_count = 5; |
| 111 | break; |
| 112 | case AudioServer::SPEAKER_SURROUND_71: |
| 113 | speaker_count = 7; |
| 114 | break; |
| 115 | } |
| 116 | |
| 117 | Spcap spcap(speaker_count, speaker_directions); //TODO: should only be created/recreated once the speaker mode / speaker positions changes |
| 118 | real_t volumes[7]; |
| 119 | spcap.calculate(source_dir, tightness, speaker_count, volumes); |
| 120 | |
| 121 | switch (AudioServer::get_singleton()->get_speaker_mode()) { |
| 122 | case AudioServer::SPEAKER_SURROUND_71: |
| 123 | output.write[3].l = volumes[5]; // side-left |
| 124 | output.write[3].r = volumes[6]; // side-right |
| 125 | [[fallthrough]]; |
| 126 | case AudioServer::SPEAKER_SURROUND_51: |
| 127 | output.write[2].l = volumes[3]; // rear-left |
| 128 | output.write[2].r = volumes[4]; // rear-right |
| 129 | [[fallthrough]]; |
| 130 | case AudioServer::SPEAKER_SURROUND_31: |
| 131 | output.write[1].r = 1.0; // LFE - always full power |
| 132 | output.write[1].l = volumes[2]; // center |
| 133 | [[fallthrough]]; |
| 134 | case AudioServer::SPEAKER_MODE_STEREO: |
| 135 | output.write[0].r = volumes[1]; // front-right |
| 136 | output.write[0].l = volumes[0]; // front-left |
| 137 | break; |
| 138 | } |
| 139 | } |
| 140 | |
| 141 | void AudioStreamPlayer3D::_calc_reverb_vol(Area3D *area, Vector3 listener_area_pos, Vector<AudioFrame> direct_path_vol, Vector<AudioFrame> &reverb_vol) { |
| 142 | reverb_vol.resize(4); |
| 143 | reverb_vol.write[0] = AudioFrame(0, 0); |
| 144 | reverb_vol.write[1] = AudioFrame(0, 0); |
| 145 | reverb_vol.write[2] = AudioFrame(0, 0); |
| 146 | reverb_vol.write[3] = AudioFrame(0, 0); |
| 147 | |
| 148 | float uniformity = area->get_reverb_uniformity(); |
| 149 | float area_send = area->get_reverb_amount(); |
| 150 | |
| 151 | if (uniformity > 0.0) { |
| 152 | float distance = listener_area_pos.length(); |
| 153 | float attenuation = Math::db_to_linear(_get_attenuation_db(distance)); |
| 154 | |
| 155 | // Determine the fraction of sound that would come from each speaker if they were all driven uniformly. |
| 156 | float center_val[3] = { 0.5f, 0.25f, 0.16666f }; |
| 157 | int channel_count = AudioServer::get_singleton()->get_channel_count(); |
| 158 | AudioFrame center_frame(center_val[channel_count - 1], center_val[channel_count - 1]); |
| 159 | |
| 160 | if (attenuation < 1.0) { |
| 161 | //pan the uniform sound |
| 162 | Vector3 rev_pos = listener_area_pos; |
| 163 | rev_pos.y = 0; |
| 164 | rev_pos.normalize(); |
| 165 | |
| 166 | // Stereo pair. |
| 167 | float c = rev_pos.x * 0.5 + 0.5; |
| 168 | reverb_vol.write[0].l = 1.0 - c; |
| 169 | reverb_vol.write[0].r = c; |
| 170 | |
| 171 | if (channel_count >= 3) { |
| 172 | // Center pair + Side pair |
| 173 | float xl = Vector3(-1, 0, -1).normalized().dot(rev_pos) * 0.5 + 0.5; |
| 174 | float xr = Vector3(1, 0, -1).normalized().dot(rev_pos) * 0.5 + 0.5; |
| 175 | |
| 176 | reverb_vol.write[1].l = xl; |
| 177 | reverb_vol.write[1].r = xr; |
| 178 | reverb_vol.write[2].l = 1.0 - xr; |
| 179 | reverb_vol.write[2].r = 1.0 - xl; |
| 180 | } |
| 181 | |
| 182 | if (channel_count >= 4) { |
| 183 | // Rear pair |
| 184 | // FIXME: Not sure what math should be done here |
| 185 | reverb_vol.write[3].l = 1.0 - c; |
| 186 | reverb_vol.write[3].r = c; |
| 187 | } |
| 188 | |
| 189 | for (int i = 0; i < channel_count; i++) { |
| 190 | reverb_vol.write[i] = reverb_vol[i].lerp(center_frame, attenuation); |
| 191 | } |
| 192 | } else { |
| 193 | for (int i = 0; i < channel_count; i++) { |
| 194 | reverb_vol.write[i] = center_frame; |
| 195 | } |
| 196 | } |
| 197 | |
| 198 | for (int i = 0; i < channel_count; i++) { |
| 199 | reverb_vol.write[i] = direct_path_vol[i].lerp(reverb_vol[i] * attenuation, uniformity); |
| 200 | reverb_vol.write[i] *= area_send; |
| 201 | } |
| 202 | |
| 203 | } else { |
| 204 | for (int i = 0; i < 4; i++) { |
| 205 | reverb_vol.write[i] = direct_path_vol[i] * area_send; |
| 206 | } |
| 207 | } |
| 208 | } |
| 209 | |
| 210 | float AudioStreamPlayer3D::_get_attenuation_db(float p_distance) const { |
| 211 | float att = 0; |
| 212 | switch (attenuation_model) { |
| 213 | case ATTENUATION_INVERSE_DISTANCE: { |
| 214 | att = Math::linear_to_db(1.0 / ((p_distance / unit_size) + CMP_EPSILON)); |
| 215 | } break; |
| 216 | case ATTENUATION_INVERSE_SQUARE_DISTANCE: { |
| 217 | float d = (p_distance / unit_size); |
| 218 | d *= d; |
| 219 | att = Math::linear_to_db(1.0 / (d + CMP_EPSILON)); |
| 220 | } break; |
| 221 | case ATTENUATION_LOGARITHMIC: { |
| 222 | att = -20 * Math::log(p_distance / unit_size + CMP_EPSILON); |
| 223 | } break; |
| 224 | case ATTENUATION_DISABLED: |
| 225 | break; |
| 226 | default: { |
| 227 | ERR_PRINT("Unknown attenuation type" ); |
| 228 | break; |
| 229 | } |
| 230 | } |
| 231 | |
| 232 | att += volume_db; |
| 233 | if (att > max_db) { |
| 234 | att = max_db; |
| 235 | } |
| 236 | |
| 237 | return att; |
| 238 | } |
| 239 | |
| 240 | void AudioStreamPlayer3D::_notification(int p_what) { |
| 241 | switch (p_what) { |
| 242 | case NOTIFICATION_ENTER_TREE: { |
| 243 | velocity_tracker->reset(get_global_transform().origin); |
| 244 | AudioServer::get_singleton()->add_listener_changed_callback(_listener_changed_cb, this); |
| 245 | if (autoplay && !Engine::get_singleton()->is_editor_hint()) { |
| 246 | play(); |
| 247 | } |
| 248 | set_stream_paused(false); |
| 249 | } break; |
| 250 | |
| 251 | case NOTIFICATION_EXIT_TREE: { |
| 252 | set_stream_paused(true); |
| 253 | AudioServer::get_singleton()->remove_listener_changed_callback(_listener_changed_cb, this); |
| 254 | } break; |
| 255 | |
| 256 | case NOTIFICATION_PREDELETE: { |
| 257 | stop(); |
| 258 | } break; |
| 259 | |
| 260 | case NOTIFICATION_PAUSED: { |
| 261 | if (!can_process()) { |
| 262 | // Node can't process so we start fading out to silence. |
| 263 | set_stream_paused(true); |
| 264 | } |
| 265 | } break; |
| 266 | |
| 267 | case NOTIFICATION_UNPAUSED: { |
| 268 | set_stream_paused(false); |
| 269 | } break; |
| 270 | |
| 271 | case NOTIFICATION_TRANSFORM_CHANGED: { |
| 272 | if (doppler_tracking != DOPPLER_TRACKING_DISABLED) { |
| 273 | velocity_tracker->update_position(get_global_transform().origin); |
| 274 | } |
| 275 | } break; |
| 276 | |
| 277 | case NOTIFICATION_INTERNAL_PHYSICS_PROCESS: { |
| 278 | // Update anything related to position first, if possible of course. |
| 279 | Vector<AudioFrame> volume_vector; |
| 280 | if (setplay.get() > 0 || (active.is_set() && last_mix_count != AudioServer::get_singleton()->get_mix_count()) || force_update_panning) { |
| 281 | force_update_panning = false; |
| 282 | volume_vector = _update_panning(); |
| 283 | } |
| 284 | |
| 285 | if (setplayback.is_valid() && setplay.get() >= 0) { |
| 286 | active.set(); |
| 287 | HashMap<StringName, Vector<AudioFrame>> bus_map; |
| 288 | bus_map[_get_actual_bus()] = volume_vector; |
| 289 | AudioServer::get_singleton()->start_playback_stream(setplayback, bus_map, setplay.get(), actual_pitch_scale, linear_attenuation, attenuation_filter_cutoff_hz); |
| 290 | setplayback.unref(); |
| 291 | setplay.set(-1); |
| 292 | } |
| 293 | |
| 294 | if (!stream_playbacks.is_empty() && active.is_set()) { |
| 295 | // Stop playing if no longer active. |
| 296 | Vector<Ref<AudioStreamPlayback>> playbacks_to_remove; |
| 297 | for (Ref<AudioStreamPlayback> &playback : stream_playbacks) { |
| 298 | if (playback.is_valid() && !AudioServer::get_singleton()->is_playback_active(playback) && !AudioServer::get_singleton()->is_playback_paused(playback)) { |
| 299 | playbacks_to_remove.push_back(playback); |
| 300 | } |
| 301 | } |
| 302 | // Now go through and remove playbacks that have finished. Removing elements from a Vector in a range based for is asking for trouble. |
| 303 | for (Ref<AudioStreamPlayback> &playback : playbacks_to_remove) { |
| 304 | stream_playbacks.erase(playback); |
| 305 | } |
| 306 | if (!playbacks_to_remove.is_empty() && stream_playbacks.is_empty()) { |
| 307 | // This node is no longer actively playing audio. |
| 308 | active.clear(); |
| 309 | set_physics_process_internal(false); |
| 310 | } |
| 311 | if (!playbacks_to_remove.is_empty()) { |
| 312 | emit_signal(SNAME("finished" )); |
| 313 | } |
| 314 | } |
| 315 | |
| 316 | while (stream_playbacks.size() > max_polyphony) { |
| 317 | AudioServer::get_singleton()->stop_playback_stream(stream_playbacks[0]); |
| 318 | stream_playbacks.remove_at(0); |
| 319 | } |
| 320 | } break; |
| 321 | } |
| 322 | } |
| 323 | |
| 324 | // Interacts with PhysicsServer3D, so can only be called during _physics_process |
| 325 | Area3D *AudioStreamPlayer3D::_get_overriding_area() { |
| 326 | //check if any area is diverting sound into a bus |
| 327 | Ref<World3D> world_3d = get_world_3d(); |
| 328 | ERR_FAIL_COND_V(world_3d.is_null(), nullptr); |
| 329 | |
| 330 | Vector3 global_pos = get_global_transform().origin; |
| 331 | |
| 332 | PhysicsDirectSpaceState3D *space_state = PhysicsServer3D::get_singleton()->space_get_direct_state(world_3d->get_space()); |
| 333 | |
| 334 | PhysicsDirectSpaceState3D::ShapeResult sr[MAX_INTERSECT_AREAS]; |
| 335 | |
| 336 | PhysicsDirectSpaceState3D::PointParameters point_params; |
| 337 | point_params.position = global_pos; |
| 338 | point_params.collision_mask = area_mask; |
| 339 | point_params.collide_with_bodies = false; |
| 340 | point_params.collide_with_areas = true; |
| 341 | |
| 342 | int areas = space_state->intersect_point(point_params, sr, MAX_INTERSECT_AREAS); |
| 343 | |
| 344 | for (int i = 0; i < areas; i++) { |
| 345 | if (!sr[i].collider) { |
| 346 | continue; |
| 347 | } |
| 348 | |
| 349 | Area3D *tarea = Object::cast_to<Area3D>(sr[i].collider); |
| 350 | if (!tarea) { |
| 351 | continue; |
| 352 | } |
| 353 | |
| 354 | if (!tarea->is_overriding_audio_bus() && !tarea->is_using_reverb_bus()) { |
| 355 | continue; |
| 356 | } |
| 357 | |
| 358 | return tarea; |
| 359 | } |
| 360 | return nullptr; |
| 361 | } |
| 362 | |
| 363 | // Interacts with PhysicsServer3D, so can only be called during _physics_process |
| 364 | StringName AudioStreamPlayer3D::_get_actual_bus() { |
| 365 | Area3D *overriding_area = _get_overriding_area(); |
| 366 | if (overriding_area && overriding_area->is_overriding_audio_bus() && !overriding_area->is_using_reverb_bus()) { |
| 367 | return overriding_area->get_audio_bus_name(); |
| 368 | } |
| 369 | return bus; |
| 370 | } |
| 371 | |
| 372 | // Interacts with PhysicsServer3D, so can only be called during _physics_process |
| 373 | Vector<AudioFrame> AudioStreamPlayer3D::_update_panning() { |
| 374 | Vector<AudioFrame> output_volume_vector; |
| 375 | output_volume_vector.resize(4); |
| 376 | for (AudioFrame &frame : output_volume_vector) { |
| 377 | frame = AudioFrame(0, 0); |
| 378 | } |
| 379 | |
| 380 | if (!active.is_set() || stream.is_null()) { |
| 381 | return output_volume_vector; |
| 382 | } |
| 383 | |
| 384 | Vector3 linear_velocity; |
| 385 | |
| 386 | //compute linear velocity for doppler |
| 387 | if (doppler_tracking != DOPPLER_TRACKING_DISABLED) { |
| 388 | linear_velocity = velocity_tracker->get_tracked_linear_velocity(); |
| 389 | } |
| 390 | |
| 391 | Vector3 global_pos = get_global_transform().origin; |
| 392 | |
| 393 | Ref<World3D> world_3d = get_world_3d(); |
| 394 | ERR_FAIL_COND_V(world_3d.is_null(), output_volume_vector); |
| 395 | |
| 396 | HashSet<Camera3D *> cameras = world_3d->get_cameras(); |
| 397 | cameras.insert(get_viewport()->get_camera_3d()); |
| 398 | |
| 399 | PhysicsDirectSpaceState3D *space_state = PhysicsServer3D::get_singleton()->space_get_direct_state(world_3d->get_space()); |
| 400 | |
| 401 | for (Camera3D *camera : cameras) { |
| 402 | if (!camera) { |
| 403 | continue; |
| 404 | } |
| 405 | Viewport *vp = camera->get_viewport(); |
| 406 | if (!vp) { |
| 407 | continue; |
| 408 | } |
| 409 | if (!vp->is_audio_listener_3d()) { |
| 410 | continue; |
| 411 | } |
| 412 | |
| 413 | bool listener_is_camera = true; |
| 414 | Node3D *listener_node = camera; |
| 415 | |
| 416 | AudioListener3D *listener = vp->get_audio_listener_3d(); |
| 417 | if (listener) { |
| 418 | listener_node = listener; |
| 419 | listener_is_camera = false; |
| 420 | } |
| 421 | |
| 422 | Vector3 local_pos = listener_node->get_global_transform().orthonormalized().affine_inverse().xform(global_pos); |
| 423 | |
| 424 | float dist = local_pos.length(); |
| 425 | |
| 426 | Vector3 area_sound_pos; |
| 427 | Vector3 listener_area_pos; |
| 428 | |
| 429 | Area3D *area = _get_overriding_area(); |
| 430 | |
| 431 | if (area && area->is_using_reverb_bus() && area->get_reverb_uniformity() > 0) { |
| 432 | area_sound_pos = space_state->get_closest_point_to_object_volume(area->get_rid(), listener_node->get_global_transform().origin); |
| 433 | listener_area_pos = listener_node->get_global_transform().affine_inverse().xform(area_sound_pos); |
| 434 | } |
| 435 | |
| 436 | if (max_distance > 0) { |
| 437 | float total_max = max_distance; |
| 438 | |
| 439 | if (area && area->is_using_reverb_bus() && area->get_reverb_uniformity() > 0) { |
| 440 | total_max = MAX(total_max, listener_area_pos.length()); |
| 441 | } |
| 442 | if (total_max > max_distance) { |
| 443 | continue; //can't hear this sound in this listener |
| 444 | } |
| 445 | } |
| 446 | |
| 447 | float multiplier = Math::db_to_linear(_get_attenuation_db(dist)); |
| 448 | if (max_distance > 0) { |
| 449 | multiplier *= MAX(0, 1.0 - (dist / max_distance)); |
| 450 | } |
| 451 | |
| 452 | float db_att = (1.0 - MIN(1.0, multiplier)) * attenuation_filter_db; |
| 453 | |
| 454 | if (emission_angle_enabled) { |
| 455 | Vector3 listenertopos = global_pos - listener_node->get_global_transform().origin; |
| 456 | float c = listenertopos.normalized().dot(get_global_transform().basis.get_column(2).normalized()); //it's z negative |
| 457 | float angle = Math::rad_to_deg(Math::acos(c)); |
| 458 | if (angle > emission_angle) { |
| 459 | db_att -= -emission_angle_filter_attenuation_db; |
| 460 | } |
| 461 | } |
| 462 | |
| 463 | linear_attenuation = Math::db_to_linear(db_att); |
| 464 | for (Ref<AudioStreamPlayback> &playback : stream_playbacks) { |
| 465 | AudioServer::get_singleton()->set_playback_highshelf_params(playback, linear_attenuation, attenuation_filter_cutoff_hz); |
| 466 | } |
| 467 | // Bake in a constant factor here to allow the project setting defaults for 2d and 3d to be normalized to 1.0. |
| 468 | float tightness = cached_global_panning_strength * 2.0f; |
| 469 | tightness *= panning_strength; |
| 470 | _calc_output_vol(local_pos.normalized(), tightness, output_volume_vector); |
| 471 | |
| 472 | for (unsigned int k = 0; k < 4; k++) { |
| 473 | output_volume_vector.write[k] = multiplier * output_volume_vector[k]; |
| 474 | } |
| 475 | |
| 476 | HashMap<StringName, Vector<AudioFrame>> bus_volumes; |
| 477 | if (area) { |
| 478 | if (area->is_overriding_audio_bus()) { |
| 479 | //override audio bus |
| 480 | bus_volumes[area->get_audio_bus_name()] = output_volume_vector; |
| 481 | } |
| 482 | |
| 483 | if (area->is_using_reverb_bus()) { |
| 484 | StringName reverb_bus_name = area->get_reverb_bus_name(); |
| 485 | Vector<AudioFrame> reverb_vol; |
| 486 | _calc_reverb_vol(area, listener_area_pos, output_volume_vector, reverb_vol); |
| 487 | bus_volumes[reverb_bus_name] = reverb_vol; |
| 488 | } |
| 489 | } else { |
| 490 | bus_volumes[bus] = output_volume_vector; |
| 491 | } |
| 492 | |
| 493 | for (Ref<AudioStreamPlayback> &playback : stream_playbacks) { |
| 494 | AudioServer::get_singleton()->set_playback_bus_volumes_linear(playback, bus_volumes); |
| 495 | } |
| 496 | |
| 497 | if (doppler_tracking != DOPPLER_TRACKING_DISABLED) { |
| 498 | Vector3 listener_velocity; |
| 499 | |
| 500 | if (listener_is_camera) { |
| 501 | listener_velocity = camera->get_doppler_tracked_velocity(); |
| 502 | } |
| 503 | |
| 504 | Vector3 local_velocity = listener_node->get_global_transform().orthonormalized().basis.xform_inv(linear_velocity - listener_velocity); |
| 505 | |
| 506 | if (local_velocity != Vector3()) { |
| 507 | float approaching = local_pos.normalized().dot(local_velocity.normalized()); |
| 508 | float velocity = local_velocity.length(); |
| 509 | float speed_of_sound = 343.0; |
| 510 | |
| 511 | float doppler_pitch_scale = pitch_scale * speed_of_sound / (speed_of_sound + velocity * approaching); |
| 512 | doppler_pitch_scale = CLAMP(doppler_pitch_scale, (1 / 8.0), 8.0); //avoid crazy stuff |
| 513 | |
| 514 | actual_pitch_scale = doppler_pitch_scale; |
| 515 | } else { |
| 516 | actual_pitch_scale = pitch_scale; |
| 517 | } |
| 518 | } else { |
| 519 | actual_pitch_scale = pitch_scale; |
| 520 | } |
| 521 | for (Ref<AudioStreamPlayback> &playback : stream_playbacks) { |
| 522 | AudioServer::get_singleton()->set_playback_pitch_scale(playback, actual_pitch_scale); |
| 523 | } |
| 524 | } |
| 525 | return output_volume_vector; |
| 526 | } |
| 527 | |
| 528 | void AudioStreamPlayer3D::set_stream(Ref<AudioStream> p_stream) { |
| 529 | stop(); |
| 530 | stream = p_stream; |
| 531 | } |
| 532 | |
| 533 | Ref<AudioStream> AudioStreamPlayer3D::get_stream() const { |
| 534 | return stream; |
| 535 | } |
| 536 | |
| 537 | void AudioStreamPlayer3D::set_volume_db(float p_volume) { |
| 538 | volume_db = p_volume; |
| 539 | } |
| 540 | |
| 541 | float AudioStreamPlayer3D::get_volume_db() const { |
| 542 | return volume_db; |
| 543 | } |
| 544 | |
| 545 | void AudioStreamPlayer3D::set_unit_size(float p_volume) { |
| 546 | unit_size = p_volume; |
| 547 | update_gizmos(); |
| 548 | } |
| 549 | |
| 550 | float AudioStreamPlayer3D::get_unit_size() const { |
| 551 | return unit_size; |
| 552 | } |
| 553 | |
| 554 | void AudioStreamPlayer3D::set_max_db(float p_boost) { |
| 555 | max_db = p_boost; |
| 556 | } |
| 557 | |
| 558 | float AudioStreamPlayer3D::get_max_db() const { |
| 559 | return max_db; |
| 560 | } |
| 561 | |
| 562 | void AudioStreamPlayer3D::set_pitch_scale(float p_pitch_scale) { |
| 563 | ERR_FAIL_COND(!(p_pitch_scale > 0.0)); |
| 564 | pitch_scale = p_pitch_scale; |
| 565 | } |
| 566 | |
| 567 | float AudioStreamPlayer3D::get_pitch_scale() const { |
| 568 | return pitch_scale; |
| 569 | } |
| 570 | |
| 571 | void AudioStreamPlayer3D::play(float p_from_pos) { |
| 572 | if (stream.is_null()) { |
| 573 | return; |
| 574 | } |
| 575 | ERR_FAIL_COND_MSG(!is_inside_tree(), "Playback can only happen when a node is inside the scene tree" ); |
| 576 | if (stream->is_monophonic() && is_playing()) { |
| 577 | stop(); |
| 578 | } |
| 579 | Ref<AudioStreamPlayback> stream_playback = stream->instantiate_playback(); |
| 580 | ERR_FAIL_COND_MSG(stream_playback.is_null(), "Failed to instantiate playback." ); |
| 581 | |
| 582 | stream_playbacks.push_back(stream_playback); |
| 583 | setplayback = stream_playback; |
| 584 | setplay.set(p_from_pos); |
| 585 | active.set(); |
| 586 | set_physics_process_internal(true); |
| 587 | } |
| 588 | |
| 589 | void AudioStreamPlayer3D::seek(float p_seconds) { |
| 590 | if (is_playing()) { |
| 591 | stop(); |
| 592 | play(p_seconds); |
| 593 | } |
| 594 | } |
| 595 | |
| 596 | void AudioStreamPlayer3D::stop() { |
| 597 | setplay.set(-1); |
| 598 | for (Ref<AudioStreamPlayback> &playback : stream_playbacks) { |
| 599 | AudioServer::get_singleton()->stop_playback_stream(playback); |
| 600 | } |
| 601 | stream_playbacks.clear(); |
| 602 | active.clear(); |
| 603 | set_physics_process_internal(false); |
| 604 | } |
| 605 | |
| 606 | bool AudioStreamPlayer3D::is_playing() const { |
| 607 | for (const Ref<AudioStreamPlayback> &playback : stream_playbacks) { |
| 608 | if (AudioServer::get_singleton()->is_playback_active(playback)) { |
| 609 | return true; |
| 610 | } |
| 611 | } |
| 612 | if (setplay.get() >= 0) { |
| 613 | return true; // play() has been called this frame, but no playback exists just yet. |
| 614 | } |
| 615 | return false; |
| 616 | } |
| 617 | |
| 618 | float AudioStreamPlayer3D::get_playback_position() { |
| 619 | // Return the playback position of the most recently started playback stream. |
| 620 | if (!stream_playbacks.is_empty()) { |
| 621 | return AudioServer::get_singleton()->get_playback_position(stream_playbacks[stream_playbacks.size() - 1]); |
| 622 | } |
| 623 | return 0; |
| 624 | } |
| 625 | |
| 626 | void AudioStreamPlayer3D::set_bus(const StringName &p_bus) { |
| 627 | //if audio is active, must lock this |
| 628 | AudioServer::get_singleton()->lock(); |
| 629 | bus = p_bus; |
| 630 | AudioServer::get_singleton()->unlock(); |
| 631 | } |
| 632 | |
| 633 | StringName AudioStreamPlayer3D::get_bus() const { |
| 634 | for (int i = 0; i < AudioServer::get_singleton()->get_bus_count(); i++) { |
| 635 | if (AudioServer::get_singleton()->get_bus_name(i) == bus) { |
| 636 | return bus; |
| 637 | } |
| 638 | } |
| 639 | return SceneStringNames::get_singleton()->Master; |
| 640 | } |
| 641 | |
| 642 | void AudioStreamPlayer3D::set_autoplay(bool p_enable) { |
| 643 | autoplay = p_enable; |
| 644 | } |
| 645 | |
| 646 | bool AudioStreamPlayer3D::is_autoplay_enabled() { |
| 647 | return autoplay; |
| 648 | } |
| 649 | |
| 650 | void AudioStreamPlayer3D::_set_playing(bool p_enable) { |
| 651 | if (p_enable) { |
| 652 | play(); |
| 653 | } else { |
| 654 | stop(); |
| 655 | } |
| 656 | } |
| 657 | |
| 658 | bool AudioStreamPlayer3D::_is_active() const { |
| 659 | return active.is_set(); |
| 660 | } |
| 661 | |
| 662 | void AudioStreamPlayer3D::_validate_property(PropertyInfo &p_property) const { |
| 663 | if (p_property.name == "bus" ) { |
| 664 | String options; |
| 665 | for (int i = 0; i < AudioServer::get_singleton()->get_bus_count(); i++) { |
| 666 | if (i > 0) { |
| 667 | options += "," ; |
| 668 | } |
| 669 | String name = AudioServer::get_singleton()->get_bus_name(i); |
| 670 | options += name; |
| 671 | } |
| 672 | |
| 673 | p_property.hint_string = options; |
| 674 | } |
| 675 | } |
| 676 | |
| 677 | void AudioStreamPlayer3D::_bus_layout_changed() { |
| 678 | notify_property_list_changed(); |
| 679 | } |
| 680 | |
| 681 | void AudioStreamPlayer3D::set_max_distance(float p_metres) { |
| 682 | ERR_FAIL_COND(p_metres < 0.0); |
| 683 | max_distance = p_metres; |
| 684 | update_gizmos(); |
| 685 | } |
| 686 | |
| 687 | float AudioStreamPlayer3D::get_max_distance() const { |
| 688 | return max_distance; |
| 689 | } |
| 690 | |
| 691 | void AudioStreamPlayer3D::set_area_mask(uint32_t p_mask) { |
| 692 | area_mask = p_mask; |
| 693 | } |
| 694 | |
| 695 | uint32_t AudioStreamPlayer3D::get_area_mask() const { |
| 696 | return area_mask; |
| 697 | } |
| 698 | |
| 699 | void AudioStreamPlayer3D::set_emission_angle_enabled(bool p_enable) { |
| 700 | emission_angle_enabled = p_enable; |
| 701 | update_gizmos(); |
| 702 | } |
| 703 | |
| 704 | bool AudioStreamPlayer3D::is_emission_angle_enabled() const { |
| 705 | return emission_angle_enabled; |
| 706 | } |
| 707 | |
| 708 | void AudioStreamPlayer3D::set_emission_angle(float p_angle) { |
| 709 | ERR_FAIL_COND(p_angle < 0 || p_angle > 90); |
| 710 | emission_angle = p_angle; |
| 711 | update_gizmos(); |
| 712 | } |
| 713 | |
| 714 | float AudioStreamPlayer3D::get_emission_angle() const { |
| 715 | return emission_angle; |
| 716 | } |
| 717 | |
| 718 | void AudioStreamPlayer3D::set_emission_angle_filter_attenuation_db(float p_angle_attenuation_db) { |
| 719 | emission_angle_filter_attenuation_db = p_angle_attenuation_db; |
| 720 | } |
| 721 | |
| 722 | float AudioStreamPlayer3D::get_emission_angle_filter_attenuation_db() const { |
| 723 | return emission_angle_filter_attenuation_db; |
| 724 | } |
| 725 | |
| 726 | void AudioStreamPlayer3D::set_attenuation_filter_cutoff_hz(float p_hz) { |
| 727 | attenuation_filter_cutoff_hz = p_hz; |
| 728 | } |
| 729 | |
| 730 | float AudioStreamPlayer3D::get_attenuation_filter_cutoff_hz() const { |
| 731 | return attenuation_filter_cutoff_hz; |
| 732 | } |
| 733 | |
| 734 | void AudioStreamPlayer3D::set_attenuation_filter_db(float p_db) { |
| 735 | attenuation_filter_db = p_db; |
| 736 | } |
| 737 | |
| 738 | float AudioStreamPlayer3D::get_attenuation_filter_db() const { |
| 739 | return attenuation_filter_db; |
| 740 | } |
| 741 | |
| 742 | void AudioStreamPlayer3D::set_attenuation_model(AttenuationModel p_model) { |
| 743 | ERR_FAIL_INDEX((int)p_model, 4); |
| 744 | attenuation_model = p_model; |
| 745 | update_gizmos(); |
| 746 | } |
| 747 | |
| 748 | AudioStreamPlayer3D::AttenuationModel AudioStreamPlayer3D::get_attenuation_model() const { |
| 749 | return attenuation_model; |
| 750 | } |
| 751 | |
| 752 | void AudioStreamPlayer3D::set_doppler_tracking(DopplerTracking p_tracking) { |
| 753 | if (doppler_tracking == p_tracking) { |
| 754 | return; |
| 755 | } |
| 756 | |
| 757 | doppler_tracking = p_tracking; |
| 758 | |
| 759 | if (doppler_tracking != DOPPLER_TRACKING_DISABLED) { |
| 760 | set_notify_transform(true); |
| 761 | velocity_tracker->set_track_physics_step(doppler_tracking == DOPPLER_TRACKING_PHYSICS_STEP); |
| 762 | if (is_inside_tree()) { |
| 763 | velocity_tracker->reset(get_global_transform().origin); |
| 764 | } |
| 765 | } else { |
| 766 | set_notify_transform(false); |
| 767 | } |
| 768 | } |
| 769 | |
| 770 | AudioStreamPlayer3D::DopplerTracking AudioStreamPlayer3D::get_doppler_tracking() const { |
| 771 | return doppler_tracking; |
| 772 | } |
| 773 | |
| 774 | void AudioStreamPlayer3D::set_stream_paused(bool p_pause) { |
| 775 | // TODO this does not have perfect recall, fix that maybe? If there are zero playbacks registered with the AudioServer, this bool isn't persisted. |
| 776 | for (Ref<AudioStreamPlayback> &playback : stream_playbacks) { |
| 777 | AudioServer::get_singleton()->set_playback_paused(playback, p_pause); |
| 778 | } |
| 779 | } |
| 780 | |
| 781 | bool AudioStreamPlayer3D::get_stream_paused() const { |
| 782 | // There's currently no way to pause some playback streams but not others. Check the first and don't bother looking at the rest. |
| 783 | if (!stream_playbacks.is_empty()) { |
| 784 | return AudioServer::get_singleton()->is_playback_paused(stream_playbacks[0]); |
| 785 | } |
| 786 | return false; |
| 787 | } |
| 788 | |
| 789 | bool AudioStreamPlayer3D::has_stream_playback() { |
| 790 | return !stream_playbacks.is_empty(); |
| 791 | } |
| 792 | |
| 793 | Ref<AudioStreamPlayback> AudioStreamPlayer3D::get_stream_playback() { |
| 794 | ERR_FAIL_COND_V_MSG(stream_playbacks.is_empty(), Ref<AudioStreamPlayback>(), "Player is inactive. Call play() before requesting get_stream_playback()." ); |
| 795 | return stream_playbacks[stream_playbacks.size() - 1]; |
| 796 | } |
| 797 | |
| 798 | void AudioStreamPlayer3D::set_max_polyphony(int p_max_polyphony) { |
| 799 | if (p_max_polyphony > 0) { |
| 800 | max_polyphony = p_max_polyphony; |
| 801 | } |
| 802 | } |
| 803 | |
| 804 | int AudioStreamPlayer3D::get_max_polyphony() const { |
| 805 | return max_polyphony; |
| 806 | } |
| 807 | |
| 808 | void AudioStreamPlayer3D::set_panning_strength(float p_panning_strength) { |
| 809 | ERR_FAIL_COND_MSG(p_panning_strength < 0, "Panning strength must be a positive number." ); |
| 810 | panning_strength = p_panning_strength; |
| 811 | } |
| 812 | |
| 813 | float AudioStreamPlayer3D::get_panning_strength() const { |
| 814 | return panning_strength; |
| 815 | } |
| 816 | |
| 817 | void AudioStreamPlayer3D::_bind_methods() { |
| 818 | ClassDB::bind_method(D_METHOD("set_stream" , "stream" ), &AudioStreamPlayer3D::set_stream); |
| 819 | ClassDB::bind_method(D_METHOD("get_stream" ), &AudioStreamPlayer3D::get_stream); |
| 820 | |
| 821 | ClassDB::bind_method(D_METHOD("set_volume_db" , "volume_db" ), &AudioStreamPlayer3D::set_volume_db); |
| 822 | ClassDB::bind_method(D_METHOD("get_volume_db" ), &AudioStreamPlayer3D::get_volume_db); |
| 823 | |
| 824 | ClassDB::bind_method(D_METHOD("set_unit_size" , "unit_size" ), &AudioStreamPlayer3D::set_unit_size); |
| 825 | ClassDB::bind_method(D_METHOD("get_unit_size" ), &AudioStreamPlayer3D::get_unit_size); |
| 826 | |
| 827 | ClassDB::bind_method(D_METHOD("set_max_db" , "max_db" ), &AudioStreamPlayer3D::set_max_db); |
| 828 | ClassDB::bind_method(D_METHOD("get_max_db" ), &AudioStreamPlayer3D::get_max_db); |
| 829 | |
| 830 | ClassDB::bind_method(D_METHOD("set_pitch_scale" , "pitch_scale" ), &AudioStreamPlayer3D::set_pitch_scale); |
| 831 | ClassDB::bind_method(D_METHOD("get_pitch_scale" ), &AudioStreamPlayer3D::get_pitch_scale); |
| 832 | |
| 833 | ClassDB::bind_method(D_METHOD("play" , "from_position" ), &AudioStreamPlayer3D::play, DEFVAL(0.0)); |
| 834 | ClassDB::bind_method(D_METHOD("seek" , "to_position" ), &AudioStreamPlayer3D::seek); |
| 835 | ClassDB::bind_method(D_METHOD("stop" ), &AudioStreamPlayer3D::stop); |
| 836 | |
| 837 | ClassDB::bind_method(D_METHOD("is_playing" ), &AudioStreamPlayer3D::is_playing); |
| 838 | ClassDB::bind_method(D_METHOD("get_playback_position" ), &AudioStreamPlayer3D::get_playback_position); |
| 839 | |
| 840 | ClassDB::bind_method(D_METHOD("set_bus" , "bus" ), &AudioStreamPlayer3D::set_bus); |
| 841 | ClassDB::bind_method(D_METHOD("get_bus" ), &AudioStreamPlayer3D::get_bus); |
| 842 | |
| 843 | ClassDB::bind_method(D_METHOD("set_autoplay" , "enable" ), &AudioStreamPlayer3D::set_autoplay); |
| 844 | ClassDB::bind_method(D_METHOD("is_autoplay_enabled" ), &AudioStreamPlayer3D::is_autoplay_enabled); |
| 845 | |
| 846 | ClassDB::bind_method(D_METHOD("_set_playing" , "enable" ), &AudioStreamPlayer3D::_set_playing); |
| 847 | ClassDB::bind_method(D_METHOD("_is_active" ), &AudioStreamPlayer3D::_is_active); |
| 848 | |
| 849 | ClassDB::bind_method(D_METHOD("set_max_distance" , "meters" ), &AudioStreamPlayer3D::set_max_distance); |
| 850 | ClassDB::bind_method(D_METHOD("get_max_distance" ), &AudioStreamPlayer3D::get_max_distance); |
| 851 | |
| 852 | ClassDB::bind_method(D_METHOD("set_area_mask" , "mask" ), &AudioStreamPlayer3D::set_area_mask); |
| 853 | ClassDB::bind_method(D_METHOD("get_area_mask" ), &AudioStreamPlayer3D::get_area_mask); |
| 854 | |
| 855 | ClassDB::bind_method(D_METHOD("set_emission_angle" , "degrees" ), &AudioStreamPlayer3D::set_emission_angle); |
| 856 | ClassDB::bind_method(D_METHOD("get_emission_angle" ), &AudioStreamPlayer3D::get_emission_angle); |
| 857 | |
| 858 | ClassDB::bind_method(D_METHOD("set_emission_angle_enabled" , "enabled" ), &AudioStreamPlayer3D::set_emission_angle_enabled); |
| 859 | ClassDB::bind_method(D_METHOD("is_emission_angle_enabled" ), &AudioStreamPlayer3D::is_emission_angle_enabled); |
| 860 | |
| 861 | ClassDB::bind_method(D_METHOD("set_emission_angle_filter_attenuation_db" , "db" ), &AudioStreamPlayer3D::set_emission_angle_filter_attenuation_db); |
| 862 | ClassDB::bind_method(D_METHOD("get_emission_angle_filter_attenuation_db" ), &AudioStreamPlayer3D::get_emission_angle_filter_attenuation_db); |
| 863 | |
| 864 | ClassDB::bind_method(D_METHOD("set_attenuation_filter_cutoff_hz" , "degrees" ), &AudioStreamPlayer3D::set_attenuation_filter_cutoff_hz); |
| 865 | ClassDB::bind_method(D_METHOD("get_attenuation_filter_cutoff_hz" ), &AudioStreamPlayer3D::get_attenuation_filter_cutoff_hz); |
| 866 | |
| 867 | ClassDB::bind_method(D_METHOD("set_attenuation_filter_db" , "db" ), &AudioStreamPlayer3D::set_attenuation_filter_db); |
| 868 | ClassDB::bind_method(D_METHOD("get_attenuation_filter_db" ), &AudioStreamPlayer3D::get_attenuation_filter_db); |
| 869 | |
| 870 | ClassDB::bind_method(D_METHOD("set_attenuation_model" , "model" ), &AudioStreamPlayer3D::set_attenuation_model); |
| 871 | ClassDB::bind_method(D_METHOD("get_attenuation_model" ), &AudioStreamPlayer3D::get_attenuation_model); |
| 872 | |
| 873 | ClassDB::bind_method(D_METHOD("set_doppler_tracking" , "mode" ), &AudioStreamPlayer3D::set_doppler_tracking); |
| 874 | ClassDB::bind_method(D_METHOD("get_doppler_tracking" ), &AudioStreamPlayer3D::get_doppler_tracking); |
| 875 | |
| 876 | ClassDB::bind_method(D_METHOD("set_stream_paused" , "pause" ), &AudioStreamPlayer3D::set_stream_paused); |
| 877 | ClassDB::bind_method(D_METHOD("get_stream_paused" ), &AudioStreamPlayer3D::get_stream_paused); |
| 878 | |
| 879 | ClassDB::bind_method(D_METHOD("set_max_polyphony" , "max_polyphony" ), &AudioStreamPlayer3D::set_max_polyphony); |
| 880 | ClassDB::bind_method(D_METHOD("get_max_polyphony" ), &AudioStreamPlayer3D::get_max_polyphony); |
| 881 | |
| 882 | ClassDB::bind_method(D_METHOD("set_panning_strength" , "panning_strength" ), &AudioStreamPlayer3D::set_panning_strength); |
| 883 | ClassDB::bind_method(D_METHOD("get_panning_strength" ), &AudioStreamPlayer3D::get_panning_strength); |
| 884 | |
| 885 | ClassDB::bind_method(D_METHOD("has_stream_playback" ), &AudioStreamPlayer3D::has_stream_playback); |
| 886 | ClassDB::bind_method(D_METHOD("get_stream_playback" ), &AudioStreamPlayer3D::get_stream_playback); |
| 887 | |
| 888 | ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "stream" , PROPERTY_HINT_RESOURCE_TYPE, "AudioStream" ), "set_stream" , "get_stream" ); |
| 889 | ADD_PROPERTY(PropertyInfo(Variant::INT, "attenuation_model" , PROPERTY_HINT_ENUM, "Inverse,Inverse Square,Logarithmic,Disabled" ), "set_attenuation_model" , "get_attenuation_model" ); |
| 890 | ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "volume_db" , PROPERTY_HINT_RANGE, "-80,80,suffix:dB" ), "set_volume_db" , "get_volume_db" ); |
| 891 | ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "unit_size" , PROPERTY_HINT_RANGE, "0.1,100,0.01,or_greater" ), "set_unit_size" , "get_unit_size" ); |
| 892 | ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "max_db" , PROPERTY_HINT_RANGE, "-24,6,suffix:dB" ), "set_max_db" , "get_max_db" ); |
| 893 | ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "pitch_scale" , PROPERTY_HINT_RANGE, "0.01,4,0.01,or_greater" ), "set_pitch_scale" , "get_pitch_scale" ); |
| 894 | ADD_PROPERTY(PropertyInfo(Variant::BOOL, "playing" , PROPERTY_HINT_NONE, "" , PROPERTY_USAGE_EDITOR), "_set_playing" , "is_playing" ); |
| 895 | ADD_PROPERTY(PropertyInfo(Variant::BOOL, "autoplay" ), "set_autoplay" , "is_autoplay_enabled" ); |
| 896 | ADD_PROPERTY(PropertyInfo(Variant::BOOL, "stream_paused" , PROPERTY_HINT_NONE, "" ), "set_stream_paused" , "get_stream_paused" ); |
| 897 | ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "max_distance" , PROPERTY_HINT_RANGE, "0,4096,0.01,or_greater,suffix:m" ), "set_max_distance" , "get_max_distance" ); |
| 898 | ADD_PROPERTY(PropertyInfo(Variant::INT, "max_polyphony" , PROPERTY_HINT_NONE, "" ), "set_max_polyphony" , "get_max_polyphony" ); |
| 899 | ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "panning_strength" , PROPERTY_HINT_RANGE, "0,3,0.01,or_greater" ), "set_panning_strength" , "get_panning_strength" ); |
| 900 | ADD_PROPERTY(PropertyInfo(Variant::STRING_NAME, "bus" , PROPERTY_HINT_ENUM, "" ), "set_bus" , "get_bus" ); |
| 901 | ADD_PROPERTY(PropertyInfo(Variant::INT, "area_mask" , PROPERTY_HINT_LAYERS_2D_PHYSICS), "set_area_mask" , "get_area_mask" ); |
| 902 | ADD_GROUP("Emission Angle" , "emission_angle" ); |
| 903 | ADD_PROPERTY(PropertyInfo(Variant::BOOL, "emission_angle_enabled" ), "set_emission_angle_enabled" , "is_emission_angle_enabled" ); |
| 904 | ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "emission_angle_degrees" , PROPERTY_HINT_RANGE, "0.1,90,0.1,degrees" ), "set_emission_angle" , "get_emission_angle" ); |
| 905 | ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "emission_angle_filter_attenuation_db" , PROPERTY_HINT_RANGE, "-80,0,0.1,suffix:dB" ), "set_emission_angle_filter_attenuation_db" , "get_emission_angle_filter_attenuation_db" ); |
| 906 | ADD_GROUP("Attenuation Filter" , "attenuation_filter_" ); |
| 907 | ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "attenuation_filter_cutoff_hz" , PROPERTY_HINT_RANGE, "1,20500,1,suffix:Hz" ), "set_attenuation_filter_cutoff_hz" , "get_attenuation_filter_cutoff_hz" ); |
| 908 | ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "attenuation_filter_db" , PROPERTY_HINT_RANGE, "-80,0,0.1,suffix:dB" ), "set_attenuation_filter_db" , "get_attenuation_filter_db" ); |
| 909 | ADD_GROUP("Doppler" , "doppler_" ); |
| 910 | ADD_PROPERTY(PropertyInfo(Variant::INT, "doppler_tracking" , PROPERTY_HINT_ENUM, "Disabled,Idle,Physics" ), "set_doppler_tracking" , "get_doppler_tracking" ); |
| 911 | |
| 912 | BIND_ENUM_CONSTANT(ATTENUATION_INVERSE_DISTANCE); |
| 913 | BIND_ENUM_CONSTANT(ATTENUATION_INVERSE_SQUARE_DISTANCE); |
| 914 | BIND_ENUM_CONSTANT(ATTENUATION_LOGARITHMIC); |
| 915 | BIND_ENUM_CONSTANT(ATTENUATION_DISABLED); |
| 916 | |
| 917 | BIND_ENUM_CONSTANT(DOPPLER_TRACKING_DISABLED); |
| 918 | BIND_ENUM_CONSTANT(DOPPLER_TRACKING_IDLE_STEP); |
| 919 | BIND_ENUM_CONSTANT(DOPPLER_TRACKING_PHYSICS_STEP); |
| 920 | |
| 921 | ADD_SIGNAL(MethodInfo("finished" )); |
| 922 | } |
| 923 | |
| 924 | AudioStreamPlayer3D::AudioStreamPlayer3D() { |
| 925 | velocity_tracker.instantiate(); |
| 926 | AudioServer::get_singleton()->connect("bus_layout_changed" , callable_mp(this, &AudioStreamPlayer3D::_bus_layout_changed)); |
| 927 | set_disable_scale(true); |
| 928 | cached_global_panning_strength = GLOBAL_GET("audio/general/3d_panning_strength" ); |
| 929 | } |
| 930 | |
| 931 | AudioStreamPlayer3D::~AudioStreamPlayer3D() { |
| 932 | } |
| 933 | |