audio_stream_interactive.cpp 36 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042
  1. /**************************************************************************/
  2. /* audio_stream_interactive.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. #include "audio_stream_interactive.h"
  31. #include "core/math/math_funcs.h"
  32. AudioStreamInteractive::AudioStreamInteractive() {
  33. }
  34. Ref<AudioStreamPlayback> AudioStreamInteractive::instantiate_playback() {
  35. Ref<AudioStreamPlaybackInteractive> playback_transitioner;
  36. playback_transitioner.instantiate();
  37. playback_transitioner->stream = Ref<AudioStreamInteractive>(this);
  38. return playback_transitioner;
  39. }
  40. String AudioStreamInteractive::get_stream_name() const {
  41. return "Transitioner";
  42. }
  43. void AudioStreamInteractive::set_clip_count(int p_count) {
  44. ERR_FAIL_COND(p_count < 0 || p_count > MAX_CLIPS);
  45. AudioServer::get_singleton()->lock();
  46. if (p_count < clip_count) {
  47. // Removing should stop players.
  48. version++;
  49. }
  50. #ifdef TOOLS_ENABLED
  51. stream_name_cache = "";
  52. if (p_count < clip_count) {
  53. for (int i = 0; i < clip_count; i++) {
  54. if (clips[i].auto_advance_next_clip >= p_count) {
  55. clips[i].auto_advance_next_clip = 0;
  56. clips[i].auto_advance = AUTO_ADVANCE_DISABLED;
  57. }
  58. }
  59. for (KeyValue<TransitionKey, Transition> &K : transition_map) {
  60. if (K.value.filler_clip >= p_count) {
  61. K.value.use_filler_clip = false;
  62. K.value.filler_clip = 0;
  63. }
  64. }
  65. if (initial_clip >= p_count) {
  66. initial_clip = 0;
  67. }
  68. }
  69. #endif
  70. clip_count = p_count;
  71. AudioServer::get_singleton()->unlock();
  72. notify_property_list_changed();
  73. emit_signal(SNAME("parameter_list_changed"));
  74. }
  75. void AudioStreamInteractive::set_initial_clip(int p_clip) {
  76. ERR_FAIL_INDEX(p_clip, clip_count);
  77. initial_clip = p_clip;
  78. }
  79. int AudioStreamInteractive::get_initial_clip() const {
  80. return initial_clip;
  81. }
  82. int AudioStreamInteractive::get_clip_count() const {
  83. return clip_count;
  84. }
  85. void AudioStreamInteractive::set_clip_name(int p_clip, const StringName &p_name) {
  86. ERR_FAIL_INDEX(p_clip, MAX_CLIPS);
  87. clips[p_clip].name = p_name;
  88. }
  89. StringName AudioStreamInteractive::get_clip_name(int p_clip) const {
  90. ERR_FAIL_COND_V(p_clip < -1 || p_clip >= MAX_CLIPS, StringName());
  91. if (p_clip == CLIP_ANY) {
  92. return RTR("All Clips");
  93. }
  94. return clips[p_clip].name;
  95. }
  96. void AudioStreamInteractive::set_clip_stream(int p_clip, const Ref<AudioStream> &p_stream) {
  97. ERR_FAIL_INDEX(p_clip, MAX_CLIPS);
  98. AudioServer::get_singleton()->lock();
  99. if (clips[p_clip].stream.is_valid()) {
  100. version++;
  101. }
  102. clips[p_clip].stream = p_stream;
  103. AudioServer::get_singleton()->unlock();
  104. #ifdef TOOLS_ENABLED
  105. if (Engine::get_singleton()->is_editor_hint()) {
  106. if (clips[p_clip].name == StringName() && p_stream.is_valid()) {
  107. String n;
  108. if (!clips[p_clip].stream->get_name().is_empty()) {
  109. n = clips[p_clip].stream->get_name().replace(",", " ");
  110. } else if (clips[p_clip].stream->get_path().is_resource_file()) {
  111. n = clips[p_clip].stream->get_path().get_file().get_basename().replace(",", " ");
  112. n = n.capitalize();
  113. }
  114. if (n != "") {
  115. clips[p_clip].name = n;
  116. }
  117. }
  118. }
  119. #endif
  120. #ifdef TOOLS_ENABLED
  121. stream_name_cache = "";
  122. notify_property_list_changed(); // Hints change if stream changes.
  123. emit_signal(SNAME("parameter_list_changed"));
  124. #endif
  125. }
  126. Ref<AudioStream> AudioStreamInteractive::get_clip_stream(int p_clip) const {
  127. ERR_FAIL_INDEX_V(p_clip, MAX_CLIPS, Ref<AudioStream>());
  128. return clips[p_clip].stream;
  129. }
  130. void AudioStreamInteractive::set_clip_auto_advance(int p_clip, AutoAdvanceMode p_mode) {
  131. ERR_FAIL_INDEX(p_clip, MAX_CLIPS);
  132. ERR_FAIL_INDEX(p_mode, 3);
  133. clips[p_clip].auto_advance = p_mode;
  134. notify_property_list_changed();
  135. }
  136. AudioStreamInteractive::AutoAdvanceMode AudioStreamInteractive::get_clip_auto_advance(int p_clip) const {
  137. ERR_FAIL_INDEX_V(p_clip, MAX_CLIPS, AUTO_ADVANCE_DISABLED);
  138. return clips[p_clip].auto_advance;
  139. }
  140. void AudioStreamInteractive::set_clip_auto_advance_next_clip(int p_clip, int p_index) {
  141. ERR_FAIL_INDEX(p_clip, MAX_CLIPS);
  142. clips[p_clip].auto_advance_next_clip = p_index;
  143. }
  144. int AudioStreamInteractive::get_clip_auto_advance_next_clip(int p_clip) const {
  145. ERR_FAIL_INDEX_V(p_clip, MAX_CLIPS, -1);
  146. return clips[p_clip].auto_advance_next_clip;
  147. }
  148. // TRANSITIONS
  149. void AudioStreamInteractive::_set_transitions(const Dictionary &p_transitions) {
  150. List<Variant> keys;
  151. p_transitions.get_key_list(&keys);
  152. for (const Variant &K : keys) {
  153. Vector2i k = K;
  154. Dictionary data = p_transitions[K];
  155. ERR_CONTINUE(!data.has("from_time"));
  156. ERR_CONTINUE(!data.has("to_time"));
  157. ERR_CONTINUE(!data.has("fade_mode"));
  158. ERR_CONTINUE(!data.has("fade_beats"));
  159. bool use_filler_clip = false;
  160. int filler_clip = 0;
  161. if (data.has("use_filler_clip") && data.has("filler_clip")) {
  162. use_filler_clip = data["use_filler_clip"];
  163. filler_clip = data["filler_clip"];
  164. }
  165. bool hold_previous = data.has("hold_previous") ? bool(data["hold_previous"]) : false;
  166. add_transition(k.x, k.y, TransitionFromTime(int(data["from_time"])), TransitionToTime(int(data["to_time"])), FadeMode(int(data["fade_mode"])), data["fade_beats"], use_filler_clip, filler_clip, hold_previous);
  167. }
  168. }
  169. Dictionary AudioStreamInteractive::_get_transitions() const {
  170. Vector<Vector2i> keys;
  171. for (const KeyValue<TransitionKey, Transition> &K : transition_map) {
  172. keys.push_back(Vector2i(K.key.from_clip, K.key.to_clip));
  173. }
  174. keys.sort();
  175. Dictionary ret;
  176. for (int i = 0; i < keys.size(); i++) {
  177. const Transition &tr = transition_map[TransitionKey(keys[i].x, keys[i].y)];
  178. Dictionary data;
  179. data["from_time"] = tr.from_time;
  180. data["to_time"] = tr.to_time;
  181. data["fade_mode"] = tr.fade_mode;
  182. data["fade_beats"] = tr.fade_beats;
  183. if (tr.use_filler_clip) {
  184. data["use_filler_clip"] = true;
  185. data["filler_clip"] = tr.filler_clip;
  186. }
  187. if (tr.hold_previous) {
  188. data["hold_previous"] = true;
  189. }
  190. ret[keys[i]] = data;
  191. }
  192. return ret;
  193. }
  194. bool AudioStreamInteractive::has_transition(int p_from_clip, int p_to_clip) const {
  195. TransitionKey tk(p_from_clip, p_to_clip);
  196. return transition_map.has(tk);
  197. }
  198. void AudioStreamInteractive::erase_transition(int p_from_clip, int p_to_clip) {
  199. TransitionKey tk(p_from_clip, p_to_clip);
  200. ERR_FAIL_COND(!transition_map.has(tk));
  201. AudioDriver::get_singleton()->lock();
  202. transition_map.erase(tk);
  203. AudioDriver::get_singleton()->unlock();
  204. }
  205. PackedInt32Array AudioStreamInteractive::get_transition_list() const {
  206. PackedInt32Array ret;
  207. for (const KeyValue<TransitionKey, Transition> &K : transition_map) {
  208. ret.push_back(K.key.from_clip);
  209. ret.push_back(K.key.to_clip);
  210. }
  211. return ret;
  212. }
  213. void AudioStreamInteractive::add_transition(int p_from_clip, int p_to_clip, TransitionFromTime p_from_time, TransitionToTime p_to_time, FadeMode p_fade_mode, float p_fade_beats, bool p_use_filler_flip, int p_filler_clip, bool p_hold_previous) {
  214. ERR_FAIL_COND(p_from_clip < CLIP_ANY || p_from_clip >= clip_count);
  215. ERR_FAIL_COND(p_to_clip < CLIP_ANY || p_to_clip >= clip_count);
  216. ERR_FAIL_UNSIGNED_INDEX(p_from_time, TRANSITION_FROM_TIME_MAX);
  217. ERR_FAIL_UNSIGNED_INDEX(p_to_time, TRANSITION_TO_TIME_MAX);
  218. ERR_FAIL_UNSIGNED_INDEX(p_fade_mode, FADE_MAX);
  219. Transition tr;
  220. tr.from_time = p_from_time;
  221. tr.to_time = p_to_time;
  222. tr.fade_mode = p_fade_mode;
  223. tr.fade_beats = p_fade_beats;
  224. tr.use_filler_clip = p_use_filler_flip;
  225. tr.filler_clip = p_filler_clip;
  226. tr.hold_previous = p_hold_previous;
  227. TransitionKey tk(p_from_clip, p_to_clip);
  228. AudioDriver::get_singleton()->lock();
  229. transition_map[tk] = tr;
  230. AudioDriver::get_singleton()->unlock();
  231. }
  232. AudioStreamInteractive::TransitionFromTime AudioStreamInteractive::get_transition_from_time(int p_from_clip, int p_to_clip) const {
  233. TransitionKey tk(p_from_clip, p_to_clip);
  234. ERR_FAIL_COND_V(!transition_map.has(tk), TRANSITION_FROM_TIME_END);
  235. return transition_map[tk].from_time;
  236. }
  237. AudioStreamInteractive::TransitionToTime AudioStreamInteractive::get_transition_to_time(int p_from_clip, int p_to_clip) const {
  238. TransitionKey tk(p_from_clip, p_to_clip);
  239. ERR_FAIL_COND_V(!transition_map.has(tk), TRANSITION_TO_TIME_START);
  240. return transition_map[tk].to_time;
  241. }
  242. AudioStreamInteractive::FadeMode AudioStreamInteractive::get_transition_fade_mode(int p_from_clip, int p_to_clip) const {
  243. TransitionKey tk(p_from_clip, p_to_clip);
  244. ERR_FAIL_COND_V(!transition_map.has(tk), FADE_DISABLED);
  245. return transition_map[tk].fade_mode;
  246. }
  247. float AudioStreamInteractive::get_transition_fade_beats(int p_from_clip, int p_to_clip) const {
  248. TransitionKey tk(p_from_clip, p_to_clip);
  249. ERR_FAIL_COND_V(!transition_map.has(tk), -1);
  250. return transition_map[tk].fade_beats;
  251. }
  252. bool AudioStreamInteractive::is_transition_using_filler_clip(int p_from_clip, int p_to_clip) const {
  253. TransitionKey tk(p_from_clip, p_to_clip);
  254. ERR_FAIL_COND_V(!transition_map.has(tk), false);
  255. return transition_map[tk].use_filler_clip;
  256. }
  257. int AudioStreamInteractive::get_transition_filler_clip(int p_from_clip, int p_to_clip) const {
  258. TransitionKey tk(p_from_clip, p_to_clip);
  259. ERR_FAIL_COND_V(!transition_map.has(tk), -1);
  260. return transition_map[tk].filler_clip;
  261. }
  262. bool AudioStreamInteractive::is_transition_holding_previous(int p_from_clip, int p_to_clip) const {
  263. TransitionKey tk(p_from_clip, p_to_clip);
  264. ERR_FAIL_COND_V(!transition_map.has(tk), false);
  265. return transition_map[tk].hold_previous;
  266. }
  267. #ifdef TOOLS_ENABLED
  268. PackedStringArray AudioStreamInteractive::_get_linked_undo_properties(const String &p_property, const Variant &p_new_value) const {
  269. PackedStringArray ret;
  270. if (p_property.begins_with("clip_") && p_property.ends_with("/stream")) {
  271. int clip = p_property.get_slicec('_', 1).to_int();
  272. if (clip < clip_count) {
  273. ret.push_back("clip_" + itos(clip) + "/name");
  274. }
  275. }
  276. if (p_property == "clip_count") {
  277. int new_clip_count = p_new_value;
  278. if (new_clip_count < clip_count) {
  279. for (int i = 0; i < clip_count; i++) {
  280. if (clips[i].auto_advance_next_clip >= new_clip_count) {
  281. ret.push_back("clip_" + itos(i) + "/auto_advance");
  282. ret.push_back("clip_" + itos(i) + "/next_clip");
  283. }
  284. }
  285. ret.push_back("_transitions");
  286. if (initial_clip >= new_clip_count) {
  287. ret.push_back("initial_clip");
  288. }
  289. }
  290. }
  291. return ret;
  292. }
  293. template <class T>
  294. static void _test_and_swap(T &p_elem, uint32_t p_a, uint32_t p_b) {
  295. if ((uint32_t)p_elem == p_a) {
  296. p_elem = p_b;
  297. } else if (uint32_t(p_elem) == p_b) {
  298. p_elem = p_a;
  299. }
  300. }
  301. void AudioStreamInteractive::_inspector_array_swap_clip(uint32_t p_item_a, uint32_t p_item_b) {
  302. ERR_FAIL_UNSIGNED_INDEX(p_item_a, (uint32_t)clip_count);
  303. ERR_FAIL_UNSIGNED_INDEX(p_item_b, (uint32_t)clip_count);
  304. for (int i = 0; i < clip_count; i++) {
  305. _test_and_swap(clips[i].auto_advance_next_clip, p_item_a, p_item_b);
  306. }
  307. Vector<TransitionKey> to_remove;
  308. HashMap<TransitionKey, Transition, TransitionKeyHasher> to_add;
  309. for (KeyValue<TransitionKey, Transition> &K : transition_map) {
  310. if (K.key.from_clip == p_item_a || K.key.from_clip == p_item_b || K.key.to_clip == p_item_a || K.key.to_clip == p_item_b) {
  311. to_remove.push_back(K.key);
  312. TransitionKey new_key = K.key;
  313. _test_and_swap(new_key.from_clip, p_item_a, p_item_b);
  314. _test_and_swap(new_key.to_clip, p_item_a, p_item_b);
  315. to_add[new_key] = K.value;
  316. }
  317. }
  318. for (int i = 0; i < to_remove.size(); i++) {
  319. transition_map.erase(to_remove[i]);
  320. }
  321. for (KeyValue<TransitionKey, Transition> &K : to_add) {
  322. transition_map.insert(K.key, K.value);
  323. }
  324. SWAP(clips[p_item_a], clips[p_item_b]);
  325. stream_name_cache = "";
  326. notify_property_list_changed();
  327. emit_signal(SNAME("parameter_list_changed"));
  328. }
  329. String AudioStreamInteractive::_get_streams_hint() const {
  330. if (!stream_name_cache.is_empty()) {
  331. return stream_name_cache;
  332. }
  333. for (int i = 0; i < clip_count; i++) {
  334. if (i > 0) {
  335. stream_name_cache += ",";
  336. }
  337. String n = String(clips[i].name).replace(",", " ");
  338. if (n == "" && clips[i].stream.is_valid()) {
  339. if (!clips[i].stream->get_name().is_empty()) {
  340. n = clips[i].stream->get_name().replace(",", " ");
  341. } else if (clips[i].stream->get_path().is_resource_file()) {
  342. n = clips[i].stream->get_path().get_file().replace(",", " ");
  343. }
  344. }
  345. if (n == "") {
  346. n = "Clip " + itos(i);
  347. }
  348. stream_name_cache += n;
  349. }
  350. return stream_name_cache;
  351. }
  352. #endif
  353. void AudioStreamInteractive::_validate_property(PropertyInfo &r_property) const {
  354. String prop = r_property.name;
  355. #ifdef TOOLS_ENABLED
  356. if (prop == "switch_to") {
  357. r_property.hint_string = _get_streams_hint();
  358. return;
  359. }
  360. #endif
  361. if (prop == "initial_clip") {
  362. #ifdef TOOLS_ENABLED
  363. r_property.hint_string = _get_streams_hint();
  364. #endif
  365. } else if (prop.begins_with("clip_") && prop != "clip_count") {
  366. int clip = prop.get_slicec('_', 1).to_int();
  367. if (clip >= clip_count) {
  368. r_property.usage = PROPERTY_USAGE_INTERNAL;
  369. } else if (prop == "clip_" + itos(clip) + "/next_clip") {
  370. if (clips[clip].auto_advance != AUTO_ADVANCE_ENABLED) {
  371. r_property.usage = 0;
  372. } else {
  373. #ifdef TOOLS_ENABLED
  374. r_property.hint_string = _get_streams_hint();
  375. #endif
  376. }
  377. }
  378. }
  379. }
  380. void AudioStreamInteractive::get_parameter_list(List<Parameter> *r_parameters) {
  381. String clip_names;
  382. for (int i = 0; i < clip_count; i++) {
  383. clip_names += ",";
  384. clip_names += clips[i].name;
  385. }
  386. r_parameters->push_back(Parameter(PropertyInfo(Variant::STRING, "switch_to_clip", PROPERTY_HINT_ENUM, clip_names, PROPERTY_USAGE_EDITOR), ""));
  387. }
  388. void AudioStreamInteractive::_bind_methods() {
  389. #ifdef TOOLS_ENABLED
  390. ClassDB::bind_method(D_METHOD("_get_linked_undo_properties", "for_property", "for_value"), &AudioStreamInteractive::_get_linked_undo_properties);
  391. ClassDB::bind_method(D_METHOD("_inspector_array_swap_clip", "a", "b"), &AudioStreamInteractive::_inspector_array_swap_clip);
  392. #endif
  393. // CLIPS
  394. ClassDB::bind_method(D_METHOD("set_clip_count", "clip_count"), &AudioStreamInteractive::set_clip_count);
  395. ClassDB::bind_method(D_METHOD("get_clip_count"), &AudioStreamInteractive::get_clip_count);
  396. ClassDB::bind_method(D_METHOD("set_initial_clip", "clip_index"), &AudioStreamInteractive::set_initial_clip);
  397. ClassDB::bind_method(D_METHOD("get_initial_clip"), &AudioStreamInteractive::get_initial_clip);
  398. ClassDB::bind_method(D_METHOD("set_clip_name", "clip_index", "name"), &AudioStreamInteractive::set_clip_name);
  399. ClassDB::bind_method(D_METHOD("get_clip_name", "clip_index"), &AudioStreamInteractive::get_clip_name);
  400. ClassDB::bind_method(D_METHOD("set_clip_stream", "clip_index", "stream"), &AudioStreamInteractive::set_clip_stream);
  401. ClassDB::bind_method(D_METHOD("get_clip_stream", "clip_index"), &AudioStreamInteractive::get_clip_stream);
  402. ClassDB::bind_method(D_METHOD("set_clip_auto_advance", "clip_index", "mode"), &AudioStreamInteractive::set_clip_auto_advance);
  403. ClassDB::bind_method(D_METHOD("get_clip_auto_advance", "clip_index"), &AudioStreamInteractive::get_clip_auto_advance);
  404. ClassDB::bind_method(D_METHOD("set_clip_auto_advance_next_clip", "clip_index", "auto_advance_next_clip"), &AudioStreamInteractive::set_clip_auto_advance_next_clip);
  405. ClassDB::bind_method(D_METHOD("get_clip_auto_advance_next_clip", "clip_index"), &AudioStreamInteractive::get_clip_auto_advance_next_clip);
  406. ADD_PROPERTY(PropertyInfo(Variant::INT, "clip_count", PROPERTY_HINT_RANGE, "1," + itos(MAX_CLIPS), PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_ARRAY, "Clips,clip_,page_size=999,unfoldable,numbered,swap_method=_inspector_array_swap_clip,add_button_text=" + String(RTR("Add Clip"))), "set_clip_count", "get_clip_count");
  407. for (int i = 0; i < MAX_CLIPS; i++) {
  408. ADD_PROPERTYI(PropertyInfo(Variant::STRING_NAME, "clip_" + itos(i) + "/name", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_INTERNAL), "set_clip_name", "get_clip_name", i);
  409. ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "clip_" + itos(i) + "/stream", PROPERTY_HINT_RESOURCE_TYPE, "AudioStream", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_INTERNAL), "set_clip_stream", "get_clip_stream", i);
  410. ADD_PROPERTYI(PropertyInfo(Variant::INT, "clip_" + itos(i) + "/auto_advance", PROPERTY_HINT_ENUM, "Disabled,Enabled,ReturnToHold", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_INTERNAL), "set_clip_auto_advance", "get_clip_auto_advance", i);
  411. ADD_PROPERTYI(PropertyInfo(Variant::INT, "clip_" + itos(i) + "/next_clip", PROPERTY_HINT_ENUM, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_INTERNAL), "set_clip_auto_advance_next_clip", "get_clip_auto_advance_next_clip", i);
  412. }
  413. // Needs to be registered after `clip_*` properties, as it depends on them.
  414. ADD_PROPERTY(PropertyInfo(Variant::INT, "initial_clip", PROPERTY_HINT_ENUM, "", PROPERTY_USAGE_DEFAULT), "set_initial_clip", "get_initial_clip");
  415. // TRANSITIONS
  416. ClassDB::bind_method(D_METHOD("add_transition", "from_clip", "to_clip", "from_time", "to_time", "fade_mode", "fade_beats", "use_filler_clip", "filler_clip", "hold_previous"), &AudioStreamInteractive::add_transition, DEFVAL(false), DEFVAL(-1), DEFVAL(false));
  417. ClassDB::bind_method(D_METHOD("has_transition", "from_clip", "to_clip"), &AudioStreamInteractive::has_transition);
  418. ClassDB::bind_method(D_METHOD("erase_transition", "from_clip", "to_clip"), &AudioStreamInteractive::erase_transition);
  419. ClassDB::bind_method(D_METHOD("get_transition_list"), &AudioStreamInteractive::get_transition_list);
  420. ClassDB::bind_method(D_METHOD("get_transition_from_time", "from_clip", "to_clip"), &AudioStreamInteractive::get_transition_from_time);
  421. ClassDB::bind_method(D_METHOD("get_transition_to_time", "from_clip", "to_clip"), &AudioStreamInteractive::get_transition_to_time);
  422. ClassDB::bind_method(D_METHOD("get_transition_fade_mode", "from_clip", "to_clip"), &AudioStreamInteractive::get_transition_fade_mode);
  423. ClassDB::bind_method(D_METHOD("get_transition_fade_beats", "from_clip", "to_clip"), &AudioStreamInteractive::get_transition_fade_beats);
  424. ClassDB::bind_method(D_METHOD("is_transition_using_filler_clip", "from_clip", "to_clip"), &AudioStreamInteractive::is_transition_using_filler_clip);
  425. ClassDB::bind_method(D_METHOD("get_transition_filler_clip", "from_clip", "to_clip"), &AudioStreamInteractive::get_transition_filler_clip);
  426. ClassDB::bind_method(D_METHOD("is_transition_holding_previous", "from_clip", "to_clip"), &AudioStreamInteractive::is_transition_holding_previous);
  427. ClassDB::bind_method(D_METHOD("_set_transitions", "transitions"), &AudioStreamInteractive::_set_transitions);
  428. ClassDB::bind_method(D_METHOD("_get_transitions"), &AudioStreamInteractive::_get_transitions);
  429. ADD_PROPERTY(PropertyInfo(Variant::DICTIONARY, "_transitions", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "_set_transitions", "_get_transitions");
  430. BIND_ENUM_CONSTANT(TRANSITION_FROM_TIME_IMMEDIATE);
  431. BIND_ENUM_CONSTANT(TRANSITION_FROM_TIME_NEXT_BEAT);
  432. BIND_ENUM_CONSTANT(TRANSITION_FROM_TIME_NEXT_BAR);
  433. BIND_ENUM_CONSTANT(TRANSITION_FROM_TIME_END);
  434. BIND_ENUM_CONSTANT(TRANSITION_TO_TIME_SAME_POSITION);
  435. BIND_ENUM_CONSTANT(TRANSITION_TO_TIME_START);
  436. BIND_ENUM_CONSTANT(FADE_DISABLED);
  437. BIND_ENUM_CONSTANT(FADE_IN);
  438. BIND_ENUM_CONSTANT(FADE_OUT);
  439. BIND_ENUM_CONSTANT(FADE_CROSS);
  440. BIND_ENUM_CONSTANT(FADE_AUTOMATIC);
  441. BIND_ENUM_CONSTANT(AUTO_ADVANCE_DISABLED);
  442. BIND_ENUM_CONSTANT(AUTO_ADVANCE_ENABLED);
  443. BIND_ENUM_CONSTANT(AUTO_ADVANCE_RETURN_TO_HOLD);
  444. BIND_CONSTANT(CLIP_ANY);
  445. }
  446. ///////////////////////////////////////////////////////////
  447. ///////////////////////////////////////////////////////////
  448. ///////////////////////////////////////////////////////////
  449. AudioStreamPlaybackInteractive::AudioStreamPlaybackInteractive() {
  450. }
  451. AudioStreamPlaybackInteractive::~AudioStreamPlaybackInteractive() {
  452. }
  453. void AudioStreamPlaybackInteractive::stop() {
  454. if (!active) {
  455. return;
  456. }
  457. active = false;
  458. for (int i = 0; i < AudioStreamInteractive::MAX_CLIPS; i++) {
  459. if (states[i].playback.is_valid()) {
  460. states[i].playback->stop();
  461. }
  462. states[i].fade_speed = 0.0;
  463. states[i].fade_volume = 0.0;
  464. states[i].fade_wait = 0.0;
  465. states[i].reset_fade();
  466. states[i].active = false;
  467. states[i].auto_advance = -1;
  468. states[i].first_mix = true;
  469. }
  470. }
  471. void AudioStreamPlaybackInteractive::start(double p_from_pos) {
  472. if (active) {
  473. stop();
  474. }
  475. if (version != stream->version) {
  476. for (int i = 0; i < AudioStreamInteractive::MAX_CLIPS; i++) {
  477. Ref<AudioStream> src_stream;
  478. if (i < stream->clip_count) {
  479. src_stream = stream->clips[i].stream;
  480. }
  481. if (states[i].stream != src_stream) {
  482. states[i].stream.unref();
  483. states[i].playback.unref();
  484. states[i].stream = src_stream;
  485. states[i].playback = src_stream->instantiate_playback();
  486. }
  487. }
  488. version = stream->version;
  489. }
  490. int current = stream->initial_clip;
  491. if (current < 0 || current >= stream->clip_count) {
  492. return; // No playback possible.
  493. }
  494. if (states[current].playback.is_null()) {
  495. return; //no playback possible
  496. }
  497. active = true;
  498. _queue(current, false);
  499. }
  500. void AudioStreamPlaybackInteractive::_queue(int p_to_clip_index, bool p_is_auto_advance) {
  501. ERR_FAIL_INDEX(p_to_clip_index, stream->clip_count);
  502. ERR_FAIL_COND(states[p_to_clip_index].playback.is_null());
  503. if (playback_current == -1) {
  504. // Nothing to do, start.
  505. int current = p_to_clip_index;
  506. State &state = states[current];
  507. state.active = true;
  508. state.fade_wait = 0;
  509. state.fade_volume = 1.0;
  510. state.fade_speed = 0;
  511. state.first_mix = true;
  512. state.playback->start(0);
  513. playback_current = current;
  514. if (stream->clips[current].auto_advance == AudioStreamInteractive::AUTO_ADVANCE_ENABLED && stream->clips[current].auto_advance_next_clip >= 0 && stream->clips[current].auto_advance_next_clip < stream->clip_count && stream->clips[current].auto_advance_next_clip != current) {
  515. //prepare auto advance
  516. state.auto_advance = stream->clips[current].auto_advance_next_clip;
  517. }
  518. return;
  519. }
  520. for (int i = 0; i < stream->clip_count; i++) {
  521. if (i == playback_current || i == p_to_clip_index) {
  522. continue;
  523. }
  524. if (states[i].active && states[i].fade_wait > 0) { // Waiting to kick in, terminate because change of plans.
  525. states[i].playback->stop();
  526. states[i].reset_fade();
  527. states[i].active = false;
  528. }
  529. }
  530. State &from_state = states[playback_current];
  531. State &to_state = states[p_to_clip_index];
  532. AudioStreamInteractive::Transition transition; // Use an empty transition by default
  533. AudioStreamInteractive::TransitionKey tkeys[4] = {
  534. AudioStreamInteractive::TransitionKey(playback_current, p_to_clip_index),
  535. AudioStreamInteractive::TransitionKey(playback_current, AudioStreamInteractive::CLIP_ANY),
  536. AudioStreamInteractive::TransitionKey(AudioStreamInteractive::CLIP_ANY, p_to_clip_index),
  537. AudioStreamInteractive::TransitionKey(AudioStreamInteractive::CLIP_ANY, AudioStreamInteractive::CLIP_ANY)
  538. };
  539. for (int i = 0; i < 4; i++) {
  540. if (stream->transition_map.has(tkeys[i])) {
  541. transition = stream->transition_map[tkeys[i]];
  542. break;
  543. }
  544. }
  545. if (transition.fade_mode == AudioStreamInteractive::FADE_AUTOMATIC) {
  546. // Adjust automatic mode based on context.
  547. if (transition.to_time == AudioStreamInteractive::TRANSITION_TO_TIME_START) {
  548. transition.fade_mode = AudioStreamInteractive::FADE_OUT;
  549. } else {
  550. transition.fade_mode = AudioStreamInteractive::FADE_CROSS;
  551. }
  552. }
  553. if (p_is_auto_advance) {
  554. transition.from_time = AudioStreamInteractive::TRANSITION_FROM_TIME_END;
  555. if (transition.to_time == AudioStreamInteractive::TRANSITION_TO_TIME_SAME_POSITION) {
  556. transition.to_time = AudioStreamInteractive::TRANSITION_TO_TIME_START;
  557. }
  558. }
  559. // Prepare the fadeout
  560. float current_pos = from_state.playback->get_playback_position();
  561. float src_fade_wait = 0;
  562. float dst_seek_to = 0;
  563. float fade_speed = 0;
  564. bool src_no_loop = false;
  565. if (from_state.stream->get_bpm()) {
  566. // Check if source speed has BPM, if so, transition syncs to BPM
  567. float beat_sec = 60 / float(from_state.stream->get_bpm());
  568. switch (transition.from_time) {
  569. case AudioStreamInteractive::TRANSITION_FROM_TIME_IMMEDIATE: {
  570. src_fade_wait = 0;
  571. } break;
  572. case AudioStreamInteractive::TRANSITION_FROM_TIME_NEXT_BEAT: {
  573. float remainder = Math::fmod(current_pos, beat_sec);
  574. src_fade_wait = beat_sec - remainder;
  575. } break;
  576. case AudioStreamInteractive::TRANSITION_FROM_TIME_NEXT_BAR: {
  577. if (from_state.stream->get_bar_beats() > 0) {
  578. float bar_sec = beat_sec * from_state.stream->get_bar_beats();
  579. float remainder = Math::fmod(current_pos, bar_sec);
  580. src_fade_wait = bar_sec - remainder;
  581. } else {
  582. // Stream does not have a number of beats per bar - avoid NaN, and play immediately.
  583. src_fade_wait = 0;
  584. }
  585. } break;
  586. case AudioStreamInteractive::TRANSITION_FROM_TIME_END: {
  587. float end = from_state.stream->get_beat_count() > 0 ? float(from_state.stream->get_beat_count() * beat_sec) : from_state.stream->get_length();
  588. if (end == 0) {
  589. // Stream does not have a length.
  590. src_fade_wait = 0;
  591. } else {
  592. src_fade_wait = end - current_pos;
  593. }
  594. if (!from_state.stream->has_loop()) {
  595. src_no_loop = true;
  596. }
  597. } break;
  598. default: {
  599. }
  600. }
  601. // Fade speed also aligned to BPM
  602. fade_speed = 1.0 / (transition.fade_beats * beat_sec);
  603. } else {
  604. // Source has no BPM, so just simple transition.
  605. if (transition.from_time == AudioStreamInteractive::TRANSITION_FROM_TIME_END && from_state.stream->get_length() > 0) {
  606. float end = from_state.stream->get_length();
  607. src_fade_wait = end - current_pos;
  608. if (!from_state.stream->has_loop()) {
  609. src_no_loop = true;
  610. }
  611. } else {
  612. src_fade_wait = 0;
  613. }
  614. fade_speed = 1.0 / transition.fade_beats;
  615. }
  616. if (transition.to_time == AudioStreamInteractive::TRANSITION_TO_TIME_PREVIOUS_POSITION && to_state.stream->get_length() > 0.0) {
  617. dst_seek_to = to_state.previous_position;
  618. } else if (transition.to_time == AudioStreamInteractive::TRANSITION_TO_TIME_SAME_POSITION && transition.from_time != AudioStreamInteractive::TRANSITION_FROM_TIME_END && to_state.stream->get_length() > 0.0) {
  619. // Seeking to basically same position as when we start fading.
  620. dst_seek_to = current_pos + src_fade_wait;
  621. float end;
  622. if (to_state.stream->get_bpm() > 0 && to_state.stream->get_beat_count()) {
  623. float beat_sec = 60 / float(to_state.stream->get_bpm());
  624. end = to_state.stream->get_beat_count() * beat_sec;
  625. } else {
  626. end = to_state.stream->get_length();
  627. }
  628. if (dst_seek_to > end) {
  629. // Seeking too far away.
  630. dst_seek_to = 0; //past end, loop to beginning.
  631. }
  632. } else {
  633. // Seek to Start
  634. dst_seek_to = 0.0;
  635. }
  636. if (transition.fade_mode == AudioStreamInteractive::FADE_DISABLED || transition.fade_mode == AudioStreamInteractive::FADE_IN) {
  637. if (src_no_loop) {
  638. // If there is no fade in the source stream, then let it continue until it ends.
  639. from_state.fade_wait = 0;
  640. from_state.fade_speed = 0;
  641. } else {
  642. // Otherwise force a very quick fade to avoid clicks
  643. from_state.fade_wait = src_fade_wait;
  644. from_state.fade_speed = 1.0 / -0.001;
  645. }
  646. } else {
  647. // Regular fade.
  648. from_state.fade_wait = src_fade_wait;
  649. from_state.fade_speed = -fade_speed;
  650. }
  651. // keep volume, since it may have been fading in from something else.
  652. to_state.playback->start(dst_seek_to);
  653. to_state.active = true;
  654. to_state.fade_volume = 0.0;
  655. to_state.first_mix = true;
  656. int auto_advance_to = -1;
  657. if (stream->clips[p_to_clip_index].auto_advance == AudioStreamInteractive::AUTO_ADVANCE_ENABLED) {
  658. int next_clip = stream->clips[p_to_clip_index].auto_advance_next_clip;
  659. if (next_clip >= 0 && next_clip < (int)stream->clip_count && states[next_clip].playback.is_valid() && next_clip != p_to_clip_index && (!transition.use_filler_clip || next_clip != transition.filler_clip)) {
  660. auto_advance_to = next_clip;
  661. }
  662. }
  663. if (return_memory != -1 && stream->clips[p_to_clip_index].auto_advance == AudioStreamInteractive::AUTO_ADVANCE_RETURN_TO_HOLD) {
  664. auto_advance_to = return_memory;
  665. return_memory = -1;
  666. }
  667. if (transition.hold_previous) {
  668. return_memory = playback_current;
  669. }
  670. if (transition.use_filler_clip && transition.filler_clip >= 0 && transition.filler_clip < (int)stream->clip_count && states[transition.filler_clip].playback.is_valid() && playback_current != transition.filler_clip && p_to_clip_index != transition.filler_clip) {
  671. State &filler_state = states[transition.filler_clip];
  672. filler_state.playback->start(0);
  673. filler_state.active = true;
  674. // Filler state does not fade (bake fade in the audio clip if you want fading.
  675. filler_state.fade_volume = 1.0;
  676. filler_state.fade_speed = 0.0;
  677. filler_state.fade_wait = src_fade_wait;
  678. filler_state.first_mix = true;
  679. float filler_end;
  680. if (filler_state.stream->get_bpm() > 0 && filler_state.stream->get_beat_count() > 0) {
  681. float filler_beat_sec = 60 / float(filler_state.stream->get_bpm());
  682. filler_end = filler_beat_sec * filler_state.stream->get_beat_count();
  683. } else {
  684. filler_end = filler_state.stream->get_length();
  685. }
  686. if (!filler_state.stream->has_loop()) {
  687. src_no_loop = true;
  688. }
  689. if (transition.fade_mode == AudioStreamInteractive::FADE_DISABLED || transition.fade_mode == AudioStreamInteractive::FADE_OUT) {
  690. // No fading, immediately start at full volume.
  691. to_state.fade_volume = 0.0;
  692. to_state.fade_speed = 1.0; //start at full volume, as filler is meant as a transition.
  693. } else {
  694. // Fade enable, prepare fade.
  695. to_state.fade_volume = 0.0;
  696. to_state.fade_speed = fade_speed;
  697. }
  698. to_state.fade_wait = src_fade_wait + filler_end;
  699. } else {
  700. to_state.fade_wait = src_fade_wait;
  701. if (transition.fade_mode == AudioStreamInteractive::FADE_DISABLED || transition.fade_mode == AudioStreamInteractive::FADE_OUT) {
  702. to_state.fade_volume = 1.0;
  703. to_state.fade_speed = 0.0;
  704. } else {
  705. to_state.fade_volume = 0.0;
  706. to_state.fade_speed = fade_speed;
  707. }
  708. to_state.auto_advance = auto_advance_to;
  709. }
  710. }
  711. void AudioStreamPlaybackInteractive::seek(double p_time) {
  712. // Seek not supported
  713. }
  714. int AudioStreamPlaybackInteractive::mix(AudioFrame *p_buffer, float p_rate_scale, int p_frames) {
  715. if (active && version != stream->version) {
  716. stop();
  717. }
  718. if (switch_request != -1) {
  719. _queue(switch_request, false);
  720. switch_request = -1;
  721. }
  722. if (!active) {
  723. return 0;
  724. }
  725. int todo = p_frames;
  726. while (todo) {
  727. int to_mix = MIN(todo, BUFFER_SIZE);
  728. _mix_internal(to_mix);
  729. for (int i = 0; i < to_mix; i++) {
  730. p_buffer[i] = mix_buffer[i];
  731. }
  732. p_buffer += to_mix;
  733. todo -= to_mix;
  734. }
  735. return p_frames;
  736. }
  737. void AudioStreamPlaybackInteractive::_mix_internal(int p_frames) {
  738. for (int i = 0; i < p_frames; i++) {
  739. mix_buffer[i] = AudioFrame(0, 0);
  740. }
  741. for (int i = 0; i < stream->clip_count; i++) {
  742. if (!states[i].active) {
  743. continue;
  744. }
  745. _mix_internal_state(i, p_frames);
  746. }
  747. }
  748. void AudioStreamPlaybackInteractive::_mix_internal_state(int p_state_idx, int p_frames) {
  749. State &state = states[p_state_idx];
  750. double mix_rate = double(AudioServer::get_singleton()->get_mix_rate());
  751. double frame_inc = 1.0 / mix_rate;
  752. int from_frame = 0;
  753. int queue_next = -1;
  754. if (state.first_mix) {
  755. // Did not start mixing yet, wait.
  756. double mix_time = p_frames * frame_inc;
  757. if (state.fade_wait < mix_time) {
  758. // time to start!
  759. from_frame = state.fade_wait * mix_rate;
  760. state.fade_wait = 0;
  761. if (state.fade_speed == 0.0) {
  762. queue_next = state.auto_advance;
  763. }
  764. playback_current = p_state_idx;
  765. state.first_mix = false;
  766. } else {
  767. // This is for fade in of new stream.
  768. state.fade_wait -= mix_time;
  769. return; // Nothing to do
  770. }
  771. }
  772. state.previous_position = state.playback->get_playback_position();
  773. state.playback->mix(temp_buffer + from_frame, 1.0, p_frames - from_frame);
  774. double frame_fade_inc = state.fade_speed * frame_inc;
  775. for (int i = from_frame; i < p_frames; i++) {
  776. if (state.fade_wait) {
  777. // This is for fade out of existing stream;
  778. state.fade_wait -= frame_inc;
  779. if (state.fade_wait < 0.0) {
  780. state.fade_wait = 0.0;
  781. }
  782. } else if (frame_fade_inc > 0) {
  783. state.fade_volume += frame_fade_inc;
  784. if (state.fade_volume >= 1.0) {
  785. state.fade_speed = 0.0;
  786. frame_fade_inc = 0.0;
  787. state.fade_volume = 1.0;
  788. queue_next = state.auto_advance;
  789. }
  790. } else if (frame_fade_inc < 0.0) {
  791. state.fade_volume += frame_fade_inc;
  792. if (state.fade_volume <= 0.0) {
  793. state.fade_speed = 0.0;
  794. frame_fade_inc = 0.0;
  795. state.fade_volume = 0.0;
  796. state.playback->stop(); // Stop playback and break, no point to continue mixing
  797. break;
  798. }
  799. }
  800. mix_buffer[i] += temp_buffer[i] * state.fade_volume;
  801. state.previous_position += frame_inc;
  802. }
  803. if (!state.playback->is_playing()) {
  804. // It finished because it either reached end or faded out, so deactivate and continue.
  805. state.active = false;
  806. }
  807. if (queue_next != -1) {
  808. _queue(queue_next, true);
  809. }
  810. }
  811. void AudioStreamPlaybackInteractive::tag_used_streams() {
  812. for (int i = 0; i < stream->clip_count; i++) {
  813. if (states[i].active && !states[i].first_mix && states[i].playback->is_playing()) {
  814. states[i].stream->tag_used(states[i].playback->get_playback_position());
  815. }
  816. }
  817. stream->tag_used(0);
  818. }
  819. void AudioStreamPlaybackInteractive::switch_to_clip_by_name(const StringName &p_name) {
  820. if (p_name == StringName()) {
  821. switch_request = -1;
  822. return;
  823. }
  824. ERR_FAIL_COND_MSG(stream.is_null(), "Attempted to switch while not playing back any stream.");
  825. for (int i = 0; i < stream->get_clip_count(); i++) {
  826. if (stream->get_clip_name(i) == p_name) {
  827. switch_request = i;
  828. return;
  829. }
  830. }
  831. ERR_FAIL_MSG("Clip not found: " + String(p_name));
  832. }
  833. void AudioStreamPlaybackInteractive::set_parameter(const StringName &p_name, const Variant &p_value) {
  834. if (p_name == SNAME("switch_to_clip")) {
  835. switch_to_clip_by_name(p_value);
  836. }
  837. }
  838. Variant AudioStreamPlaybackInteractive::get_parameter(const StringName &p_name) const {
  839. if (p_name == SNAME("switch_to_clip")) {
  840. for (int i = 0; i < stream->get_clip_count(); i++) {
  841. if (switch_request != -1) {
  842. if (switch_request == i) {
  843. return String(stream->get_clip_name(i));
  844. }
  845. } else if (playback_current == i) {
  846. return String(stream->get_clip_name(i));
  847. }
  848. }
  849. return "";
  850. }
  851. return Variant();
  852. }
  853. void AudioStreamPlaybackInteractive::switch_to_clip(int p_index) {
  854. switch_request = p_index;
  855. }
  856. int AudioStreamPlaybackInteractive::get_current_clip_index() const {
  857. return playback_current;
  858. }
  859. int AudioStreamPlaybackInteractive::get_loop_count() const {
  860. return 0; // Looping not supported
  861. }
  862. double AudioStreamPlaybackInteractive::get_playback_position() const {
  863. return 0.0;
  864. }
  865. bool AudioStreamPlaybackInteractive::is_playing() const {
  866. return active;
  867. }
  868. void AudioStreamPlaybackInteractive::_bind_methods() {
  869. ClassDB::bind_method(D_METHOD("switch_to_clip_by_name", "clip_name"), &AudioStreamPlaybackInteractive::switch_to_clip_by_name);
  870. ClassDB::bind_method(D_METHOD("switch_to_clip", "clip_index"), &AudioStreamPlaybackInteractive::switch_to_clip);
  871. ClassDB::bind_method(D_METHOD("get_current_clip_index"), &AudioStreamPlaybackInteractive::get_current_clip_index);
  872. }