godot/modules/interactive_music/audio_stream_interactive.h
Juan Linietsky 43b78cd2ad Add interactive music support
This PR adds 3 types of audio streams used for interactive music support.

* AudioStreamInteractive: Allows setting several sub-streams and transition between them with many options.
* AudioStreamPlaylist: Allows sequential or shuffled playback of a list of streams.
* AudioStreamSynchronized: Allows synchronous playback of several streams, the volume of each can be controlled.

Theese three stream types can be combined to create complex, layered interactive music and transitions between them, similar to software such as WWise.
2024-03-12 21:54:59 +01:00

270 lines
8.9 KiB
C++

/**************************************************************************/
/* audio_stream_interactive.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#ifndef AUDIO_STREAM_INTERACTIVE_H
#define AUDIO_STREAM_INTERACTIVE_H
#include "servers/audio/audio_stream.h"
class AudioStreamPlaybackInteractive;
class AudioStreamInteractive : public AudioStream {
GDCLASS(AudioStreamInteractive, AudioStream)
OBJ_SAVE_TYPE(AudioStream)
public:
enum TransitionFromTime {
TRANSITION_FROM_TIME_IMMEDIATE,
TRANSITION_FROM_TIME_NEXT_BEAT,
TRANSITION_FROM_TIME_NEXT_BAR,
TRANSITION_FROM_TIME_END,
TRANSITION_FROM_TIME_MAX
};
enum TransitionToTime {
TRANSITION_TO_TIME_SAME_POSITION,
TRANSITION_TO_TIME_START,
TRANSITION_TO_TIME_PREVIOUS_POSITION,
TRANSITION_TO_TIME_MAX,
};
enum FadeMode {
FADE_DISABLED,
FADE_IN,
FADE_OUT,
FADE_CROSS,
FADE_AUTOMATIC,
FADE_MAX
};
enum AutoAdvanceMode {
AUTO_ADVANCE_DISABLED,
AUTO_ADVANCE_ENABLED,
AUTO_ADVANCE_RETURN_TO_HOLD,
};
enum {
CLIP_ANY = -1
};
private:
friend class AudioStreamPlaybackInteractive;
int sample_rate = 44100;
bool stereo = true;
int initial_clip = 0;
double time = 0;
enum {
MAX_CLIPS = 63, // Because we use bitmasks for transition matching.
MAX_TRANSITIONS = 63,
};
struct Clip {
StringName name;
Ref<AudioStream> stream;
AutoAdvanceMode auto_advance = AUTO_ADVANCE_DISABLED;
int auto_advance_next_clip = 0;
};
Clip clips[MAX_CLIPS];
struct Transition {
TransitionFromTime from_time = TRANSITION_FROM_TIME_NEXT_BEAT;
TransitionToTime to_time = TRANSITION_TO_TIME_START;
FadeMode fade_mode = FADE_AUTOMATIC;
int fade_beats = 1;
bool use_filler_clip = false;
int filler_clip = 0;
bool hold_previous = false;
};
struct TransitionKey {
uint32_t from_clip;
uint32_t to_clip;
bool operator==(const TransitionKey &p_key) const {
return from_clip == p_key.from_clip && to_clip == p_key.to_clip;
}
TransitionKey(uint32_t p_from_clip = 0, uint32_t p_to_clip = 0) {
from_clip = p_from_clip;
to_clip = p_to_clip;
}
};
struct TransitionKeyHasher {
static _FORCE_INLINE_ uint32_t hash(const TransitionKey &p_key) {
uint32_t h = hash_murmur3_one_32(p_key.from_clip);
return hash_murmur3_one_32(p_key.to_clip, h);
}
};
HashMap<TransitionKey, Transition, TransitionKeyHasher> transition_map;
uint64_t version = 1; // Used to stop playback instances for incompatibility.
int clip_count = 0;
HashSet<AudioStreamPlaybackInteractive *> playbacks;
#ifdef TOOLS_ENABLED
mutable String stream_name_cache;
String _get_streams_hint() const;
PackedStringArray _get_linked_undo_properties(const String &p_property, const Variant &p_new_value) const;
void _inspector_array_swap_clip(uint32_t p_item_a, uint32_t p_item_b);
#endif
void _set_transitions(const Dictionary &p_transitions);
Dictionary _get_transitions() const;
public:
// CLIPS
void set_clip_count(int p_count);
int get_clip_count() const;
void set_initial_clip(int p_clip);
int get_initial_clip() const;
void set_clip_name(int p_clip, const StringName &p_name);
StringName get_clip_name(int p_clip) const;
void set_clip_stream(int p_clip, const Ref<AudioStream> &p_stream);
Ref<AudioStream> get_clip_stream(int p_clip) const;
void set_clip_auto_advance(int p_clip, AutoAdvanceMode p_mode);
AutoAdvanceMode get_clip_auto_advance(int p_clip) const;
void set_clip_auto_advance_next_clip(int p_clip, int p_index);
int get_clip_auto_advance_next_clip(int p_clip) const;
// TRANSITIONS
void 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 = false, int p_filler_clip = -1, bool p_hold_previous = false);
TransitionFromTime get_transition_from_time(int p_from_clip, int p_to_clip) const;
TransitionToTime get_transition_to_time(int p_from_clip, int p_to_clip) const;
FadeMode get_transition_fade_mode(int p_from_clip, int p_to_clip) const;
float get_transition_fade_beats(int p_from_clip, int p_to_clip) const;
bool is_transition_using_filler_clip(int p_from_clip, int p_to_clip) const;
int get_transition_filler_clip(int p_from_clip, int p_to_clip) const;
bool is_transition_holding_previous(int p_from_clip, int p_to_clip) const;
bool has_transition(int p_from_clip, int p_to_clip) const;
void erase_transition(int p_from_clip, int p_to_clip);
PackedInt32Array get_transition_list() const;
virtual Ref<AudioStreamPlayback> instantiate_playback() override;
virtual String get_stream_name() const override;
virtual double get_length() const override { return 0; }
AudioStreamInteractive();
protected:
virtual void get_parameter_list(List<Parameter> *r_parameters) override;
static void _bind_methods();
void _validate_property(PropertyInfo &r_property) const;
};
VARIANT_ENUM_CAST(AudioStreamInteractive::TransitionFromTime)
VARIANT_ENUM_CAST(AudioStreamInteractive::TransitionToTime)
VARIANT_ENUM_CAST(AudioStreamInteractive::AutoAdvanceMode)
VARIANT_ENUM_CAST(AudioStreamInteractive::FadeMode)
class AudioStreamPlaybackInteractive : public AudioStreamPlayback {
GDCLASS(AudioStreamPlaybackInteractive, AudioStreamPlayback)
friend class AudioStreamInteractive;
private:
Ref<AudioStreamInteractive> stream;
uint64_t version = 0;
enum {
BUFFER_SIZE = 1024
};
AudioFrame mix_buffer[BUFFER_SIZE];
AudioFrame temp_buffer[BUFFER_SIZE];
struct State {
Ref<AudioStream> stream;
Ref<AudioStreamPlayback> playback;
bool active = false;
double fade_wait = 0; // Time to wait until fade kicks-in
double fade_volume = 1.0;
double fade_speed = 0; // Fade speed, negative or positive
int auto_advance = -1;
bool first_mix = true;
double previous_position = 0;
void reset_fade() {
fade_wait = 0;
fade_volume = 1.0;
fade_speed = 0;
}
};
State states[AudioStreamInteractive::MAX_CLIPS];
int playback_current = -1;
bool active = false;
int return_memory = -1;
void _mix_internal(int p_frames);
void _mix_internal_state(int p_state_idx, int p_frames);
void _queue(int p_to_clip_index, bool p_is_auto_advance);
int switch_request = -1;
protected:
static void _bind_methods();
public:
virtual void start(double p_from_pos = 0.0) override;
virtual void stop() override;
virtual bool is_playing() const override;
virtual int get_loop_count() const override; // times it looped
virtual double get_playback_position() const override;
virtual void seek(double p_time) override;
virtual int mix(AudioFrame *p_buffer, float p_rate_scale, int p_frames) override;
virtual void tag_used_streams() override;
void switch_to_clip_by_name(const StringName &p_name);
void switch_to_clip(int p_index);
virtual void set_parameter(const StringName &p_name, const Variant &p_value) override;
virtual Variant get_parameter(const StringName &p_name) const override;
AudioStreamPlaybackInteractive();
~AudioStreamPlaybackInteractive();
};
#endif // AUDIO_STREAM_INTERACTIVE_H