summaryrefslogtreecommitdiff
path: root/servers/audio
diff options
context:
space:
mode:
authorreduz <reduzio@gmail.com>2022-07-21 01:00:58 +0200
committerJuan Linietsky <reduzio@gmail.com>2022-07-23 07:31:17 +0200
commitd1ddee225830b28171de031bd1f1918ced21b38f (patch)
tree59fbad7454e47cfc0f746842108d3d6e10b83a40 /servers/audio
parent976cb7ea9f59813f99e06c4c345c19ff68c2c591 (diff)
Implement BPM support
Based on #62896, only implements the BPM support part. * Implements BPM support in the AudioStreamOGG/MP3 importers. * Can select BPM/Bar Size and total beats in a song file, as well as edit looping points. * Looping is now BPM aware * Added a special importer UI for configuring this. * Added a special preview showing the audio waveform as well as the playback position in the resource picker. * Renamed `AudioStream::instance` to `instantiate` for correctness.
Diffstat (limited to 'servers/audio')
-rw-r--r--servers/audio/audio_stream.cpp96
-rw-r--r--servers/audio/audio_stream.h43
-rw-r--r--servers/audio/effects/audio_stream_generator.cpp6
-rw-r--r--servers/audio/effects/audio_stream_generator.h4
4 files changed, 129 insertions, 20 deletions
diff --git a/servers/audio/audio_stream.cpp b/servers/audio/audio_stream.cpp
index 0408db2539..80485845c9 100644
--- a/servers/audio/audio_stream.cpp
+++ b/servers/audio/audio_stream.cpp
@@ -83,6 +83,10 @@ int AudioStreamPlayback::mix(AudioFrame *p_buffer, float p_rate_scale, int p_fra
return 0;
}
+void AudioStreamPlayback::tag_used_streams() {
+ GDVIRTUAL_CALL(_tag_used_streams);
+}
+
void AudioStreamPlayback::_bind_methods() {
GDVIRTUAL_BIND(_start, "from_pos")
GDVIRTUAL_BIND(_stop)
@@ -91,6 +95,7 @@ void AudioStreamPlayback::_bind_methods() {
GDVIRTUAL_BIND(_get_playback_position)
GDVIRTUAL_BIND(_seek, "position")
GDVIRTUAL_BIND(_mix, "buffer", "rate_scale", "frames");
+ GDVIRTUAL_BIND(_tag_used_streams);
}
//////////////////////////////
@@ -187,9 +192,9 @@ int AudioStreamPlaybackResampled::mix(AudioFrame *p_buffer, float p_rate_scale,
////////////////////////////////
-Ref<AudioStreamPlayback> AudioStream::instance_playback() {
+Ref<AudioStreamPlayback> AudioStream::instantiate_playback() {
Ref<AudioStreamPlayback> ret;
- if (GDVIRTUAL_CALL(_instance_playback, ret)) {
+ if (GDVIRTUAL_CALL(_instantiate_playback, ret)) {
return ret;
}
ERR_FAIL_V_MSG(Ref<AudioStreamPlayback>(), "Method must be implemented!");
@@ -218,19 +223,74 @@ bool AudioStream::is_monophonic() const {
return true;
}
+double AudioStream::get_bpm() const {
+ double ret = 0;
+ if (GDVIRTUAL_CALL(_get_bpm, ret)) {
+ return ret;
+ }
+ return 0;
+}
+
+bool AudioStream::has_loop() const {
+ bool ret = 0;
+ if (GDVIRTUAL_CALL(_has_loop, ret)) {
+ return ret;
+ }
+ return 0;
+}
+
+int AudioStream::get_bar_beats() const {
+ int ret = 0;
+ if (GDVIRTUAL_CALL(_get_bar_beats, ret)) {
+ return ret;
+ }
+ return 0;
+}
+
+int AudioStream::get_beat_count() const {
+ int ret = 0;
+ if (GDVIRTUAL_CALL(_get_beat_count, ret)) {
+ return ret;
+ }
+ return 0;
+}
+
+void AudioStream::tag_used(float p_offset) {
+ if (tagged_frame != AudioServer::get_singleton()->get_mixed_frames()) {
+ offset_count = 0;
+ tagged_frame = AudioServer::get_singleton()->get_mixed_frames();
+ }
+ if (offset_count < MAX_TAGGED_OFFSETS) {
+ tagged_offsets[offset_count++] = p_offset;
+ }
+}
+
+uint64_t AudioStream::get_tagged_frame() const {
+ return tagged_frame;
+}
+uint32_t AudioStream::get_tagged_frame_count() const {
+ return offset_count;
+}
+float AudioStream::get_tagged_frame_offset(int p_index) const {
+ ERR_FAIL_INDEX_V(p_index, MAX_TAGGED_OFFSETS, 0);
+ return tagged_offsets[p_index];
+}
+
void AudioStream::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_length"), &AudioStream::get_length);
ClassDB::bind_method(D_METHOD("is_monophonic"), &AudioStream::is_monophonic);
- ClassDB::bind_method(D_METHOD("instance_playback"), &AudioStream::instance_playback);
- GDVIRTUAL_BIND(_instance_playback);
+ ClassDB::bind_method(D_METHOD("instantiate_playback"), &AudioStream::instantiate_playback);
+ GDVIRTUAL_BIND(_instantiate_playback);
GDVIRTUAL_BIND(_get_stream_name);
GDVIRTUAL_BIND(_get_length);
GDVIRTUAL_BIND(_is_monophonic);
+ GDVIRTUAL_BIND(_get_bpm)
+ GDVIRTUAL_BIND(_get_beat_count)
}
////////////////////////////////
-Ref<AudioStreamPlayback> AudioStreamMicrophone::instance_playback() {
+Ref<AudioStreamPlayback> AudioStreamMicrophone::instantiate_playback() {
Ref<AudioStreamPlaybackMicrophone> playback;
playback.instantiate();
@@ -363,6 +423,10 @@ void AudioStreamPlaybackMicrophone::seek(float p_time) {
// Can't seek a microphone input
}
+void AudioStreamPlaybackMicrophone::tag_used_streams() {
+ microphone->tag_used(0);
+}
+
AudioStreamPlaybackMicrophone::~AudioStreamPlaybackMicrophone() {
microphone->playbacks.erase(this);
stop();
@@ -490,7 +554,7 @@ Ref<AudioStreamPlayback> AudioStreamRandomizer::instance_playback_random() {
for (PoolEntry &entry : local_pool) {
cumulative_weight += entry.weight;
if (cumulative_weight > chosen_cumulative_weight) {
- playback->playback = entry.stream->instance_playback();
+ playback->playback = entry.stream->instantiate_playback();
last_playback = entry.stream;
break;
}
@@ -498,7 +562,7 @@ Ref<AudioStreamPlayback> AudioStreamRandomizer::instance_playback_random() {
if (playback->playback.is_null()) {
// This indicates a floating point error. Take the last element.
last_playback = local_pool[local_pool.size() - 1].stream;
- playback->playback = local_pool.write[local_pool.size() - 1].stream->instance_playback();
+ playback->playback = local_pool.write[local_pool.size() - 1].stream->instantiate_playback();
}
return playback;
}
@@ -532,14 +596,14 @@ Ref<AudioStreamPlayback> AudioStreamRandomizer::instance_playback_no_repeats() {
cumulative_weight += entry.weight;
if (cumulative_weight > chosen_cumulative_weight) {
last_playback = entry.stream;
- playback->playback = entry.stream->instance_playback();
+ playback->playback = entry.stream->instantiate_playback();
break;
}
}
if (playback->playback.is_null()) {
// This indicates a floating point error. Take the last element.
last_playback = local_pool[local_pool.size() - 1].stream;
- playback->playback = local_pool.write[local_pool.size() - 1].stream->instance_playback();
+ playback->playback = local_pool.write[local_pool.size() - 1].stream->instantiate_playback();
}
return playback;
}
@@ -568,7 +632,7 @@ Ref<AudioStreamPlayback> AudioStreamRandomizer::instance_playback_sequential() {
for (Ref<AudioStream> &entry : local_pool) {
if (found_last_stream) {
last_playback = entry;
- playback->playback = entry->instance_playback();
+ playback->playback = entry->instantiate_playback();
break;
}
if (entry == last_playback) {
@@ -578,12 +642,12 @@ Ref<AudioStreamPlayback> AudioStreamRandomizer::instance_playback_sequential() {
if (playback->playback.is_null()) {
// Wrap around
last_playback = local_pool[0];
- playback->playback = local_pool.write[0]->instance_playback();
+ playback->playback = local_pool.write[0]->instantiate_playback();
}
return playback;
}
-Ref<AudioStreamPlayback> AudioStreamRandomizer::instance_playback() {
+Ref<AudioStreamPlayback> AudioStreamRandomizer::instantiate_playback() {
switch (playback_mode) {
case PLAYBACK_RANDOM:
return instance_playback_random();
@@ -762,6 +826,14 @@ void AudioStreamPlaybackRandomizer::seek(float p_time) {
}
}
+void AudioStreamPlaybackRandomizer::tag_used_streams() {
+ Ref<AudioStreamPlayback> p = playing; // Thread safety
+ if (p.is_valid()) {
+ p->tag_used_streams();
+ }
+ randomizer->tag_used(0);
+}
+
int AudioStreamPlaybackRandomizer::mix(AudioFrame *p_buffer, float p_rate_scale, int p_frames) {
if (playing.is_valid()) {
return playing->mix(p_buffer, p_rate_scale * pitch_scale, p_frames);
diff --git a/servers/audio/audio_stream.h b/servers/audio/audio_stream.h
index bf200e7ecf..7c4577977d 100644
--- a/servers/audio/audio_stream.h
+++ b/servers/audio/audio_stream.h
@@ -40,6 +40,8 @@
#include "core/object/script_language.h"
#include "core/variant/native_ptr.h"
+class AudioStream;
+
class AudioStreamPlayback : public RefCounted {
GDCLASS(AudioStreamPlayback, RefCounted);
@@ -52,6 +54,7 @@ protected:
GDVIRTUAL0RC(float, _get_playback_position)
GDVIRTUAL1(_seek, float)
GDVIRTUAL3R(int, _mix, GDNativePtr<AudioFrame>, float, int)
+ GDVIRTUAL0(_tag_used_streams)
public:
virtual void start(float p_from_pos = 0.0);
virtual void stop();
@@ -62,6 +65,8 @@ public:
virtual float get_playback_position() const;
virtual void seek(float p_time);
+ virtual void tag_used_streams();
+
virtual int mix(AudioFrame *p_buffer, float p_rate_scale, int p_frames);
};
@@ -72,7 +77,7 @@ class AudioStreamPlaybackResampled : public AudioStreamPlayback {
FP_BITS = 16, //fixed point used for resampling
FP_LEN = (1 << FP_BITS),
FP_MASK = FP_LEN - 1,
- INTERNAL_BUFFER_LEN = 256,
+ INTERNAL_BUFFER_LEN = 128, // 128 warrants 3ms positional jitter at much at 44100hz
CUBIC_INTERP_HISTORY = 4
};
@@ -101,20 +106,42 @@ class AudioStream : public Resource {
GDCLASS(AudioStream, Resource);
OBJ_SAVE_TYPE(AudioStream); // Saves derived classes with common type so they can be interchanged.
+ enum {
+ MAX_TAGGED_OFFSETS = 8
+ };
+
+ uint64_t tagged_frame = 0;
+ uint64_t offset_count = 0;
+ float tagged_offsets[MAX_TAGGED_OFFSETS];
+
protected:
static void _bind_methods();
- GDVIRTUAL0RC(Ref<AudioStreamPlayback>, _instance_playback)
+ GDVIRTUAL0RC(Ref<AudioStreamPlayback>, _instantiate_playback)
GDVIRTUAL0RC(String, _get_stream_name)
GDVIRTUAL0RC(float, _get_length)
GDVIRTUAL0RC(bool, _is_monophonic)
+ GDVIRTUAL0RC(double, _get_bpm)
+ GDVIRTUAL0RC(bool, _has_loop)
+ GDVIRTUAL0RC(int, _get_bar_beats)
+ GDVIRTUAL0RC(int, _get_beat_count)
public:
- virtual Ref<AudioStreamPlayback> instance_playback();
+ virtual Ref<AudioStreamPlayback> instantiate_playback();
virtual String get_stream_name() const;
+ virtual double get_bpm() const;
+ virtual bool has_loop() const;
+ virtual int get_bar_beats() const;
+ virtual int get_beat_count() const;
+
virtual float get_length() const;
virtual bool is_monophonic() const;
+
+ void tag_used(float p_offset);
+ uint64_t get_tagged_frame() const;
+ uint32_t get_tagged_frame_count() const;
+ float get_tagged_frame_offset(int p_index) const;
};
// Microphone
@@ -131,7 +158,7 @@ protected:
static void _bind_methods();
public:
- virtual Ref<AudioStreamPlayback> instance_playback() override;
+ virtual Ref<AudioStreamPlayback> instantiate_playback() override;
virtual String get_stream_name() const override;
virtual float get_length() const override; //if supported, otherwise return 0
@@ -153,6 +180,7 @@ class AudioStreamPlaybackMicrophone : public AudioStreamPlaybackResampled {
protected:
virtual int _mix_internal(AudioFrame *p_buffer, int p_frames) override;
virtual float get_stream_sampling_rate() override;
+ virtual float get_playback_position() const override;
public:
virtual int mix(AudioFrame *p_buffer, float p_rate_scale, int p_frames) override;
@@ -163,9 +191,10 @@ public:
virtual int get_loop_count() const override; //times it looped
- virtual float get_playback_position() const override;
virtual void seek(float p_time) override;
+ virtual void tag_used_streams() override;
+
~AudioStreamPlaybackMicrophone();
AudioStreamPlaybackMicrophone();
};
@@ -233,7 +262,7 @@ public:
void set_playback_mode(PlaybackMode p_playback_mode);
PlaybackMode get_playback_mode() const;
- virtual Ref<AudioStreamPlayback> instance_playback() override;
+ virtual Ref<AudioStreamPlayback> instantiate_playback() override;
virtual String get_stream_name() const override;
virtual float get_length() const override; //if supported, otherwise return 0
@@ -265,6 +294,8 @@ public:
virtual int mix(AudioFrame *p_buffer, float p_rate_scale, int p_frames) override;
+ virtual void tag_used_streams() override;
+
~AudioStreamPlaybackRandomizer();
};
diff --git a/servers/audio/effects/audio_stream_generator.cpp b/servers/audio/effects/audio_stream_generator.cpp
index 46de1692e4..6365dacc80 100644
--- a/servers/audio/effects/audio_stream_generator.cpp
+++ b/servers/audio/effects/audio_stream_generator.cpp
@@ -46,7 +46,7 @@ float AudioStreamGenerator::get_buffer_length() const {
return buffer_len;
}
-Ref<AudioStreamPlayback> AudioStreamGenerator::instance_playback() {
+Ref<AudioStreamPlayback> AudioStreamGenerator::instantiate_playback() {
Ref<AudioStreamGeneratorPlayback> playback;
playback.instantiate();
playback->generator = this;
@@ -196,6 +196,10 @@ void AudioStreamGeneratorPlayback::seek(float p_time) {
//no seek possible
}
+void AudioStreamGeneratorPlayback::tag_used_streams() {
+ generator->tag_used(0);
+}
+
void AudioStreamGeneratorPlayback::_bind_methods() {
ClassDB::bind_method(D_METHOD("push_frame", "frame"), &AudioStreamGeneratorPlayback::push_frame);
ClassDB::bind_method(D_METHOD("can_push_buffer", "amount"), &AudioStreamGeneratorPlayback::can_push_buffer);
diff --git a/servers/audio/effects/audio_stream_generator.h b/servers/audio/effects/audio_stream_generator.h
index 2ce4b95fcf..0394c3c6a9 100644
--- a/servers/audio/effects/audio_stream_generator.h
+++ b/servers/audio/effects/audio_stream_generator.h
@@ -50,7 +50,7 @@ public:
void set_buffer_length(float p_seconds);
float get_buffer_length() const;
- virtual Ref<AudioStreamPlayback> instance_playback() override;
+ virtual Ref<AudioStreamPlayback> instantiate_playback() override;
virtual String get_stream_name() const override;
virtual float get_length() const override;
@@ -89,6 +89,8 @@ public:
int get_frames_available() const;
int get_skips() const;
+ virtual void tag_used_streams() override;
+
void clear_buffer();
AudioStreamGeneratorPlayback();