Browse Source

Merge pull request #52036 from reduz/native-extension-argument-pointers

Implement NativeExtension pointer arguments
Juan Linietsky 4 years ago
parent
commit
aa3c3a9ebb

+ 1 - 0
core/core_constants.cpp

@@ -558,6 +558,7 @@ void register_global_constants() {
 	BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_NODE_PATH_VALID_TYPES);
 	BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_SAVE_FILE);
 	BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_INT_IS_OBJECTID);
+	BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_INT_IS_POINTER);
 	BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_ARRAY_TYPE);
 	BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_MAX);
 

+ 16 - 2
core/doc_data.cpp

@@ -31,7 +31,14 @@
 #include "doc_data.h"
 
 void DocData::return_doc_from_retinfo(DocData::MethodDoc &p_method, const PropertyInfo &p_retinfo) {
-	if (p_retinfo.type == Variant::INT && p_retinfo.usage & PROPERTY_USAGE_CLASS_IS_ENUM) {
+	if (p_retinfo.type == Variant::INT && p_retinfo.hint == PROPERTY_HINT_INT_IS_POINTER) {
+		p_method.return_type = p_retinfo.hint_string;
+		if (p_method.return_type == "") {
+			p_method.return_type = "void*";
+		} else {
+			p_method.return_type += "*";
+		}
+	} else if (p_retinfo.type == Variant::INT && p_retinfo.usage & PROPERTY_USAGE_CLASS_IS_ENUM) {
 		p_method.return_enum = p_retinfo.class_name;
 		if (p_method.return_enum.begins_with("_")) { //proxy class
 			p_method.return_enum = p_method.return_enum.substr(1, p_method.return_enum.length());
@@ -55,7 +62,14 @@ void DocData::return_doc_from_retinfo(DocData::MethodDoc &p_method, const Proper
 void DocData::argument_doc_from_arginfo(DocData::ArgumentDoc &p_argument, const PropertyInfo &p_arginfo) {
 	p_argument.name = p_arginfo.name;
 
-	if (p_arginfo.type == Variant::INT && p_arginfo.usage & PROPERTY_USAGE_CLASS_IS_ENUM) {
+	if (p_arginfo.type == Variant::INT && p_arginfo.hint == PROPERTY_HINT_INT_IS_POINTER) {
+		p_argument.type = p_arginfo.hint_string;
+		if (p_argument.type == "") {
+			p_argument.type = "void*";
+		} else {
+			p_argument.type += "*";
+		}
+	} else if (p_arginfo.type == Variant::INT && p_arginfo.usage & PROPERTY_USAGE_CLASS_IS_ENUM) {
 		p_argument.enumeration = p_arginfo.class_name;
 		if (p_argument.enumeration.begins_with("_")) { //proxy class
 			p_argument.enumeration = p_argument.enumeration.substr(1, p_argument.enumeration.length());

+ 21 - 0
core/extension/extension_api_dump.cpp

@@ -39,6 +39,13 @@
 #ifdef TOOLS_ENABLED
 
 static String get_type_name(const PropertyInfo &p_info) {
+	if (p_info.type == Variant::INT && (p_info.hint == PROPERTY_HINT_INT_IS_POINTER)) {
+		if (p_info.hint_string == "") {
+			return "void*";
+		} else {
+			return p_info.hint_string + "*";
+		}
+	}
 	if (p_info.type == Variant::INT && (p_info.usage & PROPERTY_USAGE_CLASS_IS_ENUM)) {
 		return String("enum::") + String(p_info.class_name);
 	}
@@ -831,6 +838,20 @@ Dictionary NativeExtensionAPIDump::generate_extension_api() {
 		}
 	}
 
+	{
+		Array native_structures;
+
+		{
+			Dictionary d;
+			d["name"] = "AudioFrame";
+			d["format"] = "float left,float right";
+
+			native_structures.push_back(d);
+		}
+
+		api_dump["native_structures"] = native_structures;
+	}
+
 	return api_dump;
 }
 

+ 2 - 3
core/object/make_virtuals.py

@@ -2,7 +2,7 @@ proto = """
 #define GDVIRTUAL$VER($RET m_name $ARG) \\
 StringName _gdvirtual_##m_name##_sn = #m_name;\\
 GDNativeExtensionClassCallVirtual _gdvirtual_##m_name = (_get_extension() && _get_extension()->get_virtual) ? _get_extension()->get_virtual(_get_extension()->class_userdata, #m_name) : (GDNativeExtensionClassCallVirtual) nullptr;\\
-bool _gdvirtual_##m_name##_call($CALLARGS) $CONST { \\
+_FORCE_INLINE_ bool _gdvirtual_##m_name##_call($CALLARGS) $CONST { \\
 	ScriptInstance *script_instance = ((Object*)(this))->get_script_instance();\\
 	if (script_instance) {\\
 		Callable::CallError ce; \\
@@ -23,7 +23,7 @@ bool _gdvirtual_##m_name##_call($CALLARGS) $CONST { \\
 \\
     return false;\\
 }\\
-bool _gdvirtual_##m_name##_overriden() const { \\
+_FORCE_INLINE_ bool _gdvirtual_##m_name##_overriden() const { \\
 	ScriptInstance *script_instance = ((Object*)(this))->get_script_instance();\\
 	if (script_instance) {\\
 	    return script_instance->has_method(_gdvirtual_##m_name##_sn);\\
@@ -42,7 +42,6 @@ _FORCE_INLINE_ static MethodInfo _gdvirtual_##m_name##_get_method_info() { \\
     return method_info;\\
 }
 
-
 """
 
 

+ 1 - 0
core/object/object.h

@@ -97,6 +97,7 @@ enum PropertyHint {
 	PROPERTY_HINT_SAVE_FILE, ///< a file path must be passed, hint_text (optionally) is a filter "*.png,*.wav,*.doc,". This opens a save dialog
 	PROPERTY_HINT_INT_IS_OBJECTID,
 	PROPERTY_HINT_ARRAY_TYPE,
+	PROPERTY_HINT_INT_IS_POINTER,
 	PROPERTY_HINT_MAX,
 	// When updating PropertyHint, also sync the hardcoded list in VisualScriptEditorVariableEdit
 };

+ 7 - 0
core/variant/method_ptrcall.h

@@ -191,6 +191,7 @@ struct PtrToArg<ObjectID> {
 
 // This is for the special cases used by Variant.
 
+// No EncodeT because direct pointer conversion not possible.
 #define MAKE_VECARG(m_type)                                                              \
 	template <>                                                                          \
 	struct PtrToArg<Vector<m_type>> {                                                    \
@@ -236,6 +237,7 @@ struct PtrToArg<ObjectID> {
 		}                                                                                \
 	}
 
+// No EncodeT because direct pointer conversion not possible.
 #define MAKE_VECARG_ALT(m_type, m_type_alt)                                              \
 	template <>                                                                          \
 	struct PtrToArg<Vector<m_type_alt>> {                                                \
@@ -285,6 +287,7 @@ MAKE_VECARG_ALT(String, StringName);
 
 // For stuff that gets converted to Array vectors.
 
+// No EncodeT because direct pointer conversion not possible.
 #define MAKE_VECARR(m_type)                                                    \
 	template <>                                                                \
 	struct PtrToArg<Vector<m_type>> {                                          \
@@ -325,6 +328,7 @@ MAKE_VECARR(Variant);
 MAKE_VECARR(RID);
 MAKE_VECARR(Plane);
 
+// No EncodeT because direct pointer conversion not possible.
 #define MAKE_DVECARR(m_type)                                                   \
 	template <>                                                                \
 	struct PtrToArg<Vector<m_type>> {                                          \
@@ -372,6 +376,7 @@ MAKE_VECARR(Plane);
 
 // Special case for IPAddress.
 
+// No EncodeT because direct pointer conversion not possible.
 #define MAKE_STRINGCONV_BY_REFERENCE(m_type)                                  \
 	template <>                                                               \
 	struct PtrToArg<m_type> {                                                 \
@@ -395,6 +400,7 @@ MAKE_VECARR(Plane);
 
 MAKE_STRINGCONV_BY_REFERENCE(IPAddress);
 
+// No EncodeT because direct pointer conversion not possible.
 template <>
 struct PtrToArg<Vector<Face3>> {
 	_FORCE_INLINE_ static Vector<Face3> convert(const void *p_ptr) {
@@ -429,6 +435,7 @@ struct PtrToArg<Vector<Face3>> {
 	}
 };
 
+// No EncodeT because direct pointer conversion not possible.
 template <>
 struct PtrToArg<const Vector<Face3> &> {
 	_FORCE_INLINE_ static Vector<Face3> convert(const void *p_ptr) {

+ 130 - 0
core/variant/native_ptr.h

@@ -0,0 +1,130 @@
+/*************************************************************************/
+/*  native_ptr.h                                                         */
+/*************************************************************************/
+/*                       This file is part of:                           */
+/*                           GODOT ENGINE                                */
+/*                      https://godotengine.org                          */
+/*************************************************************************/
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur.                 */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md).   */
+/*                                                                       */
+/* 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 NATIVE_PTR_H
+#define NATIVE_PTR_H
+
+#include "core/math/audio_frame.h"
+#include "core/variant/method_ptrcall.h"
+#include "core/variant/type_info.h"
+
+template <class T>
+struct GDNativeConstPtr {
+	const T *data = nullptr;
+	GDNativeConstPtr(const T *p_assign) { data = p_assign; }
+	static const char *get_name() { return "const void"; }
+	operator const T *() const { return data; }
+	operator Variant() const { return uint64_t(data); }
+};
+
+template <class T>
+struct GDNativePtr {
+	T *data = nullptr;
+	GDNativePtr(T *p_assign) { data = p_assign; }
+	static const char *get_name() { return "void"; }
+	operator T *() const { return data; }
+	operator Variant() const { return uint64_t(data); }
+};
+
+#define GDVIRTUAL_NATIVE_PTR(m_type)                                  \
+	template <>                                                       \
+	struct GDNativeConstPtr<m_type> {                                 \
+		const m_type *data = nullptr;                                 \
+		GDNativeConstPtr(const m_type *p_assign) { data = p_assign; } \
+		static const char *get_name() { return "const " #m_type; }    \
+		operator const m_type *() const { return data; }              \
+		operator Variant() const { return uint64_t(data); }           \
+	};                                                                \
+	template <>                                                       \
+	struct GDNativePtr<m_type> {                                      \
+		m_type *data = nullptr;                                       \
+		GDNativePtr(m_type *p_assign) { data = p_assign; }            \
+		static const char *get_name() { return #m_type; }             \
+		operator m_type *() const { return data; }                    \
+		operator Variant() const { return uint64_t(data); }           \
+	};
+
+template <class T>
+struct GetTypeInfo<GDNativeConstPtr<T>> {
+	static const Variant::Type VARIANT_TYPE = Variant::NIL;
+	static const GodotTypeInfo::Metadata METADATA = GodotTypeInfo::METADATA_NONE;
+	static inline PropertyInfo get_class_info() {
+		return PropertyInfo(Variant::INT, String(), PROPERTY_HINT_INT_IS_POINTER, GDNativeConstPtr<T>::get_name());
+	}
+};
+
+template <class T>
+struct GetTypeInfo<GDNativePtr<T>> {
+	static const Variant::Type VARIANT_TYPE = Variant::NIL;
+	static const GodotTypeInfo::Metadata METADATA = GodotTypeInfo::METADATA_NONE;
+	static inline PropertyInfo get_class_info() {
+		return PropertyInfo(Variant::INT, String(), PROPERTY_HINT_INT_IS_POINTER, GDNativePtr<T>::get_name());
+	}
+};
+
+template <class T>
+struct PtrToArg<GDNativeConstPtr<T>> {
+	_FORCE_INLINE_ static GDNativeConstPtr<T> convert(const void *p_ptr) {
+		return GDNativeConstPtr<T>(reinterpret_cast<const T *>(p_ptr));
+	}
+	typedef const T *EncodeT;
+	_FORCE_INLINE_ static void encode(GDNativeConstPtr<T> p_val, void *p_ptr) {
+		*((const T **)p_ptr) = p_val.data;
+	}
+};
+template <class T>
+struct PtrToArg<GDNativePtr<T>> {
+	_FORCE_INLINE_ static GDNativePtr<T> convert(const void *p_ptr) {
+		return GDNativePtr<T>(reinterpret_cast<const T *>(p_ptr));
+	}
+	typedef T *EncodeT;
+	_FORCE_INLINE_ static void encode(GDNativePtr<T> p_val, void *p_ptr) {
+		*((T **)p_ptr) = p_val.data;
+	}
+};
+
+GDVIRTUAL_NATIVE_PTR(AudioFrame)
+GDVIRTUAL_NATIVE_PTR(bool)
+GDVIRTUAL_NATIVE_PTR(char)
+GDVIRTUAL_NATIVE_PTR(char16_t)
+GDVIRTUAL_NATIVE_PTR(char32_t)
+GDVIRTUAL_NATIVE_PTR(wchar_t)
+GDVIRTUAL_NATIVE_PTR(uint8_t)
+GDVIRTUAL_NATIVE_PTR(int8_t)
+GDVIRTUAL_NATIVE_PTR(uint16_t)
+GDVIRTUAL_NATIVE_PTR(int16_t)
+GDVIRTUAL_NATIVE_PTR(uint32_t)
+GDVIRTUAL_NATIVE_PTR(int32_t)
+GDVIRTUAL_NATIVE_PTR(int64_t)
+GDVIRTUAL_NATIVE_PTR(uint64_t)
+GDVIRTUAL_NATIVE_PTR(float)
+GDVIRTUAL_NATIVE_PTR(double)
+
+#endif // NATIVE_PTR_H

+ 3 - 1
doc/classes/@GlobalScope.xml

@@ -2351,9 +2351,11 @@
 		</constant>
 		<constant name="PROPERTY_HINT_INT_IS_OBJECTID" value="38" enum="PropertyHint">
 		</constant>
+		<constant name="PROPERTY_HINT_INT_IS_POINTER" value="40" enum="PropertyHint">
+		</constant>
 		<constant name="PROPERTY_HINT_ARRAY_TYPE" value="39" enum="PropertyHint">
 		</constant>
-		<constant name="PROPERTY_HINT_MAX" value="40" enum="PropertyHint">
+		<constant name="PROPERTY_HINT_MAX" value="41" enum="PropertyHint">
 		</constant>
 		<constant name="PROPERTY_USAGE_NONE" value="0" enum="PropertyUsageFlags">
 		</constant>

+ 15 - 0
doc/classes/AudioStream.xml

@@ -13,6 +13,21 @@
 		<link title="Audio Spectrum Demo">https://godotengine.org/asset-library/asset/528</link>
 	</tutorials>
 	<methods>
+		<method name="_get_length" qualifiers="virtual const">
+			<return type="float" />
+			<description>
+			</description>
+		</method>
+		<method name="_get_stream_name" qualifiers="virtual const">
+			<return type="String" />
+			<description>
+			</description>
+		</method>
+		<method name="_instance_playback" qualifiers="virtual const">
+			<return type="AudioStreamPlayback" />
+			<description>
+			</description>
+		</method>
 		<method name="get_length" qualifiers="const">
 			<return type="float" />
 			<description>

+ 40 - 0
doc/classes/AudioStreamPlayback.xml

@@ -10,6 +10,46 @@
 		<link title="Audio Generator Demo">https://godotengine.org/asset-library/asset/526</link>
 	</tutorials>
 	<methods>
+		<method name="_get_loop_count" qualifiers="virtual const">
+			<return type="int" />
+			<description>
+			</description>
+		</method>
+		<method name="_get_playback_position" qualifiers="virtual const">
+			<return type="float" />
+			<description>
+			</description>
+		</method>
+		<method name="_is_playing" qualifiers="virtual const">
+			<return type="bool" />
+			<description>
+			</description>
+		</method>
+		<method name="_mix" qualifiers="virtual">
+			<return type="void" />
+			<argument index="0" name="buffer" type="AudioFrame*" />
+			<argument index="1" name="rate_scale" type="float" />
+			<argument index="2" name="frames" type="int" />
+			<description>
+			</description>
+		</method>
+		<method name="_seek" qualifiers="virtual">
+			<return type="void" />
+			<argument index="0" name="position" type="float" />
+			<description>
+			</description>
+		</method>
+		<method name="_start" qualifiers="virtual">
+			<return type="void" />
+			<argument index="0" name="from_pos" type="float" />
+			<description>
+			</description>
+		</method>
+		<method name="_stop" qualifiers="virtual">
+			<return type="void" />
+			<description>
+			</description>
+		</method>
 	</methods>
 	<constants>
 	</constants>

+ 2 - 0
doc/tools/makerst.py

@@ -1007,6 +1007,8 @@ def format_table(f, data, remove_empty_columns=False):  # type: (TextIO, Iterabl
 
 
 def make_type(klass, state):  # type: (str, State) -> str
+    if klass.find("*") != -1:  # Pointer, ignore
+        return klass
     link_type = klass
     if link_type.endswith("[]"):  # Typed array, strip [] to link to contained type.
         link_type = link_type[:-2]

+ 3 - 3
editor/editor_help.cpp

@@ -170,7 +170,7 @@ void EditorHelp::_add_type(const String &p_type, const String &p_enum) {
 	if (t.is_empty()) {
 		t = "void";
 	}
-	bool can_ref = (t != "void") || !p_enum.is_empty();
+	bool can_ref = (t != "void" && t.find("*") == -1) || !p_enum.is_empty();
 
 	if (!p_enum.is_empty()) {
 		if (p_enum.get_slice_count(".") > 1) {
@@ -632,8 +632,8 @@ void EditorHelp::_update_doc() {
 				continue;
 			}
 		}
-		// Ignore undocumented private.
-		if (cd.methods[i].name.begins_with("_") && cd.methods[i].description.is_empty()) {
+		// Ignore undocumented non virtual private.
+		if (cd.methods[i].name.begins_with("_") && cd.methods[i].description.is_empty() && cd.methods[i].qualifiers.find("virtual") == -1) {
 			continue;
 		}
 		methods.push_back(cd.methods[i]);

+ 5 - 1
scene/2d/audio_stream_player_2d.cpp

@@ -272,8 +272,12 @@ void AudioStreamPlayer2D::set_stream(Ref<AudioStream> p_stream) {
 	}
 
 	if (p_stream.is_valid()) {
-		stream = p_stream;
 		stream_playback = p_stream->instance_playback();
+		if (stream_playback.is_valid()) {
+			stream = p_stream;
+		} else {
+			stream.unref();
+		}
 	}
 
 	AudioServer::get_singleton()->unlock();

+ 5 - 1
scene/3d/audio_stream_player_3d.cpp

@@ -624,8 +624,12 @@ void AudioStreamPlayer3D::set_stream(Ref<AudioStream> p_stream) {
 	}
 
 	if (p_stream.is_valid()) {
-		stream = p_stream;
 		stream_playback = p_stream->instance_playback();
+		if (stream_playback.is_valid()) {
+			stream = p_stream;
+		} else {
+			stream.unref();
+		}
 	}
 
 	AudioServer::get_singleton()->unlock();

+ 5 - 1
scene/audio/audio_stream_player.cpp

@@ -202,8 +202,12 @@ void AudioStreamPlayer::set_stream(Ref<AudioStream> p_stream) {
 	}
 
 	if (p_stream.is_valid()) {
-		stream = p_stream;
 		stream_playback = p_stream->instance_playback();
+		if (stream_playback.is_valid()) {
+			stream = p_stream;
+		} else {
+			stream.unref();
+		}
 	}
 
 	AudioServer::get_singleton()->unlock();

+ 84 - 0
servers/audio/audio_stream.cpp

@@ -33,6 +33,63 @@
 #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;
+	}
+}
+
+void AudioStreamPlayback::mix(AudioFrame *p_buffer, float p_rate_scale, int p_frames) {
+	if (GDVIRTUAL_CALL(_mix, p_buffer, p_rate_scale, p_frames)) {
+		return;
+	}
+	WARN_PRINT_ONCE("AudioStreamPlayback::mix unimplemented!");
+}
+
+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() {
@@ -92,8 +149,34 @@ void AudioStreamPlaybackResampled::mix(AudioFrame *p_buffer, float p_rate_scale,
 
 ////////////////////////////////
 
+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;
+}
+
 void AudioStream::_bind_methods() {
 	ClassDB::bind_method(D_METHOD("get_length"), &AudioStream::get_length);
+	GDVIRTUAL_BIND(_instance_playback);
+	GDVIRTUAL_BIND(_get_stream_name);
+	GDVIRTUAL_BIND(_get_length);
 }
 
 ////////////////////////////////
@@ -358,3 +441,4 @@ void AudioStreamPlaybackRandomPitch::mix(AudioFrame *p_buffer, float p_rate_scal
 AudioStreamPlaybackRandomPitch::~AudioStreamPlaybackRandomPitch() {
 	random_pitch->playbacks.erase(this);
 }
+/////////////////////////////////////////////

+ 27 - 10
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)
+	GDVIRTUAL3(_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 void mix(AudioFrame *p_buffer, float p_rate_scale, int p_frames);
 };
 
 class AudioStreamPlaybackResampled : public AudioStreamPlayback {
@@ -84,11 +97,15 @@ class AudioStream : public Resource {
 protected:
 	static void _bind_methods();
 
+	GDVIRTUAL0RC(Ref<AudioStreamPlayback>, _instance_playback)
+	GDVIRTUAL0RC(String, _get_stream_name)
+	GDVIRTUAL0RC(float, _get_length)
+
 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;
 };
 
 // Microphone

+ 2 - 2
servers/register_server_types.cpp

@@ -140,8 +140,8 @@ void register_server_types() {
 	GDREGISTER_VIRTUAL_CLASS(XRInterface);
 	GDREGISTER_CLASS(XRPositionalTracker);
 
-	GDREGISTER_VIRTUAL_CLASS(AudioStream);
-	GDREGISTER_VIRTUAL_CLASS(AudioStreamPlayback);
+	GDREGISTER_CLASS(AudioStream);
+	GDREGISTER_CLASS(AudioStreamPlayback);
 	GDREGISTER_VIRTUAL_CLASS(AudioStreamPlaybackResampled);
 	GDREGISTER_CLASS(AudioStreamMicrophone);
 	GDREGISTER_CLASS(AudioStreamRandomPitch);