diff options
Diffstat (limited to 'servers/audio')
-rw-r--r-- | servers/audio/audio_stream.cpp | 145 | ||||
-rw-r--r-- | servers/audio/audio_stream.h | 54 | ||||
-rw-r--r-- | servers/audio/effects/audio_effect_capture.h | 1 | ||||
-rw-r--r-- | servers/audio/effects/audio_effect_pitch_shift.cpp | 2 | ||||
-rw-r--r-- | servers/audio/effects/audio_stream_generator.cpp | 7 | ||||
-rw-r--r-- | servers/audio/effects/audio_stream_generator.h | 3 |
6 files changed, 187 insertions, 25 deletions
diff --git a/servers/audio/audio_stream.cpp b/servers/audio/audio_stream.cpp index f3fa857682..c098a97906 100644 --- a/servers/audio/audio_stream.cpp +++ b/servers/audio/audio_stream.cpp @@ -33,6 +33,65 @@ #include "core/config/project_settings.h" #include "core/os/os.h" +void AudioStreamPlayback::start(float p_from_pos) { + if (GDVIRTUAL_CALL(_start, p_from_pos)) { + return; + } + ERR_FAIL_MSG("AudioStreamPlayback::start unimplemented!"); +} +void AudioStreamPlayback::stop() { + if (GDVIRTUAL_CALL(_stop)) { + return; + } + ERR_FAIL_MSG("AudioStreamPlayback::stop unimplemented!"); +} +bool AudioStreamPlayback::is_playing() const { + bool ret; + if (GDVIRTUAL_CALL(_is_playing, ret)) { + return ret; + } + ERR_FAIL_V_MSG(false, "AudioStreamPlayback::is_playing unimplemented!"); +} + +int AudioStreamPlayback::get_loop_count() const { + int ret; + if (GDVIRTUAL_CALL(_get_loop_count, ret)) { + return ret; + } + return 0; +} + +float AudioStreamPlayback::get_playback_position() const { + float ret; + if (GDVIRTUAL_CALL(_get_playback_position, ret)) { + return ret; + } + ERR_FAIL_V_MSG(0, "AudioStreamPlayback::get_playback_position unimplemented!"); +} +void AudioStreamPlayback::seek(float p_time) { + if (GDVIRTUAL_CALL(_seek, p_time)) { + return; + } +} + +int AudioStreamPlayback::mix(AudioFrame *p_buffer, float p_rate_scale, int p_frames) { + int ret; + if (GDVIRTUAL_CALL(_mix, p_buffer, p_rate_scale, p_frames, ret)) { + return ret; + } + WARN_PRINT_ONCE("AudioStreamPlayback::mix unimplemented!"); + return 0; +} + +void AudioStreamPlayback::_bind_methods() { + GDVIRTUAL_BIND(_start, "from_pos") + GDVIRTUAL_BIND(_stop) + GDVIRTUAL_BIND(_is_playing) + GDVIRTUAL_BIND(_get_loop_count) + GDVIRTUAL_BIND(_get_playback_position) + GDVIRTUAL_BIND(_seek, "position") + GDVIRTUAL_BIND(_mix, "buffer", "rate_scale", "frames"); +} ////////////////////////////// void AudioStreamPlaybackResampled::_begin_resample() { @@ -46,12 +105,14 @@ void AudioStreamPlaybackResampled::_begin_resample() { mix_offset = 0; } -void AudioStreamPlaybackResampled::mix(AudioFrame *p_buffer, float p_rate_scale, int p_frames) { +int AudioStreamPlaybackResampled::mix(AudioFrame *p_buffer, float p_rate_scale, int p_frames) { float target_rate = AudioServer::get_singleton()->get_mix_rate(); float playback_speed_scale = AudioServer::get_singleton()->get_playback_speed_scale(); uint64_t mix_increment = uint64_t(((get_stream_sampling_rate() * p_rate_scale * playback_speed_scale) / double(target_rate)) * double(FP_LEN)); + int mixed_frames_total = p_frames; + for (int i = 0; i < p_frames; i++) { uint32_t idx = CUBIC_INTERP_HISTORY + uint32_t(mix_offset >> FP_BITS); //standard cubic interpolation (great quality/performance ratio) @@ -62,6 +123,11 @@ void AudioStreamPlaybackResampled::mix(AudioFrame *p_buffer, float p_rate_scale, AudioFrame y2 = internal_buffer[idx - 1]; AudioFrame y3 = internal_buffer[idx - 0]; + if (idx <= internal_buffer_end && idx >= internal_buffer_end && mixed_frames_total == p_frames) { + // The internal buffer ends somewhere in this range, and we haven't yet recorded the number of good frames we have. + mixed_frames_total = i; + } + float mu2 = mu * mu; AudioFrame a0 = 3 * y1 - 3 * y2 + y3 - y0; AudioFrame a1 = 2 * y0 - 5 * y1 + 4 * y2 - y3; @@ -78,7 +144,14 @@ void AudioStreamPlaybackResampled::mix(AudioFrame *p_buffer, float p_rate_scale, internal_buffer[2] = internal_buffer[INTERNAL_BUFFER_LEN + 2]; internal_buffer[3] = internal_buffer[INTERNAL_BUFFER_LEN + 3]; if (is_playing()) { - _mix_internal(internal_buffer + 4, INTERNAL_BUFFER_LEN); + int mixed_frames = _mix_internal(internal_buffer + 4, INTERNAL_BUFFER_LEN); + if (mixed_frames != INTERNAL_BUFFER_LEN) { + // internal_buffer[mixed_frames] is the first frame of silence. + internal_buffer_end = mixed_frames; + } else { + // The internal buffer does not contain the first frame of silence. + internal_buffer_end = -1; + } } else { //fill with silence, not playing for (int j = 0; j < INTERNAL_BUFFER_LEN; ++j) { @@ -88,12 +161,49 @@ void AudioStreamPlaybackResampled::mix(AudioFrame *p_buffer, float p_rate_scale, mix_offset -= (INTERNAL_BUFFER_LEN << FP_BITS); } } + return mixed_frames_total; } //////////////////////////////// +Ref<AudioStreamPlayback> AudioStream::instance_playback() { + Ref<AudioStreamPlayback> ret; + if (GDVIRTUAL_CALL(_instance_playback, ret)) { + return ret; + } + ERR_FAIL_V_MSG(Ref<AudioStreamPlayback>(), "Method must be implemented!"); +} +String AudioStream::get_stream_name() const { + String ret; + if (GDVIRTUAL_CALL(_get_stream_name, ret)) { + return ret; + } + return String(); +} + +float AudioStream::get_length() const { + float ret; + if (GDVIRTUAL_CALL(_get_length, ret)) { + return ret; + } + return 0; +} + +bool AudioStream::is_monophonic() const { + bool ret; + if (GDVIRTUAL_CALL(_is_monophonic, ret)) { + return ret; + } + return true; +} + void AudioStream::_bind_methods() { ClassDB::bind_method(D_METHOD("get_length"), &AudioStream::get_length); + ClassDB::bind_method(D_METHOD("is_monophonic"), &AudioStream::is_monophonic); + GDVIRTUAL_BIND(_instance_playback); + GDVIRTUAL_BIND(_get_stream_name); + GDVIRTUAL_BIND(_get_length); + GDVIRTUAL_BIND(_is_monophonic); } //////////////////////////////// @@ -121,13 +231,17 @@ float AudioStreamMicrophone::get_length() const { return 0; } +bool AudioStreamMicrophone::is_monophonic() const { + return true; +} + void AudioStreamMicrophone::_bind_methods() { } AudioStreamMicrophone::AudioStreamMicrophone() { } -void AudioStreamPlaybackMicrophone::_mix_internal(AudioFrame *p_buffer, int p_frames) { +int AudioStreamPlaybackMicrophone::_mix_internal(AudioFrame *p_buffer, int p_frames) { AudioDriver::get_singleton()->lock(); Vector<int32_t> buf = AudioDriver::get_singleton()->get_input_buffer(); @@ -138,6 +252,8 @@ void AudioStreamPlaybackMicrophone::_mix_internal(AudioFrame *p_buffer, int p_fr unsigned int input_position = AudioDriver::get_singleton()->get_input_position(); #endif + int mixed_frames = p_frames; + if (playback_delay > input_size) { for (int i = 0; i < p_frames; i++) { p_buffer[i] = AudioFrame(0.0f, 0.0f); @@ -157,6 +273,9 @@ void AudioStreamPlaybackMicrophone::_mix_internal(AudioFrame *p_buffer, int p_fr p_buffer[i] = AudioFrame(l, r); } else { + if (mixed_frames == p_frames) { + mixed_frames = i; + } p_buffer[i] = AudioFrame(0.0f, 0.0f); } } @@ -169,10 +288,12 @@ void AudioStreamPlaybackMicrophone::_mix_internal(AudioFrame *p_buffer, int p_fr #endif AudioDriver::get_singleton()->unlock(); + + return mixed_frames; } -void AudioStreamPlaybackMicrophone::mix(AudioFrame *p_buffer, float p_rate_scale, int p_frames) { - AudioStreamPlaybackResampled::mix(p_buffer, p_rate_scale, p_frames); +int AudioStreamPlaybackMicrophone::mix(AudioFrame *p_buffer, float p_rate_scale, int p_frames) { + return AudioStreamPlaybackResampled::mix(p_buffer, p_rate_scale, p_frames); } float AudioStreamPlaybackMicrophone::get_stream_sampling_rate() { @@ -281,6 +402,14 @@ float AudioStreamRandomPitch::get_length() const { return 0; } +bool AudioStreamRandomPitch::is_monophonic() const { + if (audio_stream.is_valid()) { + return audio_stream->is_monophonic(); + } + + return true; // It doesn't really matter what we return here, but no sense instancing a many playbacks of a null stream. +} + void AudioStreamRandomPitch::_bind_methods() { ClassDB::bind_method(D_METHOD("set_audio_stream", "stream"), &AudioStreamRandomPitch::set_audio_stream); ClassDB::bind_method(D_METHOD("get_audio_stream"), &AudioStreamRandomPitch::get_audio_stream); @@ -345,16 +474,18 @@ void AudioStreamPlaybackRandomPitch::seek(float p_time) { } } -void AudioStreamPlaybackRandomPitch::mix(AudioFrame *p_buffer, float p_rate_scale, int p_frames) { +int AudioStreamPlaybackRandomPitch::mix(AudioFrame *p_buffer, float p_rate_scale, int p_frames) { if (playing.is_valid()) { - playing->mix(p_buffer, p_rate_scale * pitch_scale, p_frames); + return playing->mix(p_buffer, p_rate_scale * pitch_scale, p_frames); } else { for (int i = 0; i < p_frames; i++) { p_buffer[i] = AudioFrame(0, 0); } + return p_frames; } } AudioStreamPlaybackRandomPitch::~AudioStreamPlaybackRandomPitch() { random_pitch->playbacks.erase(this); } +///////////////////////////////////////////// diff --git a/servers/audio/audio_stream.h b/servers/audio/audio_stream.h index 0d426f99b2..12d4343f5c 100644 --- a/servers/audio/audio_stream.h +++ b/servers/audio/audio_stream.h @@ -36,20 +36,33 @@ #include "servers/audio/audio_filter_sw.h" #include "servers/audio_server.h" +#include "core/object/gdvirtual.gen.inc" +#include "core/object/script_language.h" +#include "core/variant/native_ptr.h" + class AudioStreamPlayback : public RefCounted { GDCLASS(AudioStreamPlayback, RefCounted); +protected: + static void _bind_methods(); + GDVIRTUAL1(_start, float) + GDVIRTUAL0(_stop) + GDVIRTUAL0RC(bool, _is_playing) + GDVIRTUAL0RC(int, _get_loop_count) + GDVIRTUAL0RC(float, _get_playback_position) + GDVIRTUAL1(_seek, float) + GDVIRTUAL3R(int, _mix, GDNativePtr<AudioFrame>, float, int) public: - virtual void start(float p_from_pos = 0.0) = 0; - virtual void stop() = 0; - virtual bool is_playing() const = 0; + virtual void start(float p_from_pos = 0.0); + virtual void stop(); + virtual bool is_playing() const; - virtual int get_loop_count() const = 0; //times it looped + virtual int get_loop_count() const; //times it looped - virtual float get_playback_position() const = 0; - virtual void seek(float p_time) = 0; + virtual float get_playback_position() const; + virtual void seek(float p_time); - virtual void mix(AudioFrame *p_buffer, float p_rate_scale, int p_frames) = 0; + virtual int mix(AudioFrame *p_buffer, float p_rate_scale, int p_frames); }; class AudioStreamPlaybackResampled : public AudioStreamPlayback { @@ -64,15 +77,17 @@ class AudioStreamPlaybackResampled : public AudioStreamPlayback { }; AudioFrame internal_buffer[INTERNAL_BUFFER_LEN + CUBIC_INTERP_HISTORY]; + unsigned int internal_buffer_end = -1; uint64_t mix_offset; protected: void _begin_resample(); - virtual void _mix_internal(AudioFrame *p_buffer, int p_frames) = 0; + // Returns the number of frames that were mixed. + virtual int _mix_internal(AudioFrame *p_buffer, int p_frames) = 0; virtual float get_stream_sampling_rate() = 0; public: - virtual void mix(AudioFrame *p_buffer, float p_rate_scale, int p_frames) override; + virtual int mix(AudioFrame *p_buffer, float p_rate_scale, int p_frames) override; AudioStreamPlaybackResampled() { mix_offset = 0; } }; @@ -84,11 +99,17 @@ class AudioStream : public Resource { protected: static void _bind_methods(); + GDVIRTUAL0RC(Ref<AudioStreamPlayback>, _instance_playback) + GDVIRTUAL0RC(String, _get_stream_name) + GDVIRTUAL0RC(float, _get_length) + GDVIRTUAL0RC(bool, _is_monophonic) + public: - virtual Ref<AudioStreamPlayback> instance_playback() = 0; - virtual String get_stream_name() const = 0; + virtual Ref<AudioStreamPlayback> instance_playback(); + virtual String get_stream_name() const; - virtual float get_length() const = 0; //if supported, otherwise return 0 + virtual float get_length() const; + virtual bool is_monophonic() const; }; // Microphone @@ -110,6 +131,8 @@ public: virtual float get_length() const override; //if supported, otherwise return 0 + virtual bool is_monophonic() const override; + AudioStreamMicrophone(); }; @@ -123,11 +146,11 @@ class AudioStreamPlaybackMicrophone : public AudioStreamPlaybackResampled { Ref<AudioStreamMicrophone> microphone; protected: - virtual void _mix_internal(AudioFrame *p_buffer, int p_frames) override; + virtual int _mix_internal(AudioFrame *p_buffer, int p_frames) override; virtual float get_stream_sampling_rate() override; public: - virtual void mix(AudioFrame *p_buffer, float p_rate_scale, int p_frames) override; + virtual int mix(AudioFrame *p_buffer, float p_rate_scale, int p_frames) override; virtual void start(float p_from_pos = 0.0) override; virtual void stop() override; @@ -168,6 +191,7 @@ public: virtual String get_stream_name() const override; virtual float get_length() const override; //if supported, otherwise return 0 + virtual bool is_monophonic() const override; AudioStreamRandomPitch(); }; @@ -191,7 +215,7 @@ public: virtual float get_playback_position() const override; virtual void seek(float p_time) override; - virtual void mix(AudioFrame *p_buffer, float p_rate_scale, int p_frames) override; + virtual int mix(AudioFrame *p_buffer, float p_rate_scale, int p_frames) override; ~AudioStreamPlaybackRandomPitch(); }; diff --git a/servers/audio/effects/audio_effect_capture.h b/servers/audio/effects/audio_effect_capture.h index 7f50fc4965..bb1d03be8c 100644 --- a/servers/audio/effects/audio_effect_capture.h +++ b/servers/audio/effects/audio_effect_capture.h @@ -34,6 +34,7 @@ #include "core/config/engine.h" #include "core/math/audio_frame.h" #include "core/object/ref_counted.h" +#include "core/templates/ring_buffer.h" #include "core/templates/vector.h" #include "servers/audio/audio_effect.h" #include "servers/audio_server.h" diff --git a/servers/audio/effects/audio_effect_pitch_shift.cpp b/servers/audio/effects/audio_effect_pitch_shift.cpp index bfbaeee3f3..d6c396e0a5 100644 --- a/servers/audio/effects/audio_effect_pitch_shift.cpp +++ b/servers/audio/effects/audio_effect_pitch_shift.cpp @@ -40,7 +40,7 @@ * * NAME: smbPitchShift.cpp * VERSION: 1.2 -* HOME URL: http://blogs.zynaptiq.com/bernsee +* HOME URL: https://blogs.zynaptiq.com/bernsee * KNOWN BUGS: none * * SYNOPSIS: Routine for doing pitch shifting while maintaining diff --git a/servers/audio/effects/audio_stream_generator.cpp b/servers/audio/effects/audio_stream_generator.cpp index bced2997ce..447acf53a4 100644 --- a/servers/audio/effects/audio_stream_generator.cpp +++ b/servers/audio/effects/audio_stream_generator.cpp @@ -64,6 +64,10 @@ float AudioStreamGenerator::get_length() const { return 0; } +bool AudioStreamGenerator::is_monophonic() const { + return true; +} + void AudioStreamGenerator::_bind_methods() { ClassDB::bind_method(D_METHOD("set_mix_rate", "hz"), &AudioStreamGenerator::set_mix_rate); ClassDB::bind_method(D_METHOD("get_mix_rate"), &AudioStreamGenerator::get_mix_rate); @@ -138,7 +142,7 @@ void AudioStreamGeneratorPlayback::clear_buffer() { mixed = 0; } -void AudioStreamGeneratorPlayback::_mix_internal(AudioFrame *p_buffer, int p_frames) { +int AudioStreamGeneratorPlayback::_mix_internal(AudioFrame *p_buffer, int p_frames) { int read_amount = buffer.data_left(); if (p_frames < read_amount) { read_amount = p_frames; @@ -156,6 +160,7 @@ void AudioStreamGeneratorPlayback::_mix_internal(AudioFrame *p_buffer, int p_fra } mixed += p_frames / generator->get_mix_rate(); + return read_amount < p_frames ? read_amount : p_frames; } float AudioStreamGeneratorPlayback::get_stream_sampling_rate() { diff --git a/servers/audio/effects/audio_stream_generator.h b/servers/audio/effects/audio_stream_generator.h index 5d46771f4d..918589f6d0 100644 --- a/servers/audio/effects/audio_stream_generator.h +++ b/servers/audio/effects/audio_stream_generator.h @@ -54,6 +54,7 @@ public: virtual String get_stream_name() const override; virtual float get_length() const override; + virtual bool is_monophonic() const override; AudioStreamGenerator(); }; @@ -67,7 +68,7 @@ class AudioStreamGeneratorPlayback : public AudioStreamPlaybackResampled { AudioStreamGenerator *generator; protected: - virtual void _mix_internal(AudioFrame *p_buffer, int p_frames) override; + virtual int _mix_internal(AudioFrame *p_buffer, int p_frames) override; virtual float get_stream_sampling_rate() override; static void _bind_methods(); |