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