audio_stream_interactive.cpp 36 KB

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