Path: blob/master/modules/interactive_music/audio_stream_synchronized.cpp
10277 views
/**************************************************************************/1/* audio_stream_synchronized.cpp */2/**************************************************************************/3/* This file is part of: */4/* GODOT ENGINE */5/* https://godotengine.org */6/**************************************************************************/7/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */8/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */9/* */10/* Permission is hereby granted, free of charge, to any person obtaining */11/* a copy of this software and associated documentation files (the */12/* "Software"), to deal in the Software without restriction, including */13/* without limitation the rights to use, copy, modify, merge, publish, */14/* distribute, sublicense, and/or sell copies of the Software, and to */15/* permit persons to whom the Software is furnished to do so, subject to */16/* the following conditions: */17/* */18/* The above copyright notice and this permission notice shall be */19/* included in all copies or substantial portions of the Software. */20/* */21/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */22/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */23/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */24/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */25/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */26/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */27/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */28/**************************************************************************/2930#include "audio_stream_synchronized.h"3132#include "core/math/math_funcs.h"3334AudioStreamSynchronized::AudioStreamSynchronized() {35}3637Ref<AudioStreamPlayback> AudioStreamSynchronized::instantiate_playback() {38Ref<AudioStreamPlaybackSynchronized> playback_playlist;39playback_playlist.instantiate();40playback_playlist->stream = Ref<AudioStreamSynchronized>(this);41playback_playlist->_update_playback_instances();42playbacks.insert(playback_playlist.operator->());43return playback_playlist;44}4546String AudioStreamSynchronized::get_stream_name() const {47return "Synchronized";48}4950void AudioStreamSynchronized::set_sync_stream(int p_stream_index, Ref<AudioStream> p_stream) {51ERR_FAIL_COND(p_stream == this);52ERR_FAIL_INDEX(p_stream_index, MAX_STREAMS);5354AudioServer::get_singleton()->lock();55audio_streams[p_stream_index] = p_stream;56for (AudioStreamPlaybackSynchronized *E : playbacks) {57E->_update_playback_instances();58}59AudioServer::get_singleton()->unlock();60}6162Ref<AudioStream> AudioStreamSynchronized::get_sync_stream(int p_stream_index) const {63ERR_FAIL_INDEX_V(p_stream_index, MAX_STREAMS, Ref<AudioStream>());6465return audio_streams[p_stream_index];66}6768void AudioStreamSynchronized::set_sync_stream_volume(int p_stream_index, float p_db) {69ERR_FAIL_INDEX(p_stream_index, MAX_STREAMS);70audio_stream_volume_db[p_stream_index] = p_db;71}7273float AudioStreamSynchronized::get_sync_stream_volume(int p_stream_index) const {74ERR_FAIL_INDEX_V(p_stream_index, MAX_STREAMS, 0);75return audio_stream_volume_db[p_stream_index];76}7778double AudioStreamSynchronized::get_bpm() const {79for (int i = 0; i < stream_count; i++) {80if (audio_streams[i].is_valid()) {81double bpm = audio_streams[i]->get_bpm();82if (bpm != 0.0) {83return bpm;84}85}86}87return 0.0;88}8990int AudioStreamSynchronized::get_beat_count() const {91int max_beats = 0;92for (int i = 0; i < stream_count; i++) {93if (audio_streams[i].is_valid()) {94max_beats = MAX(max_beats, audio_streams[i]->get_beat_count());95}96}97return max_beats;98}99100int AudioStreamSynchronized::get_bar_beats() const {101for (int i = 0; i < stream_count; i++) {102if (audio_streams[i].is_valid()) {103int bar_beats = audio_streams[i]->get_bar_beats();104if (bar_beats != 0) {105return bar_beats;106}107}108}109return 0;110}111112bool AudioStreamSynchronized::has_loop() const {113for (int i = 0; i < stream_count; i++) {114if (audio_streams[i].is_valid()) {115if (audio_streams[i]->has_loop()) {116return true;117}118}119}120return false;121}122123double AudioStreamSynchronized::get_length() const {124double max_length = 0.0;125for (int i = 0; i < stream_count; i++) {126if (audio_streams[i].is_valid()) {127max_length = MAX(max_length, audio_streams[i]->get_length());128}129}130return max_length;131}132133void AudioStreamSynchronized::set_stream_count(int p_count) {134ERR_FAIL_COND(p_count < 0 || p_count > MAX_STREAMS);135AudioServer::get_singleton()->lock();136stream_count = p_count;137AudioServer::get_singleton()->unlock();138notify_property_list_changed();139}140141int AudioStreamSynchronized::get_stream_count() const {142return stream_count;143}144145void AudioStreamSynchronized::_validate_property(PropertyInfo &property) const {146String prop = property.name;147if (prop != "stream_count" && prop.begins_with("stream_")) {148int stream = prop.get_slicec('/', 0).get_slicec('_', 1).to_int();149if (stream >= stream_count) {150property.usage = PROPERTY_USAGE_INTERNAL;151}152}153}154155void AudioStreamSynchronized::_bind_methods() {156ClassDB::bind_method(D_METHOD("set_stream_count", "stream_count"), &AudioStreamSynchronized::set_stream_count);157ClassDB::bind_method(D_METHOD("get_stream_count"), &AudioStreamSynchronized::get_stream_count);158159ClassDB::bind_method(D_METHOD("set_sync_stream", "stream_index", "audio_stream"), &AudioStreamSynchronized::set_sync_stream);160ClassDB::bind_method(D_METHOD("get_sync_stream", "stream_index"), &AudioStreamSynchronized::get_sync_stream);161ClassDB::bind_method(D_METHOD("set_sync_stream_volume", "stream_index", "volume_db"), &AudioStreamSynchronized::set_sync_stream_volume);162ClassDB::bind_method(D_METHOD("get_sync_stream_volume", "stream_index"), &AudioStreamSynchronized::get_sync_stream_volume);163164ADD_PROPERTY(PropertyInfo(Variant::INT, "stream_count", PROPERTY_HINT_RANGE, "0," + itos(MAX_STREAMS), PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_ARRAY, "Streams,stream_,unfoldable,page_size=999,add_button_text=" + String(TTRC("Add Stream"))), "set_stream_count", "get_stream_count");165166for (int i = 0; i < MAX_STREAMS; i++) {167ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "stream_" + itos(i) + "/stream", PROPERTY_HINT_RESOURCE_TYPE, "AudioStream", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_INTERNAL), "set_sync_stream", "get_sync_stream", i);168ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "stream_" + itos(i) + "/volume", PROPERTY_HINT_RANGE, "-60,12,0.01,suffix:db", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_INTERNAL), "set_sync_stream_volume", "get_sync_stream_volume", i);169}170171BIND_CONSTANT(MAX_STREAMS);172}173174//////////////////////175//////////////////////176177AudioStreamPlaybackSynchronized::AudioStreamPlaybackSynchronized() {178}179180AudioStreamPlaybackSynchronized::~AudioStreamPlaybackSynchronized() {181if (stream.is_valid()) {182stream->playbacks.erase(this);183}184}185186void AudioStreamPlaybackSynchronized::stop() {187active = false;188for (int i = 0; i < stream->stream_count; i++) {189if (playback[i].is_valid()) {190playback[i]->stop();191}192}193}194195void AudioStreamPlaybackSynchronized::start(double p_from_pos) {196if (active) {197stop();198}199200for (int i = 0; i < stream->stream_count; i++) {201if (playback[i].is_valid()) {202playback[i]->start(p_from_pos);203active = true;204}205}206}207208void AudioStreamPlaybackSynchronized::seek(double p_time) {209for (int i = 0; i < stream->stream_count; i++) {210if (playback[i].is_valid()) {211playback[i]->seek(p_time);212}213}214}215216int AudioStreamPlaybackSynchronized::mix(AudioFrame *p_buffer, float p_rate_scale, int p_frames) {217if (!active) {218return 0;219}220221int todo = p_frames;222223bool any_active = false;224while (todo) {225int to_mix = MIN(todo, MIX_BUFFER_SIZE);226227bool first = true;228for (int i = 0; i < stream->stream_count; i++) {229if (playback[i].is_valid() && playback[i]->is_playing()) {230float volume = Math::db_to_linear(stream->audio_stream_volume_db[i]);231if (first) {232playback[i]->mix(p_buffer, p_rate_scale, to_mix);233for (int j = 0; j < to_mix; j++) {234p_buffer[j] *= volume;235}236first = false;237any_active = true;238} else {239playback[i]->mix(mix_buffer, p_rate_scale, to_mix);240for (int j = 0; j < to_mix; j++) {241p_buffer[j] += mix_buffer[j] * volume;242}243}244}245}246247if (first) {248// Nothing mixed, put zeroes.249for (int j = 0; j < to_mix; j++) {250p_buffer[j] = AudioFrame(0, 0);251}252}253254p_buffer += to_mix;255todo -= to_mix;256}257258if (!any_active) {259active = false;260}261return p_frames;262}263264void AudioStreamPlaybackSynchronized::tag_used_streams() {265if (active) {266for (int i = 0; i < stream->stream_count; i++) {267if (playback[i].is_valid() && playback[i]->is_playing()) {268stream->audio_streams[i]->tag_used(playback[i]->get_playback_position());269}270}271stream->tag_used(0);272}273}274275int AudioStreamPlaybackSynchronized::get_loop_count() const {276int min_loops = 0;277bool min_loops_found = false;278for (int i = 0; i < stream->stream_count; i++) {279if (playback[i].is_valid() && playback[i]->is_playing()) {280int loops = playback[i]->get_loop_count();281if (!min_loops_found || loops < min_loops) {282min_loops = loops;283min_loops_found = true;284}285}286}287return min_loops;288}289290double AudioStreamPlaybackSynchronized::get_playback_position() const {291float max_pos = 0;292bool pos_found = false;293for (int i = 0; i < stream->stream_count; i++) {294if (playback[i].is_valid() && playback[i]->is_playing()) {295float pos = playback[i]->get_playback_position();296if (!pos_found || pos > max_pos) {297max_pos = pos;298pos_found = true;299}300}301}302return max_pos;303}304305bool AudioStreamPlaybackSynchronized::is_playing() const {306return active;307}308309void AudioStreamPlaybackSynchronized::_update_playback_instances() {310stop();311312for (int i = 0; i < stream->stream_count; i++) {313if (stream->audio_streams[i].is_valid()) {314playback[i] = stream->audio_streams[i]->instantiate_playback();315} else {316playback[i].unref();317}318}319}320321322