├── .gitmodules ├── demo ├── icon.png ├── volume.png ├── TTSDriver.gdns ├── default_env.tres ├── project.godot ├── libgdtts │ └── libgdtts.gdnlib └── UI.tscn ├── .gitignore ├── .toolchains ├── linux.x86.toolchain ├── linux.x86_64.toolchain ├── linux.armv7.toolchain ├── mingw.x86.toolchain ├── linux.arm64.toolchain ├── macos.arm64.toolchain ├── macos.x86_64.toolchain └── mingw.x86_64.toolchain ├── src ├── array.h ├── dictionary.h ├── variant.h ├── dictionary.cpp ├── tts_sapi │ ├── tts_sapi.h │ └── tts_sapi.cpp ├── array.cpp ├── tts_libspeechd │ ├── tts_libspeechd.h │ └── tts_libspeechd.cpp ├── tts_driver.h ├── ustring.h ├── gdnative.h ├── tts_nsspeech │ ├── tts_nsspeech.h │ └── tts_nsspeech.mm ├── gdnative.cpp ├── ustring.cpp ├── variant.cpp └── tts_driver.cpp ├── UNLICENSE ├── meson.build └── README.md /.gitmodules: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /demo/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bruvzg/godot_tts/HEAD/demo/icon.png -------------------------------------------------------------------------------- /demo/volume.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bruvzg/godot_tts/HEAD/demo/volume.png -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .import/* 2 | _build/* 3 | *.import 4 | *.so 5 | *.dylib 6 | *.dll 7 | *.a 8 | _*/** 9 | export_presets.cfg 10 | -------------------------------------------------------------------------------- /demo/TTSDriver.gdns: -------------------------------------------------------------------------------- 1 | [gd_resource type="NativeScript" load_steps=2 format=2] 2 | 3 | [ext_resource path="res://libgdtts/libgdtts.gdnlib" type="GDNativeLibrary" id=1] 4 | 5 | [resource] 6 | 7 | resource_name = "TTSDriver" 8 | class_name = "TTSDriver" 9 | library = ExtResource( 1 ) 10 | -------------------------------------------------------------------------------- /.toolchains/linux.x86.toolchain: -------------------------------------------------------------------------------- 1 | [binaries] 2 | c = 'i686-linux-gnu-gcc' 3 | cpp = 'i686-linux-gnu-g++' 4 | ar = 'i686-linux-gnu-ar' 5 | strip = 'i686-linux-gnu-strip' 6 | pkgconfig = 'i686-linux-gnu-pkg-config' 7 | 8 | [host_machine] 9 | system = 'linux' 10 | cpu_family = 'x86' 11 | cpu = 'i686' 12 | endian = 'little' 13 | 14 | [built-in options] 15 | c_args = ['-march=i686', '-fPIC'] 16 | cpp_args = ['-march=i686', '-fPIC'] 17 | c_link_args = ['-march=i686'] 18 | cpp_link_args = ['-march=i686'] 19 | -------------------------------------------------------------------------------- /.toolchains/linux.x86_64.toolchain: -------------------------------------------------------------------------------- 1 | [binaries] 2 | c = 'x86_64-linux-gnu-gcc' 3 | cpp = 'x86_64-linux-gnu-g++' 4 | ar = 'x86_64-linux-gnu-ar' 5 | strip = 'x86_64-linux-gnu-strip' 6 | pkgconfig = 'x86_64-linux-gnu-pkg-config' 7 | 8 | [host_machine] 9 | system = 'linux' 10 | cpu_family = 'x86_64' 11 | cpu = 'x86_64' 12 | endian = 'little' 13 | 14 | [built-in options] 15 | c_args = ['-march=x86-64', '-fPIC'] 16 | cpp_args = ['-march=x86-64', '-fPIC'] 17 | c_link_args = ['-march=x86-64'] 18 | cpp_link_args = ['-march=x86-64'] 19 | -------------------------------------------------------------------------------- /.toolchains/linux.armv7.toolchain: -------------------------------------------------------------------------------- 1 | [binaries] 2 | c = 'arm-linux-gnueabihf-gcc' 3 | cpp = 'arm-linux-gnueabihf-g++' 4 | ar = 'arm-linux-gnueabihf-ar' 5 | strip = 'arm-linux-gnueabihf-strip' 6 | pkgconfig = 'arm-linux-gnueabihf-pkg-config' 7 | 8 | [host_machine] 9 | system = 'linux' 10 | cpu_family = 'arm' 11 | cpu = 'armv7' 12 | endian = 'little' 13 | 14 | [built-in options] 15 | c_args = ['-march=armv7', '-fPIC'] 16 | cpp_args = ['-march=armv7', '-fPIC'] 17 | c_link_args = ['-march=armv7'] 18 | cpp_link_args = ['-march=armv7'] 19 | -------------------------------------------------------------------------------- /.toolchains/mingw.x86.toolchain: -------------------------------------------------------------------------------- 1 | [binaries] 2 | c = '/opt/homebrew/bin/i686-w64-mingw32-gcc' 3 | cpp = '/opt/homebrew/bin/i686-w64-mingw32-g++' 4 | ar = '/opt/homebrew/bin/i686-w64-mingw32-ar' 5 | strip = '/opt/homebrew/bin/i686-w64-mingw32-strip' 6 | pkgconfig = '/opt/homebrew/bin/i686-w64-mingw32-pkg-config' 7 | 8 | [host_machine] 9 | system = 'windows' 10 | cpu_family = 'x86' 11 | cpu = 'i686' 12 | endian = 'little' 13 | 14 | [built-in options] 15 | c_args = ['-D_WIN32_WINNT=0x0601'] 16 | cpp_args = ['-D_WIN32_WINNT=0x0601'] 17 | -------------------------------------------------------------------------------- /.toolchains/linux.arm64.toolchain: -------------------------------------------------------------------------------- 1 | [binaries] 2 | c = 'aarch64-linux-gnu-gcc' 3 | cpp = 'aarch64-linux-gnu-g++' 4 | ar = 'aarch64-linux-gnu-ar' 5 | strip = 'aarch64-linux-gnu-strip' 6 | pkgconfig = 'aarch64-linux-gnu-pkg-config' 7 | 8 | [host_machine] 9 | system = 'linux' 10 | cpu_family = 'aarch64' 11 | cpu = 'aarch64' 12 | endian = 'little' 13 | 14 | [built-in options] 15 | c_args = ['-march=armv8-a', '-fPIC'] 16 | cpp_args = ['-march=armv8-a', '-fPIC'] 17 | c_link_args = ['-march=armv8-a'] 18 | cpp_link_args = ['-march=armv8-a'] 19 | -------------------------------------------------------------------------------- /.toolchains/macos.arm64.toolchain: -------------------------------------------------------------------------------- 1 | [binaries] 2 | c = 'clang' 3 | cpp = 'clang++' 4 | objcpp = 'clang++' 5 | ar = 'ar' 6 | strip = 'strip' 7 | pkgconfig = 'pkg-config' 8 | 9 | [host_machine] 10 | system = 'darwin' 11 | cpu_family = 'aarch64' 12 | cpu = 'aarch64' 13 | endian = 'little' 14 | 15 | [built-in options] 16 | c_args = ['-arch', 'arm64'] 17 | cpp_args = ['-arch', 'arm64'] 18 | objcpp_args = ['-arch', 'arm64'] 19 | c_link_args = ['-arch', 'arm64'] 20 | cpp_link_args = ['-arch', 'arm64'] 21 | objcpp_link_args = ['-arch', 'arm64'] 22 | -------------------------------------------------------------------------------- /.toolchains/macos.x86_64.toolchain: -------------------------------------------------------------------------------- 1 | [binaries] 2 | c = 'clang' 3 | cpp = 'clang++' 4 | objcpp = 'clang++' 5 | ar = 'ar' 6 | strip = 'strip' 7 | pkgconfig = 'pkg-config' 8 | 9 | [host_machine] 10 | system = 'darwin' 11 | cpu_family = 'x86_64' 12 | cpu = 'x86_64' 13 | endian = 'little' 14 | 15 | [built-in options] 16 | c_args = ['-arch', 'x86_64'] 17 | cpp_args = ['-arch', 'x86_64'] 18 | objcpp_args = ['-arch', 'x86_64'] 19 | c_link_args = ['-arch', 'x86_64'] 20 | cpp_link_args = ['-arch', 'x86_64'] 21 | objcpp_link_args = ['-arch', 'x86_64'] 22 | -------------------------------------------------------------------------------- /.toolchains/mingw.x86_64.toolchain: -------------------------------------------------------------------------------- 1 | [binaries] 2 | c = '/opt/homebrew/bin/x86_64-w64-mingw32-gcc' 3 | cpp = '/opt/homebrew/bin/x86_64-w64-mingw32-g++' 4 | ar = '/opt/homebrew/bin/x86_64-w64-mingw32-ar' 5 | strip = '/opt/homebrew/bin/x86_64-w64-mingw32-strip' 6 | pkgconfig = '/opt/homebrew/bin/x86_64-w64-mingw32-pkg-config' 7 | 8 | [host_machine] 9 | system = 'windows' 10 | cpu_family = 'x86_64' 11 | cpu = 'amd64' 12 | endian = 'little' 13 | 14 | [built-in options] 15 | c_args = ['-D_WIN32_WINNT=0x0601'] 16 | cpp_args = ['-D_WIN32_WINNT=0x0601'] -------------------------------------------------------------------------------- /demo/default_env.tres: -------------------------------------------------------------------------------- 1 | [gd_resource type="Environment" load_steps=2 format=2] 2 | 3 | [sub_resource type="ProceduralSky" id=1] 4 | radiance_size = 4 5 | sky_top_color = Color( 0.647059, 0.839216, 0.945098, 1 ) 6 | sky_horizon_color = Color( 0.839216, 0.917647, 0.980392, 1 ) 7 | ground_bottom_color = Color( 0.156863, 0.184314, 0.211765, 1 ) 8 | ground_horizon_color = Color( 0.423529, 0.396078, 0.372549, 1 ) 9 | sun_energy = 16.0 10 | 11 | [resource] 12 | background_mode = 2 13 | background_sky = SubResource( 1 ) 14 | fog_height_min = 0.0 15 | fog_height_max = 100.0 16 | ssao_quality = 0 17 | -------------------------------------------------------------------------------- /demo/project.godot: -------------------------------------------------------------------------------- 1 | ; Engine configuration file. 2 | ; It's best edited using the editor UI and not directly, 3 | ; since the parameters that go here are not all obvious. 4 | ; 5 | ; Format: 6 | ; [section] ; section goes between [] 7 | ; param=value ; assign values to parameters 8 | 9 | config_version=4 10 | 11 | _global_script_classes=[ ] 12 | _global_script_class_icons={ 13 | } 14 | 15 | [application] 16 | 17 | config/name="libgdtts test" 18 | run/main_scene="res://UI.tscn" 19 | config/icon="res://icon.png" 20 | 21 | [display] 22 | 23 | window/size/width=800 24 | 25 | [rendering] 26 | 27 | environment/default_environment="res://default_env.tres" 28 | -------------------------------------------------------------------------------- /src/array.h: -------------------------------------------------------------------------------- 1 | /*************************************************************************/ 2 | /* array.h */ 3 | /*************************************************************************/ 4 | 5 | #pragma once 6 | 7 | #include 8 | #include 9 | 10 | class Variant; 11 | 12 | class Array { 13 | godot_array gdn; 14 | 15 | public: 16 | Array(); 17 | Array(const Array &p_array); 18 | 19 | Array &operator=(const Array &p_array); 20 | 21 | bool empty() const; 22 | void erase(const Variant& p_var); 23 | void push_back(const Variant& p_var); 24 | 25 | ~Array(); 26 | }; 27 | -------------------------------------------------------------------------------- /src/dictionary.h: -------------------------------------------------------------------------------- 1 | /*************************************************************************/ 2 | /* dictionary.h */ 3 | /*************************************************************************/ 4 | 5 | #pragma once 6 | 7 | #include 8 | #include 9 | 10 | class Variant; 11 | 12 | class Dictionary { 13 | godot_dictionary gdn; 14 | 15 | public: 16 | Dictionary(); 17 | Dictionary(const Dictionary & p_dict); 18 | 19 | Dictionary &operator=(const Dictionary &p_dict); 20 | 21 | void set(const Variant& p_key, const Variant& p_value); 22 | 23 | ~Dictionary(); 24 | }; 25 | -------------------------------------------------------------------------------- /demo/libgdtts/libgdtts.gdnlib: -------------------------------------------------------------------------------- 1 | [general] 2 | 3 | singleton=false 4 | load_once=true 5 | symbol_prefix="godot_" 6 | 7 | [entry] 8 | 9 | Windows.32="res://libgdtts/libgdtts.windows.x86.dll" 10 | Windows.64="res://libgdtts/libgdtts.windows.x86_64.dll" 11 | 12 | OSX="res://libgdtts/libgdtts.darwin.universal.dylib" 13 | 14 | X11.x86="res://libgdtts/libgdtts.linux.x86.so" 15 | X11.x86_64="res://libgdtts/libgdtts.linux.x86_64.so" 16 | X11.armv7="res://libgdtts/libgdtts.linux.arm.so" 17 | X11.arm64="res://libgdtts/libgdtts.linux.aarch64.so" 18 | 19 | [dependencies] 20 | 21 | Windows.32=[] 22 | Windows.64=[] 23 | 24 | OSX=[] 25 | 26 | X11.x86=[] 27 | X11.x86_64=[] 28 | X11.armv7=[] 29 | X11.arm64=[] 30 | -------------------------------------------------------------------------------- /src/variant.h: -------------------------------------------------------------------------------- 1 | /*************************************************************************/ 2 | /* variant.h */ 3 | /*************************************************************************/ 4 | 5 | #pragma once 6 | 7 | #include 8 | #include 9 | 10 | class Array; 11 | class Dictionary; 12 | class String; 13 | 14 | class Variant { 15 | godot_variant gdn; 16 | 17 | public: 18 | Variant(); 19 | Variant(const Variant& p_var); 20 | Variant(bool p_bool); 21 | Variant(int p_int); 22 | Variant(float p_int); 23 | Variant(const String& p_string); 24 | Variant(const Dictionary& p_dict); 25 | Variant(const Array& p_array); 26 | 27 | Variant &operator=(const Variant& p_var); 28 | 29 | operator bool() const; 30 | operator int() const; 31 | operator float() const; 32 | operator String() const; 33 | operator Dictionary() const; 34 | operator Array() const; 35 | 36 | ~Variant(); 37 | }; 38 | -------------------------------------------------------------------------------- /src/dictionary.cpp: -------------------------------------------------------------------------------- 1 | /*************************************************************************/ 2 | /* dictionary.cpp */ 3 | /*************************************************************************/ 4 | 5 | #include 6 | #include 7 | 8 | Dictionary::Dictionary() { 9 | api->godot_dictionary_new(&gdn); 10 | }; 11 | 12 | Dictionary::Dictionary(const Dictionary & p_dict) { 13 | api->godot_dictionary_new_copy(&gdn, &p_dict.gdn); 14 | }; 15 | 16 | Dictionary &Dictionary::operator=(const Dictionary &p_dict) { 17 | api->godot_dictionary_destroy(&gdn); 18 | api->godot_dictionary_new_copy(&gdn, &p_dict.gdn); 19 | return *this; 20 | }; 21 | 22 | void Dictionary::set(const Variant& p_key, const Variant& p_value) { 23 | api->godot_dictionary_set(&gdn, reinterpret_cast(&p_key), reinterpret_cast(&p_value)); 24 | }; 25 | 26 | Dictionary::~Dictionary() { 27 | api->godot_dictionary_destroy(&gdn); 28 | }; 29 | -------------------------------------------------------------------------------- /src/tts_sapi/tts_sapi.h: -------------------------------------------------------------------------------- 1 | /*************************************************************************/ 2 | /* tts_sapi.h */ 3 | /*************************************************************************/ 4 | 5 | #pragma once 6 | 7 | #include "tts_driver.h" 8 | 9 | #include 10 | #include 11 | #include 12 | 13 | class TTSDriverSAPI : public TTSDriver { 14 | ISpVoice *synth; 15 | bool co_initialized; 16 | int pitch; 17 | 18 | public: 19 | TTSDriverSAPI(); 20 | 21 | bool initialize(); 22 | 23 | void speak(const String &p_text, bool p_interrupt); 24 | void stop(); 25 | bool is_speaking(); 26 | 27 | Array get_voices(); 28 | void set_voice(const String &p_voice); 29 | 30 | void set_volume(int p_volume); 31 | int get_volume(); 32 | 33 | void set_pitch(float p_pitch); 34 | float get_pitch(); 35 | 36 | void set_rate(int p_rate); 37 | int get_rate(); 38 | 39 | ~TTSDriverSAPI(); 40 | }; 41 | 42 | static TTSDriverSAPI singleton; 43 | -------------------------------------------------------------------------------- /src/array.cpp: -------------------------------------------------------------------------------- 1 | /*************************************************************************/ 2 | /* array.cpp */ 3 | /*************************************************************************/ 4 | 5 | #include 6 | #include 7 | 8 | Array::Array() { 9 | api->godot_array_new(&gdn); 10 | }; 11 | 12 | Array::Array(const Array &p_array) { 13 | api->godot_array_new_copy(&gdn, &p_array.gdn); 14 | }; 15 | 16 | Array &Array::operator=(const Array &p_array) { 17 | api->godot_array_destroy(&gdn); 18 | api->godot_array_new_copy(&gdn, &p_array.gdn); 19 | return *this; 20 | }; 21 | 22 | bool Array::empty() const { 23 | return api->godot_array_empty(&gdn); 24 | }; 25 | 26 | void Array::erase(const Variant& p_var) { 27 | api->godot_array_erase(&gdn, reinterpret_cast(&p_var)); 28 | }; 29 | 30 | void Array::push_back(const Variant& p_var) { 31 | api->godot_array_push_back(&gdn, reinterpret_cast(&p_var)); 32 | }; 33 | 34 | Array::~Array() { 35 | api->godot_array_destroy(&gdn); 36 | }; 37 | -------------------------------------------------------------------------------- /src/tts_libspeechd/tts_libspeechd.h: -------------------------------------------------------------------------------- 1 | /*************************************************************************/ 2 | /* tts_libspeechd.h */ 3 | /*************************************************************************/ 4 | 5 | #pragma once 6 | 7 | #include "tts_driver.h" 8 | 9 | #include 10 | #include 11 | #include 12 | 13 | class TTSDriverSPD : public TTSDriver { 14 | static std::list messages; 15 | SPDConnection *synth; 16 | 17 | protected: 18 | static void end_of_speech(size_t msg_id, size_t client_id, SPDNotificationType type); 19 | 20 | public: 21 | TTSDriverSPD(); 22 | 23 | bool initialize(); 24 | 25 | void speak(const String &p_text, bool p_interrupt); 26 | void stop(); 27 | bool is_speaking(); 28 | 29 | Array get_voices(); 30 | void set_voice(const String &p_voice); 31 | 32 | void set_volume(int p_volume); 33 | int get_volume(); 34 | 35 | void set_pitch(float p_pitch); 36 | float get_pitch(); 37 | 38 | void set_rate(int p_rate); 39 | int get_rate(); 40 | 41 | ~TTSDriverSPD(); 42 | }; 43 | 44 | static TTSDriverSPD singleton; 45 | -------------------------------------------------------------------------------- /src/tts_driver.h: -------------------------------------------------------------------------------- 1 | /*************************************************************************/ 2 | /* tts_driver.h */ 3 | /*************************************************************************/ 4 | 5 | #pragma once 6 | 7 | #include "array.h" 8 | #include "dictionary.h" 9 | #include "variant.h" 10 | #include "ustring.h" 11 | 12 | class TTSDriver { 13 | static TTSDriver *singleton; 14 | 15 | public: 16 | TTSDriver(); 17 | 18 | static TTSDriver *get_singleton(); 19 | void set_singleton(); 20 | 21 | static void register_methods(void *p_handle); 22 | 23 | virtual bool initialize() = 0; 24 | 25 | virtual void speak(const String &p_text, bool p_interrupt = false) = 0; 26 | virtual void stop() = 0; 27 | 28 | virtual bool is_speaking() = 0; 29 | 30 | virtual Array get_voices() = 0; 31 | virtual void set_voice(const String &p_voice) = 0; 32 | 33 | virtual void set_volume(int p_volume) = 0; 34 | virtual int get_volume() = 0; 35 | 36 | virtual void set_pitch(float p_pitch) = 0; 37 | virtual float get_pitch() = 0; 38 | 39 | virtual void set_rate(int p_rate) = 0; 40 | virtual int get_rate() = 0; 41 | 42 | virtual ~TTSDriver(); 43 | }; 44 | -------------------------------------------------------------------------------- /src/ustring.h: -------------------------------------------------------------------------------- 1 | /*************************************************************************/ 2 | /* ustring.h */ 3 | /*************************************************************************/ 4 | 5 | #pragma once 6 | 7 | #include 8 | #include 9 | #include 10 | 11 | class String; 12 | 13 | class CharString { 14 | godot_char_string gdn; 15 | 16 | public: 17 | const char *get_data() const; 18 | 19 | ~CharString(); 20 | }; 21 | 22 | class String { 23 | godot_string gdn; 24 | 25 | public: 26 | String(); 27 | String(const String &p_string); 28 | String(const char *p_string); 29 | String(const wchar_t *p_string); 30 | 31 | const wchar_t *c_str() const; 32 | CharString utf8() const; 33 | 34 | String &operator=(const String &p_string); 35 | 36 | String operator+(const String &p_string) const; 37 | bool operator==(const String &p_string) const; 38 | 39 | static String itos(int64_t p_num, godot_int p_base); 40 | 41 | ~String(); 42 | }; 43 | 44 | String operator+(const char *p_string_a, const String &p_string_b); 45 | 46 | String operator+(const wchar_t *p_string_a, const String &p_string_b); 47 | -------------------------------------------------------------------------------- /UNLICENSE: -------------------------------------------------------------------------------- 1 | This is free and unencumbered software released into the public domain. 2 | 3 | Anyone is free to copy, modify, publish, use, compile, sell, or 4 | distribute this software, either in source code form or as a compiled 5 | binary, for any purpose, commercial or non-commercial, and by any 6 | means. 7 | 8 | In jurisdictions that recognize copyright laws, the author or authors 9 | of this software dedicate any and all copyright interest in the 10 | software to the public domain. We make this dedication for the benefit 11 | of the public at large and to the detriment of our heirs and 12 | successors. We intend this dedication to be an overt act of 13 | relinquishment in perpetuity of all present and future rights to this 14 | software under copyright law. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 20 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 21 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | OTHER DEALINGS IN THE SOFTWARE. 23 | 24 | For more information, please refer to 25 | -------------------------------------------------------------------------------- /src/gdnative.h: -------------------------------------------------------------------------------- 1 | /*************************************************************************/ 2 | /* gdnative.h */ 3 | /*************************************************************************/ 4 | 5 | #pragma once 6 | 7 | #include 8 | 9 | extern "C" const godot_gdnative_core_api_struct *api; 10 | extern "C" const godot_gdnative_ext_nativescript_api_struct *nativescript_api; 11 | extern "C" const godot_gdnative_ext_nativescript_1_1_api_struct *nativescript_1_1_api; 12 | 13 | extern "C" void GDN_EXPORT godot_gdnative_init(godot_gdnative_init_options *p_options); 14 | extern "C" void GDN_EXPORT godot_gdnative_terminate(godot_gdnative_terminate_options *p_options); 15 | extern "C" void GDN_EXPORT godot_nativescript_init(void *p_handle); 16 | 17 | typedef GDCALLINGCONV godot_variant (*gdn_method)(godot_object *, void *, void *, int, godot_variant **); 18 | typedef GDCALLINGCONV void *(*gdn_create_func)(godot_object *, void *); 19 | typedef GDCALLINGCONV void (*gdn_destroy_func)(godot_object *, void *, void *); 20 | 21 | #define DEBUG_PRINT_WARNING(m_text) api->godot_print_warning(m_text, __FUNCTION__, __FILE__, __LINE__); 22 | #define DEBUG_PRINT_ERROR(m_text) api->godot_print_error(m_text, __FUNCTION__, __FILE__, __LINE__); 23 | -------------------------------------------------------------------------------- /src/tts_nsspeech/tts_nsspeech.h: -------------------------------------------------------------------------------- 1 | /*************************************************************************/ 2 | /* tts_nsspeech.h */ 3 | /*************************************************************************/ 4 | 5 | #pragma once 6 | 7 | #include "tts_driver.h" 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | @interface GodotSpeechSynthesizerDelegate : NSObject { 15 | std::list messages; 16 | }; 17 | - (void)discardMessages; 18 | - (void)appendMessage:(const String &)message; 19 | - (bool)isSpeaking; 20 | @end 21 | 22 | class TTSDriverNSSpeech : public TTSDriver { 23 | GodotSpeechSynthesizerDelegate *delegate; 24 | NSSpeechSynthesizer *synth; 25 | float pitch; 26 | 27 | public: 28 | TTSDriverNSSpeech(); 29 | 30 | bool initialize(); 31 | 32 | void speak(const String &p_text, bool p_interrupt); 33 | void stop(); 34 | bool is_speaking(); 35 | 36 | Array get_voices(); 37 | void set_voice(const String &p_voice); 38 | 39 | void set_volume(int p_volume); 40 | int get_volume(); 41 | 42 | void set_pitch(float p_pitch); 43 | float get_pitch(); 44 | 45 | void set_rate(int p_rate); 46 | int get_rate(); 47 | 48 | ~TTSDriverNSSpeech(); 49 | }; 50 | 51 | static TTSDriverNSSpeech singleton; 52 | -------------------------------------------------------------------------------- /src/gdnative.cpp: -------------------------------------------------------------------------------- 1 | /*************************************************************************/ 2 | /* gdnative_core.cpp */ 3 | /*************************************************************************/ 4 | 5 | #include "gdnative.h" 6 | #include "array.h" 7 | #include "dictionary.h" 8 | #include "variant.h" 9 | #include "string.h" 10 | #include "tts_driver.h" 11 | 12 | const godot_gdnative_core_api_struct *api = nullptr; 13 | const godot_gdnative_ext_nativescript_api_struct *nativescript_api = nullptr; 14 | const godot_gdnative_ext_nativescript_1_1_api_struct *nativescript_1_1_api = NULL; 15 | 16 | extern "C" void GDN_EXPORT godot_gdnative_init(godot_gdnative_init_options *p_options) { 17 | api = p_options->api_struct; 18 | for (unsigned int i = 0; i < api->num_extensions; i++) { 19 | if (api->extensions[i]->type == GDNATIVE_EXT_NATIVESCRIPT) { 20 | nativescript_api = reinterpret_cast(api->extensions[i]); 21 | const godot_gdnative_api_struct *next = nativescript_api->next; 22 | while (next) { 23 | if ((next->version.major == 1) && (next->version.minor == 1)) { 24 | nativescript_1_1_api = reinterpret_cast(next); 25 | } 26 | next = next->next; 27 | } 28 | } 29 | } 30 | }; 31 | 32 | extern "C" void GDN_EXPORT godot_gdnative_terminate(godot_gdnative_terminate_options *p_options) { 33 | api = NULL; 34 | nativescript_api = NULL; 35 | nativescript_1_1_api = NULL; 36 | }; 37 | 38 | extern "C" void GDN_EXPORT godot_nativescript_init(void *p_handle) { 39 | TTSDriver::register_methods(p_handle); 40 | if (TTSDriver::get_singleton()) { 41 | if (!TTSDriver::get_singleton()->initialize()) { 42 | DEBUG_PRINT_ERROR("Cannot initialize TTSDriver!"); 43 | } 44 | } else { 45 | DEBUG_PRINT_ERROR("No TTSDriver singleton!"); 46 | } 47 | }; 48 | -------------------------------------------------------------------------------- /src/ustring.cpp: -------------------------------------------------------------------------------- 1 | /*************************************************************************/ 2 | /* ustring.cpp */ 3 | /*************************************************************************/ 4 | 5 | #include 6 | 7 | const char *CharString::get_data() const { 8 | return api->godot_char_string_get_data(&gdn); 9 | }; 10 | 11 | CharString::~CharString() { 12 | api->godot_char_string_destroy(&gdn); 13 | }; 14 | 15 | String::String() { 16 | api->godot_string_new(&gdn); 17 | }; 18 | 19 | String::String(const char *p_string) { 20 | api->godot_string_new(&gdn); 21 | api->godot_string_parse_utf8(&gdn, p_string); 22 | }; 23 | 24 | String::String(const wchar_t *p_string) { 25 | api->godot_string_new_with_wide_string(&gdn, p_string, wcslen(p_string)); 26 | }; 27 | 28 | String::String(const String &p_string) { 29 | api->godot_string_new_copy(&gdn, &p_string.gdn); 30 | }; 31 | 32 | const wchar_t *String::c_str() const { 33 | return api->godot_string_wide_str(&gdn); 34 | }; 35 | 36 | CharString String::utf8() const { 37 | godot_char_string ret = api->godot_string_utf8(&gdn); 38 | return *reinterpret_cast(&ret); 39 | }; 40 | 41 | String String::itos(int64_t p_num, godot_int p_base) { 42 | godot_string ret = api->godot_string_num_int64(p_num, p_base); 43 | return *reinterpret_cast(&ret); 44 | } 45 | 46 | String &String::operator=(const String &p_string) { 47 | api->godot_string_destroy(&gdn); 48 | api->godot_string_new_copy(&gdn, &p_string.gdn); 49 | return *this; 50 | }; 51 | 52 | String String::operator+(const String &p_string) const { 53 | String ret = *this; 54 | ret.gdn = api->godot_string_operator_plus(&ret.gdn, &p_string.gdn); 55 | return ret; 56 | }; 57 | 58 | bool String::operator==(const String &p_string) const { 59 | return api->godot_string_operator_equal(&gdn, &p_string.gdn); 60 | }; 61 | 62 | String::~String() { 63 | api->godot_string_destroy(&gdn); 64 | }; 65 | 66 | String operator+(const char *p_string_a, const String &p_string_b) { 67 | return String(p_string_a) + p_string_b; 68 | }; 69 | 70 | String operator+(const wchar_t *p_string_a, const String &p_string_b) { 71 | return String(p_string_a) + p_string_b; 72 | }; 73 | -------------------------------------------------------------------------------- /src/variant.cpp: -------------------------------------------------------------------------------- 1 | /*************************************************************************/ 2 | /* variant.cpp */ 3 | /*************************************************************************/ 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | Variant::Variant() { 11 | api->godot_variant_new_nil(&gdn); 12 | }; 13 | 14 | Variant::Variant(const Variant& p_var) { 15 | api->godot_variant_new_copy(&gdn, &p_var.gdn); 16 | }; 17 | 18 | Variant::Variant(bool p_bool) { 19 | api->godot_variant_new_bool(&gdn, p_bool); 20 | }; 21 | 22 | Variant::Variant(int p_int) { 23 | api->godot_variant_new_int(&gdn, p_int); 24 | }; 25 | 26 | Variant::Variant(float p_float) { 27 | api->godot_variant_new_real(&gdn, p_float); 28 | }; 29 | 30 | Variant::Variant(const String& p_string) { 31 | api->godot_variant_new_string(&gdn, reinterpret_cast(&p_string)); 32 | }; 33 | 34 | Variant::Variant(const Dictionary& p_dict) { 35 | api->godot_variant_new_dictionary(&gdn, reinterpret_cast(&p_dict)); 36 | }; 37 | 38 | Variant::Variant(const Array& p_array) { 39 | api->godot_variant_new_array(&gdn, reinterpret_cast(&p_array)); 40 | }; 41 | 42 | Variant &Variant::operator=(const Variant& p_var) { 43 | api->godot_variant_destroy(&gdn); 44 | api->godot_variant_new_copy(&gdn, &p_var.gdn); 45 | return *this; 46 | }; 47 | 48 | Variant::operator bool() const { 49 | return api->godot_variant_booleanize(&gdn); 50 | }; 51 | 52 | Variant::operator float() const { 53 | return api->godot_variant_as_real(&gdn); 54 | }; 55 | 56 | Variant::operator int() const { 57 | return api->godot_variant_as_int(&gdn); 58 | }; 59 | 60 | Variant::operator String() const { 61 | godot_string ret = api->godot_variant_as_string(&gdn); 62 | return *reinterpret_cast(&ret); 63 | }; 64 | 65 | Variant::operator Dictionary() const { 66 | godot_dictionary ret = api->godot_variant_as_dictionary(&gdn); 67 | return *reinterpret_cast(&ret); 68 | }; 69 | 70 | Variant::operator Array() const { 71 | godot_array ret = api->godot_variant_as_array(&gdn); 72 | return *reinterpret_cast(&ret); 73 | }; 74 | 75 | Variant::~Variant() { 76 | api->godot_variant_destroy(&gdn); 77 | }; 78 | -------------------------------------------------------------------------------- /meson.build: -------------------------------------------------------------------------------- 1 | project( 2 | 'libgdtts', 3 | 'cpp', 4 | meson_version : '>= 0.45' 5 | ) 6 | library_version = '0.2.0' 7 | 8 | prefix = '' 9 | cpp_args = [] 10 | link_args = [] 11 | dependencies = [] 12 | sources = [ 13 | 'src/array.cpp', 14 | 'src/dictionary.cpp', 15 | 'src/gdnative.cpp', 16 | 'src/tts_driver.cpp', 17 | 'src/ustring.cpp', 18 | 'src/variant.cpp' 19 | ] 20 | 21 | if host_machine.system() == 'darwin' 22 | add_languages('objcpp') 23 | sources += [ 24 | 'src/tts_nsspeech/tts_nsspeech.mm' 25 | ] 26 | cpp_args += [ 27 | '-DOS_MACOS', 28 | '-std=c++14', 29 | '-Wwrite-strings', 30 | ] 31 | link_args += [ 32 | '-framework', 'Foundation', 33 | '-framework', 'Appkit', 34 | '-framework', 'Cocoa', 35 | '-Wl,-undefined,dynamic_lookup', 36 | ] 37 | elif host_machine.system() == 'linux' 38 | dependencies += dependency('speech-dispatcher', required: true) 39 | sources += [ 40 | 'src/tts_libspeechd/tts_libspeechd.cpp' 41 | ] 42 | cpp_args += [ 43 | '-DOS_LINUX', 44 | '-std=c++14', 45 | '-fPIC', 46 | '-Wwrite-strings', 47 | '-Wno-attributes' 48 | ] 49 | link_args += [ 50 | '-static-libgcc', 51 | '-static-libstdc++', 52 | '-Wl,--no-undefined', 53 | '-Wl,-R,\'\$ORIGIN\'', 54 | ] 55 | elif host_machine.system() == 'windows' 56 | sources += [ 57 | 'src/tts_sapi/tts_sapi.cpp' 58 | ] 59 | if meson.get_compiler('cpp').get_id() == 'msvc' 60 | prefix = 'lib' 61 | cpp_args += [ 62 | '/TP' 63 | ] 64 | link_args += [ 65 | '/WX', 66 | 'kernel32.lib', 67 | 'ole32.lib', 68 | 'oleaut32.lib', 69 | 'sapi.lib' 70 | ] 71 | else 72 | cpp_args += [ 73 | '-DOS_WINDOWS', 74 | '-std=c++14', 75 | '-Wwrite-strings', 76 | ] 77 | link_args += [ 78 | '-lkernel32', 79 | '-lole32', 80 | '-loleaut32', 81 | '-lsapi', 82 | '--static', 83 | '-static-libgcc', 84 | '-static-libstdc++', 85 | '-Wl,--no-undefined', 86 | ] 87 | endif 88 | else 89 | error('Text-to-speech is not implemented on this platform!') 90 | endif 91 | 92 | shared_library( 93 | prefix + 'gdtts' + '.' + target_machine.system() + '.' + target_machine.cpu_family(), 94 | sources, 95 | dependencies : dependencies, 96 | link_args : link_args, 97 | cpp_args : cpp_args, 98 | include_directories : include_directories('src', 'godot_headers') 99 | ) 100 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ⚠️ This module is not actively maintained ⚠️ 2 | ## Godot 4.0 have native TTS implementation for all platforms (Android, iOS, Linux, macOS, Windows and Web), see https://github.com/godotengine/godot/pull/56192 (backport for 3.x - https://github.com/godotengine/godot/pull/61316) 3 | 4 | --- 5 | 6 | ## Godot text-to-speech module (libgdtts) 7 | 8 | This GDNative module implements minimal native text-to-speech interface for Godot Engine on Windows, macOS and Linux. 9 | 10 | ### Dependencies: 11 | #### Linux: 12 | - Godot 3.x (3.0.6+) 13 | - GCC or Clang 14 | - Meson build system 15 | - Speech Dispatcher library and development files (libspeechd) 16 | 17 | #### macOS: 18 | - Godot 3.x (3.0.6+) 19 | - Xcode 20 | - Meson build system 21 | 22 | #### Windows: 23 | - Godot 3.x (3.0.6+) 24 | - MinGW-W64 or Microsoft Visual C++ 25 | - Meson build system 26 | 27 | ### Binary downloads: 28 | Download [release](https://github.com/bruvzg/godot_tts/releases) archive and extract it into your project directory. 29 | 30 | ### Compiling: 31 | 1. Go to the root directory of the source code. 32 | 2. Run the build system: 33 | ```sh 34 | meson [build_dir] --buildtype=release 35 | ninja -C [build_dir] 36 | ``` 37 | 38 | ### Loading module: 39 | ```gdscript 40 | const TTSDriver = preload("res://TTSDriver.gdns") 41 | ... 42 | var tts = TTSDriver.new() 43 | ``` 44 | 45 | ### Module API: 46 | - `void tts.speak(String text, bool interrupt)`, Begins speaking synthesized text. 47 | - `void tts.stop()`, Stops synthesis in progress. 48 | - `bool tts.is_speaking()`, Returns `true` if the synthesizer is generating speech, `false` otherwise. 49 | - `Array tts.get_voices()`, Returns an `Array` of voice information `Dictionaries`. 50 | Where `String name` is voice identifier and `String language` is language code in `lang_variant` format. 51 | `lang` part is 2 or 3-letter code based on the ISO-639 standard, in lowercase. 52 | `variant` part is engine dependent string describing country, region or/and dialect. 53 | - `void tts.set_voice(String name)`, Sets the speech synthesizer’s current voice. 54 | - `int tts.get_volume()`/`void tts.set_volume(int volume)`, The speech synthesizer’s volume. 55 | The granularity of the volume is engine dependent. 56 | Values may be truncated. Ranges from 0 to 100. 57 | - `float tts.get_pitch()`/`void tts.set_pitch(float pitch)`, The speech synthesizer’s pitch. 58 | The granularity of the pitch is engine dependent. 59 | Values may be truncated. Ranges from -10 to 10. 60 | - `int tts.get_rate`/`void tts.set_rate(int rate)`, The speech synthesizer’s rendering rate adjustment. 61 | The granularity of the rate is engine dependent. 62 | Values may be truncated. Ranges from -100 to 100. 63 | 64 | ### License: 65 | - The source code for the **libgdtts** module is released under unlicense. 66 | For more information, see http://unlicense.org/ or the accompanying UNLICENSE file. 67 | - **Godot** and **GodotNativeTools** are licensed under MIT license. 68 | For more information, see https://github.com/godotengine/godot/blob/master/LICENSE.txt. 69 | - **Speech Dispatcher** C API library is licensed under GNU Lesser General Public License 2.1 or later. 70 | For more information, see https://github.com/brailcom/speechd/blob/master/src/api/c/libspeechd.c 71 | - **Speech Dispatcher** backend is licensed under GNU General Public License. 72 | For more information, see (https://github.com/brailcom/speechd/blob/master/COPYING. 73 | -------------------------------------------------------------------------------- /src/tts_libspeechd/tts_libspeechd.cpp: -------------------------------------------------------------------------------- 1 | /*************************************************************************/ 2 | /* tts_libspeechd.cpp */ 3 | /*************************************************************************/ 4 | 5 | #include "tts_libspeechd.h" 6 | 7 | std::list TTSDriverSPD::messages; 8 | 9 | void TTSDriverSPD::end_of_speech(size_t msg_id, size_t client_id, SPDNotificationType type) { 10 | messages.erase(std::find(messages.begin(), messages.end(), msg_id)); 11 | }; 12 | 13 | TTSDriverSPD::TTSDriverSPD() { 14 | synth = NULL; 15 | }; 16 | 17 | bool TTSDriverSPD::initialize() { 18 | synth = spd_open("Godot", NULL, NULL, SPD_MODE_THREADED); 19 | if (synth) { 20 | synth->callback_end = synth->callback_cancel = end_of_speech; 21 | spd_set_notification_on(synth, SPD_END); 22 | spd_set_notification_on(synth, SPD_CANCEL); 23 | return true; 24 | } else { 25 | DEBUG_PRINT_ERROR("Cannot initialize Speech Dispatcher!"); 26 | return false; 27 | } 28 | }; 29 | 30 | void TTSDriverSPD::speak(const String &p_text, bool p_interrupt) { 31 | if (synth) { 32 | if (p_interrupt) { 33 | spd_cancel(synth); 34 | } 35 | int id = spd_say(synth, SPD_MESSAGE, p_text.utf8().get_data()); 36 | if (id != -1) 37 | messages.push_back(id); 38 | } else { 39 | DEBUG_PRINT_WARNING("TTS driver is not initialized!"); 40 | } 41 | }; 42 | 43 | void TTSDriverSPD::stop() { 44 | if (synth) { 45 | spd_cancel(synth); 46 | } else { 47 | DEBUG_PRINT_WARNING("TTS driver is not initialized!"); 48 | } 49 | }; 50 | 51 | Array TTSDriverSPD::get_voices() { 52 | Array list; 53 | if (synth) { 54 | SPDVoice **voices = spd_list_synthesis_voices(synth); 55 | if (voices != NULL) { 56 | SPDVoice **voices_ptr = voices; 57 | while (*voices_ptr != NULL) { 58 | Dictionary voice_d; 59 | voice_d.set(String("name"), String((*voices_ptr)->name)); 60 | voice_d.set(String("language"), String((*voices_ptr)->language) + "_" + String((*voices_ptr)->variant)); 61 | list.push_back(voice_d); 62 | 63 | voices_ptr++; 64 | } 65 | free_spd_voices(voices); 66 | } 67 | } else { 68 | DEBUG_PRINT_WARNING("TTS driver is not initialized!"); 69 | } 70 | return list; 71 | }; 72 | 73 | void TTSDriverSPD::set_voice(const String &p_voice) { 74 | if (synth) { 75 | spd_set_synthesis_voice(synth, p_voice.utf8().get_data()); 76 | } else { 77 | DEBUG_PRINT_WARNING("TTS driver is not initialized!"); 78 | } 79 | }; 80 | 81 | bool TTSDriverSPD::is_speaking() { 82 | if (synth) { 83 | return !messages.empty(); 84 | } else { 85 | DEBUG_PRINT_WARNING("TTS driver is not initialized!"); 86 | return false; 87 | } 88 | }; 89 | 90 | void TTSDriverSPD::set_volume(int p_volume) { 91 | if (synth) { 92 | spd_set_volume(synth, p_volume * 2 - 100); 93 | } else { 94 | DEBUG_PRINT_WARNING("TTS driver is not initialized!"); 95 | } 96 | }; 97 | 98 | int TTSDriverSPD::get_volume() { 99 | if (synth) { 100 | return spd_get_volume(synth) / 2 + 100; 101 | } else { 102 | DEBUG_PRINT_WARNING("TTS driver is not initialized!"); 103 | return 0; 104 | } 105 | }; 106 | 107 | void TTSDriverSPD::set_pitch(float p_pitch) { 108 | if (synth) { 109 | spd_set_voice_pitch(synth, p_pitch); 110 | } else { 111 | DEBUG_PRINT_WARNING("TTS driver is not initialized!"); 112 | } 113 | }; 114 | 115 | float TTSDriverSPD::get_pitch() { 116 | if (synth) { 117 | return spd_get_voice_pitch(synth) / 10.0f; 118 | } else { 119 | DEBUG_PRINT_WARNING("TTS driver is not initialized!"); 120 | return 0; 121 | } 122 | }; 123 | 124 | void TTSDriverSPD::set_rate(int p_rate) { 125 | if (synth) { 126 | spd_set_voice_rate(synth, p_rate * 10.0f); 127 | } else { 128 | DEBUG_PRINT_WARNING("TTS driver is not initialized!"); 129 | } 130 | }; 131 | 132 | int TTSDriverSPD::get_rate() { 133 | if (synth) { 134 | return spd_get_voice_rate(synth); 135 | } else { 136 | DEBUG_PRINT_WARNING("TTS driver is not initialized!"); 137 | return 0; 138 | } 139 | }; 140 | 141 | TTSDriverSPD::~TTSDriverSPD() { 142 | if (synth) { 143 | spd_close(synth); 144 | synth = NULL; 145 | } 146 | }; 147 | -------------------------------------------------------------------------------- /demo/UI.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=3 format=2] 2 | 3 | [ext_resource path="res://volume.png" type="Texture" id=1] 4 | 5 | [sub_resource type="GDScript" id=1] 6 | script/source = "extends Control 7 | 8 | const TTSDriver = preload(\"res://TTSDriver.gdns\") 9 | var tts 10 | 11 | func _process(delta): 12 | if tts.is_speaking(): 13 | get_node(\"ColorRect\").color = Color(1, 0, 0) 14 | else: 15 | get_node(\"ColorRect\").color = Color(0, 0, 0) 16 | 17 | func _ready(): 18 | tts = TTSDriver.new() 19 | set_process(true) 20 | var voices = tts.get_voices(); 21 | for v in voices: 22 | var readable_name = v[\"name\"] 23 | readable_name = readable_name.replace(\"com.apple.speech.synthesis.voice.\", \"\") 24 | readable_name = readable_name.replace(\"HKEY_LOCAL_MACHINE\\\\SOFTWARE\\\\Microsoft\\\\Speech\\\\Voices\\\\Tokens\\\\\", \"\") 25 | get_node(\"OptionButton\").add_item(\"\\\"\" + readable_name + \"\\\" speaks \" + v[\"language\"]) 26 | get_node(\"OptionButton\").set_item_metadata(get_node(\"OptionButton\").get_item_count() - 1, v[\"name\"]) 27 | get_node(\"HSliderVol\").value = tts.get_volume() 28 | get_node(\"HSliderRate\").value = tts.get_rate() 29 | get_node(\"HSliderPitch\").value = tts.get_pitch() 30 | 31 | func _on_voice_selected(ID): 32 | tts.set_voice(get_node(\"OptionButton\").get_item_metadata(ID)) 33 | 34 | func _on_speak_pressed(): 35 | tts.speak(get_node(\"LineEdit\").text, false) 36 | 37 | func _on_volmue_changed(value): 38 | tts.set_volume(value) 39 | 40 | func _on_interrupt_pressed(): 41 | tts.speak(\"Halt!\", true) 42 | 43 | func _on_stop_pressed(): 44 | tts.stop() 45 | 46 | func _on_rate_value_changed(value): 47 | tts.set_rate(value) 48 | 49 | func _on_HSliderPitch_value_changed(value): 50 | tts.set_pitch(value) 51 | " 52 | 53 | [node name="Control" type="Control"] 54 | anchor_right = 1.0 55 | anchor_bottom = 1.0 56 | margin_left = 2.0 57 | margin_right = 2.0 58 | script = SubResource( 1 ) 59 | 60 | [node name="LabelVoice" type="Label" parent="."] 61 | margin_left = 18.0 62 | margin_top = 20.0 63 | margin_right = 68.0 64 | margin_bottom = 40.0 65 | text = "Voice:" 66 | 67 | [node name="OptionButton" type="OptionButton" parent="."] 68 | margin_left = 88.0 69 | margin_top = 20.0 70 | margin_right = 778.0 71 | margin_bottom = 45.0 72 | toggle_mode = false 73 | 74 | [node name="LabelVolume" type="Label" parent="."] 75 | margin_left = 18.0 76 | margin_top = 60.0 77 | margin_right = 73.0 78 | margin_bottom = 80.0 79 | text = "Volume:" 80 | 81 | [node name="LabelRate" type="Label" parent="."] 82 | margin_left = 18.0 83 | margin_top = 100.0 84 | margin_right = 73.0 85 | margin_bottom = 120.0 86 | text = "Rate:" 87 | __meta__ = { 88 | "_edit_use_anchors_": false 89 | } 90 | 91 | [node name="LineEdit" type="LineEdit" parent="."] 92 | margin_left = 18.0 93 | margin_top = 420.0 94 | margin_right = 778.0 95 | margin_bottom = 449.0 96 | text = "test text-to-speech" 97 | 98 | [node name="Button" type="Button" parent="."] 99 | margin_left = 618.0 100 | margin_top = 460.0 101 | margin_right = 778.0 102 | margin_bottom = 485.0 103 | text = "Speak" 104 | 105 | [node name="Button2" type="Button" parent="."] 106 | margin_left = 618.0 107 | margin_top = 540.0 108 | margin_right = 778.0 109 | margin_bottom = 565.0 110 | text = "Halt!" 111 | 112 | [node name="Button3" type="Button" parent="."] 113 | margin_left = 618.0 114 | margin_top = 500.0 115 | margin_right = 778.0 116 | margin_bottom = 525.0 117 | text = "Stop" 118 | 119 | [node name="HSliderVol" type="HSlider" parent="."] 120 | margin_left = 88.0 121 | margin_top = 60.0 122 | margin_right = 468.0 123 | margin_bottom = 80.0 124 | value = 100.0 125 | 126 | [node name="HSliderRate" type="HSlider" parent="."] 127 | margin_left = 88.0 128 | margin_top = 100.0 129 | margin_right = 468.0 130 | margin_bottom = 120.0 131 | min_value = -100.0 132 | value = 100.0 133 | __meta__ = { 134 | "_edit_use_anchors_": false 135 | } 136 | 137 | [node name="LabelPitch" type="Label" parent="."] 138 | margin_left = 18.0 139 | margin_top = 140.0 140 | margin_right = 73.0 141 | margin_bottom = 160.0 142 | text = "Pitch:" 143 | __meta__ = { 144 | "_edit_use_anchors_": false 145 | } 146 | 147 | [node name="HSliderPitch" type="HSlider" parent="."] 148 | margin_left = 88.0 149 | margin_top = 140.0 150 | margin_right = 468.0 151 | margin_bottom = 160.0 152 | min_value = -10.0 153 | max_value = 10.0 154 | value = 0.0 155 | __meta__ = { 156 | "_edit_use_anchors_": false 157 | } 158 | 159 | [node name="ColorRect" type="ColorRect" parent="."] 160 | margin_left = 18.0 161 | margin_top = 460.0 162 | margin_right = 198.0 163 | margin_bottom = 560.0 164 | color = Color( 0, 0, 0, 1 ) 165 | 166 | [node name="Icon" type="Sprite" parent="ColorRect"] 167 | position = Vector2( 90, 30 ) 168 | texture = ExtResource( 1 ) 169 | 170 | [node name="LabelISS" type="Label" parent="ColorRect"] 171 | margin_top = 65.0 172 | margin_right = 180.0 173 | margin_bottom = 100.0 174 | custom_colors/font_color = Color( 0, 0, 0, 1 ) 175 | text = "SPEAKING" 176 | align = 1 177 | valign = 1 178 | 179 | [connection signal="item_selected" from="OptionButton" to="." method="_on_voice_selected"] 180 | [connection signal="pressed" from="Button" to="." method="_on_speak_pressed"] 181 | [connection signal="pressed" from="Button2" to="." method="_on_interrupt_pressed"] 182 | [connection signal="pressed" from="Button3" to="." method="_on_stop_pressed"] 183 | [connection signal="value_changed" from="HSliderVol" to="." method="_on_volmue_changed"] 184 | [connection signal="value_changed" from="HSliderRate" to="." method="_on_rate_value_changed"] 185 | [connection signal="value_changed" from="HSliderPitch" to="." method="_on_HSliderPitch_value_changed"] 186 | -------------------------------------------------------------------------------- /src/tts_nsspeech/tts_nsspeech.mm: -------------------------------------------------------------------------------- 1 | /*************************************************************************/ 2 | /* tts_nsspeech.mm */ 3 | /*************************************************************************/ 4 | 5 | #include "tts_nsspeech.h" 6 | 7 | @implementation GodotSpeechSynthesizerDelegate 8 | - (void)discardMessages { 9 | messages.clear(); 10 | }; 11 | 12 | - (void)appendMessage:(const String &)message { 13 | messages.push_back(message); 14 | }; 15 | 16 | - (void)speechSynthesizer:(NSSpeechSynthesizer *)sender didFinishSpeaking:(BOOL)finishedSpeaking { 17 | if (messages.size() > 0) { 18 | messages.pop_front(); 19 | if (messages.size() > 0) { 20 | [sender startSpeakingString:[NSString stringWithUTF8String:messages.front().utf8().get_data()]]; 21 | } 22 | } 23 | }; 24 | 25 | - (bool)isSpeaking { 26 | return !messages.empty(); 27 | }; 28 | @end 29 | 30 | TTSDriverNSSpeech::TTSDriverNSSpeech() { 31 | synth = NULL; 32 | pitch = 0; 33 | delegate = NULL; 34 | }; 35 | 36 | bool TTSDriverNSSpeech::initialize() { 37 | synth = [[NSSpeechSynthesizer alloc] init]; 38 | if (synth) { 39 | delegate = [[GodotSpeechSynthesizerDelegate alloc] init]; 40 | if (delegate) { 41 | [synth setDelegate:delegate]; 42 | return true; 43 | } else { 44 | DEBUG_PRINT_ERROR("Cannot initialize NSSpeechSynthesizerDelegate"); 45 | return false; 46 | } 47 | } else { 48 | DEBUG_PRINT_ERROR("Cannot initialize NSSpeechSynthesizer!"); 49 | return false; 50 | } 51 | }; 52 | 53 | void TTSDriverNSSpeech::speak(const String &p_text, bool p_interrupt) { 54 | if (synth && delegate) { 55 | if (p_interrupt) { 56 | [delegate discardMessages]; 57 | [synth stopSpeaking]; 58 | [delegate appendMessage:p_text]; 59 | [synth startSpeakingString:[NSString stringWithUTF8String:p_text.utf8().get_data()]]; 60 | } else { 61 | [delegate appendMessage:p_text]; 62 | if (![synth isSpeaking]) { 63 | [synth startSpeakingString:[NSString stringWithUTF8String:p_text.utf8().get_data()]]; 64 | } 65 | } 66 | } else { 67 | DEBUG_PRINT_WARNING("TTS driver is not initialized!"); 68 | } 69 | }; 70 | 71 | void TTSDriverNSSpeech::stop() { 72 | if (synth && delegate) { 73 | [delegate discardMessages]; 74 | [synth stopSpeaking]; 75 | } else { 76 | DEBUG_PRINT_WARNING("TTS driver is not initialized!"); 77 | } 78 | }; 79 | 80 | Array TTSDriverNSSpeech::get_voices() { 81 | Array list; 82 | for (NSString *voiceIdentifierString in [NSSpeechSynthesizer availableVoices]) { 83 | NSString *voiceLocaleIdentifier = [[NSSpeechSynthesizer attributesForVoice:voiceIdentifierString] objectForKey:NSVoiceLocaleIdentifier]; 84 | Dictionary voice_d; 85 | voice_d.set(String("name"), String([voiceIdentifierString UTF8String])); 86 | voice_d.set(String("language"), String([voiceLocaleIdentifier UTF8String])); 87 | list.push_back(voice_d); 88 | } 89 | return list; 90 | }; 91 | 92 | void TTSDriverNSSpeech::set_voice(const String &p_voice) { 93 | if (synth) { 94 | [synth setObject:nil forProperty:NSSpeechResetProperty error:nil]; 95 | 96 | [synth setVoice:[NSString stringWithUTF8String:p_voice.utf8().get_data()]]; 97 | float base_pitch = [[synth objectForProperty:NSSpeechPitchBaseProperty error:nil] floatValue]; 98 | int val = base_pitch + (base_pitch * pitch / 10.0f); 99 | 100 | [synth setObject:[NSNumber numberWithInt:val] forProperty:NSSpeechPitchBaseProperty error:nullptr]; 101 | } else { 102 | DEBUG_PRINT_WARNING("TTS driver is not initialized!"); 103 | } 104 | }; 105 | 106 | bool TTSDriverNSSpeech::is_speaking() { 107 | if (synth && delegate) { 108 | return [synth isSpeaking] || [delegate isSpeaking]; 109 | } else { 110 | DEBUG_PRINT_WARNING("TTS driver is not initialized!"); 111 | return false; 112 | } 113 | }; 114 | 115 | void TTSDriverNSSpeech::set_volume(int p_volume) { 116 | if (synth) { 117 | [synth setVolume:p_volume / 100.0]; 118 | } else { 119 | DEBUG_PRINT_WARNING("TTS driver is not initialized!"); 120 | } 121 | }; 122 | 123 | int TTSDriverNSSpeech::get_volume() { 124 | if (synth) { 125 | return [synth volume] * 100.0; 126 | } else { 127 | DEBUG_PRINT_WARNING("TTS driver is not initialized!"); 128 | return 0; 129 | } 130 | }; 131 | 132 | void TTSDriverNSSpeech::set_pitch(float p_pitch) { 133 | if (synth) { 134 | pitch = p_pitch; 135 | 136 | // Get base pitch. 137 | [synth setObject:nil forProperty:NSSpeechResetProperty error:nil]; 138 | float base_pitch = [[synth objectForProperty:NSSpeechPitchBaseProperty error:nil] floatValue]; 139 | int val = base_pitch + (base_pitch * pitch / 10.0f); 140 | 141 | [synth setObject:[NSNumber numberWithInt:val] forProperty:NSSpeechPitchBaseProperty error:nullptr]; 142 | } else { 143 | DEBUG_PRINT_WARNING("TTS driver is not initialized!"); 144 | } 145 | }; 146 | 147 | float TTSDriverNSSpeech::get_pitch() { 148 | if (synth) { 149 | return pitch; 150 | } else { 151 | DEBUG_PRINT_WARNING("TTS driver is not initialized!"); 152 | return 0; 153 | } 154 | }; 155 | 156 | void TTSDriverNSSpeech::set_rate(int p_rate) { 157 | if (synth) { 158 | [synth setRate:200 + (p_rate / 2)]; 159 | } else { 160 | DEBUG_PRINT_WARNING("TTS driver is not initialized!"); 161 | } 162 | }; 163 | 164 | int TTSDriverNSSpeech::get_rate() { 165 | if (synth) { 166 | return ([synth rate] - 200) * 2; 167 | } else { 168 | DEBUG_PRINT_WARNING("TTS driver is not initialized!"); 169 | return 0; 170 | } 171 | }; 172 | 173 | TTSDriverNSSpeech::~TTSDriverNSSpeech() { 174 | if (synth) { 175 | [synth release]; 176 | synth = NULL; 177 | } 178 | if (delegate) { 179 | [delegate release]; 180 | delegate = NULL; 181 | } 182 | }; 183 | -------------------------------------------------------------------------------- /src/tts_sapi/tts_sapi.cpp: -------------------------------------------------------------------------------- 1 | /*************************************************************************/ 2 | /* tts_sapi.cpp */ 3 | /*************************************************************************/ 4 | 5 | #include "tts_sapi.h" 6 | 7 | #include 8 | #include 9 | #include 10 | 11 | TTSDriverSAPI::TTSDriverSAPI() { 12 | co_initialized = false; 13 | synth = NULL; 14 | pitch = 0; 15 | }; 16 | 17 | bool TTSDriverSAPI::initialize() { 18 | HRESULT hr = CoInitialize(NULL); 19 | if (SUCCEEDED(hr)) { 20 | co_initialized = true; 21 | HRESULT hr = CoCreateInstance(CLSID_SpVoice, NULL, CLSCTX_ALL, IID_ISpVoice, (void **)&synth); 22 | if (SUCCEEDED(hr)) { 23 | return true; 24 | } else { 25 | synth = NULL; 26 | if (co_initialized) { 27 | co_initialized = false; 28 | CoUninitialize(); 29 | } 30 | DEBUG_PRINT_ERROR("Cannot initialize ISpVoice"); 31 | return false; 32 | } 33 | } else { 34 | co_initialized = false; 35 | DEBUG_PRINT_ERROR("CoInitializeEx failed!"); 36 | return false; 37 | } 38 | }; 39 | 40 | void TTSDriverSAPI::speak(const String &p_text, bool p_interrupt) { 41 | if (synth) { 42 | String text; 43 | DWORD flags = SPF_ASYNC; 44 | if (pitch == 0) { 45 | text = p_text; 46 | flags |= SPF_IS_NOT_XML; 47 | } else { 48 | text = String("") + p_text + String(""); 49 | flags |= SPF_IS_XML; 50 | } 51 | if (p_interrupt) { 52 | synth->Speak(text.c_str(), flags | SPF_PURGEBEFORESPEAK, NULL); 53 | } else { 54 | synth->Speak(text.c_str(), flags, NULL); 55 | } 56 | } else { 57 | DEBUG_PRINT_WARNING("TTS driver is not initialized!"); 58 | } 59 | }; 60 | 61 | void TTSDriverSAPI::stop() { 62 | if (synth) { 63 | synth->Speak(NULL, SPF_PURGEBEFORESPEAK, NULL); 64 | } else { 65 | DEBUG_PRINT_WARNING("TTS driver is not initialized!"); 66 | } 67 | }; 68 | 69 | Array TTSDriverSAPI::get_voices() { 70 | Array list; 71 | IEnumSpObjectTokens *cpEnum; 72 | ISpObjectToken *cpVoiceToken; 73 | ISpDataKey *cpDataKeyAttribs; 74 | ULONG ulCount = 0; 75 | HRESULT hr = SpEnumTokens(SPCAT_VOICES, NULL, NULL, &cpEnum); 76 | if (SUCCEEDED(hr)) { 77 | hr = cpEnum->GetCount(&ulCount); 78 | while (SUCCEEDED(hr) && ulCount--) { 79 | hr = cpEnum->Next(1, &cpVoiceToken, NULL); 80 | HRESULT hr_attr = cpVoiceToken->OpenKey(SPTOKENKEY_ATTRIBUTES, &cpDataKeyAttribs); 81 | if (SUCCEEDED(hr_attr)) { 82 | wchar_t *w_id = 0L; 83 | wchar_t *w_lang = 0L; 84 | cpVoiceToken->GetId(&w_id); 85 | cpDataKeyAttribs->GetStringValue(L"Language", &w_lang); 86 | LCID locale = wcstol(w_lang, NULL, 16); 87 | int locale_chars = GetLocaleInfoW(locale, LOCALE_SISO639LANGNAME, NULL, 0); 88 | int region_chars = GetLocaleInfoW(locale, LOCALE_SISO3166CTRYNAME, NULL, 0); 89 | wchar_t *w_lang_code = new wchar_t[locale_chars]; 90 | wchar_t *w_reg_code = new wchar_t[region_chars]; 91 | GetLocaleInfoW(locale, LOCALE_SISO639LANGNAME, w_lang_code, locale_chars); 92 | GetLocaleInfoW(locale, LOCALE_SISO3166CTRYNAME, w_reg_code, region_chars); 93 | Dictionary voice_d; 94 | voice_d.set(String("name"), String(w_id)); 95 | voice_d.set(String("language"), String(w_lang_code) + "_" + String(w_reg_code)); 96 | list.push_back(voice_d); 97 | delete[] w_lang_code; 98 | delete[] w_reg_code; 99 | cpDataKeyAttribs->Release(); 100 | } 101 | cpVoiceToken->Release(); 102 | } 103 | cpEnum->Release(); 104 | } else { 105 | DEBUG_PRINT_WARNING("TTS driver is not initialized!"); 106 | } 107 | return list; 108 | }; 109 | 110 | void TTSDriverSAPI::set_voice(const String &p_voice) { 111 | if (synth) { 112 | IEnumSpObjectTokens *cpEnum; 113 | ISpObjectToken *cpVoiceToken; 114 | ULONG ulCount = 0; 115 | HRESULT hr = SpEnumTokens(SPCAT_VOICES, NULL, NULL, &cpEnum); 116 | if (SUCCEEDED(hr)) { 117 | hr = cpEnum->GetCount(&ulCount); 118 | 119 | while (SUCCEEDED(hr) && ulCount--) { 120 | wchar_t *w_id = 0L; 121 | hr = cpEnum->Next(1, &cpVoiceToken, NULL); 122 | cpVoiceToken->GetId(&w_id); 123 | if (String(w_id) == p_voice) { 124 | synth->SetVoice(cpVoiceToken); 125 | cpVoiceToken->Release(); 126 | cpEnum->Release(); 127 | return; 128 | } 129 | cpVoiceToken->Release(); 130 | } 131 | cpEnum->Release(); 132 | } 133 | } else { 134 | DEBUG_PRINT_WARNING("TTS driver is not initialized!"); 135 | } 136 | }; 137 | 138 | bool TTSDriverSAPI::is_speaking() { 139 | if (synth) { 140 | SPVOICESTATUS pStatus; 141 | synth->GetStatus(&pStatus, NULL); 142 | return (pStatus.dwRunningState == SPRS_IS_SPEAKING); 143 | } else { 144 | DEBUG_PRINT_WARNING("TTS driver is not initialized!"); 145 | return false; 146 | } 147 | }; 148 | 149 | void TTSDriverSAPI::set_volume(int p_volume) { 150 | if (synth) { 151 | synth->SetVolume(p_volume); 152 | } else { 153 | DEBUG_PRINT_WARNING("TTS driver is not initialized!"); 154 | } 155 | }; 156 | 157 | int TTSDriverSAPI::get_volume() { 158 | USHORT volume = 0; 159 | if (synth) { 160 | synth->GetVolume(&volume); 161 | } else { 162 | DEBUG_PRINT_WARNING("TTS driver is not initialized!"); 163 | } 164 | return volume; 165 | }; 166 | 167 | void TTSDriverSAPI::set_pitch(float p_pitch) { 168 | if (synth) { 169 | pitch = p_pitch; 170 | } else { 171 | DEBUG_PRINT_WARNING("TTS driver is not initialized!"); 172 | } 173 | }; 174 | 175 | float TTSDriverSAPI::get_pitch() { 176 | if (synth) { 177 | return pitch; 178 | } else { 179 | DEBUG_PRINT_WARNING("TTS driver is not initialized!"); 180 | return 0; 181 | } 182 | }; 183 | 184 | void TTSDriverSAPI::set_rate(int p_rate) { 185 | if (synth) { 186 | synth->SetRate(p_rate / 10); 187 | } else { 188 | DEBUG_PRINT_WARNING("TTS driver is not initialized!"); 189 | } 190 | }; 191 | 192 | int TTSDriverSAPI::get_rate() { 193 | long rate = 0; 194 | if (synth) { 195 | synth->GetRate(&rate); 196 | } else { 197 | DEBUG_PRINT_WARNING("TTS driver is not initialized!"); 198 | } 199 | return rate * 10; 200 | }; 201 | 202 | TTSDriverSAPI::~TTSDriverSAPI() { 203 | if (synth) { 204 | synth->Release(); 205 | synth = NULL; 206 | } 207 | if (co_initialized) { 208 | co_initialized = false; 209 | CoUninitialize(); 210 | } 211 | }; 212 | -------------------------------------------------------------------------------- /src/tts_driver.cpp: -------------------------------------------------------------------------------- 1 | /*************************************************************************/ 2 | /* tts_driver.cpp */ 3 | /*************************************************************************/ 4 | 5 | #include "tts_driver.h" 6 | 7 | TTSDriver *TTSDriver::singleton = NULL; 8 | 9 | //GDNative wrappers 10 | void GDCALLINGCONV *gdn_tts_init(godot_object *p_instance, void *p_method_data) { 11 | //TTSDriver is singleton, nothing to do 12 | return NULL; 13 | }; 14 | 15 | void GDCALLINGCONV gdn_tts_terminate(godot_object *p_instance, void *p_method_data, void *p_user_data) { 16 | //TTSDriver is singleton, nothing to do 17 | }; 18 | 19 | //void speak(const String &p_text, bool p_interrupt = false); 20 | Variant GDCALLINGCONV gdn_tts_speak(godot_object *p_instance, void *p_method_data, void *p_user_data, int p_num_args, Variant **p_args) { 21 | if ((p_num_args >= 2) && TTSDriver::get_singleton()) { 22 | TTSDriver::get_singleton()->speak(*p_args[0], *p_args[1]); 23 | } 24 | return Variant(); 25 | }; 26 | 27 | //void stop(); 28 | Variant GDCALLINGCONV gdn_tts_stop(godot_object *p_instance, void *p_method_data, void *p_user_data, int p_num_args, Variant **p_args) { 29 | if (TTSDriver::get_singleton()) { 30 | TTSDriver::get_singleton()->stop(); 31 | } 32 | return Variant(); 33 | }; 34 | 35 | //bool is_speaking(); 36 | Variant GDCALLINGCONV gdn_tts_is_speaking(godot_object *p_instance, void *p_method_data, void *p_user_data, int p_num_args, Variant **p_args) { 37 | if (TTSDriver::get_singleton()) { 38 | return TTSDriver::get_singleton()->is_speaking(); 39 | } 40 | return false; 41 | }; 42 | 43 | //Array get_voices(); 44 | Variant GDCALLINGCONV gdn_tts_get_voices(godot_object *p_instance, void *p_method_data, void *p_user_data, int p_num_args, Variant **p_args) { 45 | if (TTSDriver::get_singleton()) { 46 | return TTSDriver::get_singleton()->get_voices(); 47 | } 48 | return Array(); 49 | }; 50 | 51 | //void set_voice(const String &p_voice); 52 | Variant GDCALLINGCONV gdn_tts_set_voice(godot_object *p_instance, void *p_method_data, void *p_user_data, int p_num_args, Variant **p_args) { 53 | if ((p_num_args >= 1) && TTSDriver::get_singleton()) { 54 | TTSDriver::get_singleton()->set_voice(*p_args[0]); 55 | } 56 | return Variant(); 57 | }; 58 | 59 | //void set_volume(int p_volume); 60 | Variant GDCALLINGCONV gdn_tts_set_volume(godot_object *p_instance, void *p_method_data, void *p_user_data, int p_num_args, Variant **p_args) { 61 | if ((p_num_args >= 1) && TTSDriver::get_singleton()) { 62 | TTSDriver::get_singleton()->set_volume(*p_args[0]); 63 | } 64 | return Variant(); 65 | }; 66 | 67 | //int get_volume(); 68 | Variant GDCALLINGCONV gdn_tts_get_volume(godot_object *p_instance, void *p_method_data, void *p_user_data, int p_num_args, Variant **p_args) { 69 | if (TTSDriver::get_singleton()) { 70 | return TTSDriver::get_singleton()->get_volume(); 71 | } 72 | return 0; 73 | }; 74 | 75 | //void set_pitch(float p_pitch); 76 | Variant GDCALLINGCONV gdn_tts_set_pitch(godot_object *p_instance, void *p_method_data, void *p_user_data, int p_num_args, Variant **p_args) { 77 | if ((p_num_args >= 1) && TTSDriver::get_singleton()) { 78 | TTSDriver::get_singleton()->set_pitch(*p_args[0]); 79 | } 80 | return Variant(); 81 | }; 82 | 83 | //float get_pitch(); 84 | Variant GDCALLINGCONV gdn_tts_get_pitch(godot_object *p_instance, void *p_method_data, void *p_user_data, int p_num_args, Variant **p_args) { 85 | if (TTSDriver::get_singleton()) { 86 | return TTSDriver::get_singleton()->get_pitch(); 87 | } 88 | return 0; 89 | }; 90 | 91 | //void set_rate(int p_rate); 92 | Variant GDCALLINGCONV gdn_tts_set_rate(godot_object *p_instance, void *p_method_data, void *p_user_data, int p_num_args, Variant **p_args) { 93 | if ((p_num_args >= 1) && TTSDriver::get_singleton()) { 94 | TTSDriver::get_singleton()->set_rate(*p_args[0]); 95 | } 96 | return Variant(); 97 | }; 98 | 99 | //int get_rate(); 100 | Variant GDCALLINGCONV gdn_tts_get_rate(godot_object *p_instance, void *p_method_data, void *p_user_data, int p_num_args, Variant **p_args) { 101 | if (TTSDriver::get_singleton()) { 102 | return TTSDriver::get_singleton()->get_rate(); 103 | } 104 | return 0; 105 | }; 106 | 107 | //GDNative registration 108 | void register_nativescript_class(void *p_handle, const char *p_class_name, const char *p_base_class_name, gdn_create_func p_create_func, gdn_destroy_func p_destroy_func, const char *p_docs = NULL) { 109 | 110 | godot_instance_create_func create_func = { 111 | p_create_func, 112 | NULL, 113 | NULL 114 | }; 115 | godot_instance_destroy_func destroy_func = { 116 | p_destroy_func, 117 | NULL, 118 | NULL 119 | }; 120 | nativescript_api->godot_nativescript_register_class(p_handle, p_class_name, p_base_class_name, create_func, destroy_func); 121 | nativescript_api->godot_nativescript_register_tool_class(p_handle, p_class_name, p_base_class_name, create_func, destroy_func); 122 | if (nativescript_1_1_api && p_docs) { 123 | godot_string docs = api->godot_string_chars_to_utf8(p_docs); 124 | nativescript_1_1_api->godot_nativescript_set_class_documentation(p_handle, p_class_name, docs); 125 | api->godot_string_destroy(&docs); 126 | } 127 | }; 128 | 129 | void register_nativescript_method(void *p_handle, const char *p_class_name, const char *p_method_name, gdn_method p_method, unsigned int p_num_args, godot_method_arg *p_args, const char *p_docs = NULL) { 130 | 131 | godot_instance_method method = { 132 | p_method, 133 | NULL, 134 | NULL 135 | }; 136 | godot_method_attributes attr = { 137 | GODOT_METHOD_RPC_MODE_DISABLED 138 | }; 139 | nativescript_api->godot_nativescript_register_method(p_handle, p_class_name, p_method_name, attr, method); 140 | if (nativescript_1_1_api && (p_num_args > 0)) { 141 | nativescript_1_1_api->godot_nativescript_set_method_argument_information(p_handle, p_class_name, p_method_name, p_num_args, p_args); 142 | } 143 | if (nativescript_1_1_api && p_docs) { 144 | godot_string docs = api->godot_string_chars_to_utf8(p_docs); 145 | nativescript_1_1_api->godot_nativescript_set_method_documentation(p_handle, p_class_name, p_method_name, docs); 146 | api->godot_string_destroy(&docs); 147 | } 148 | for (unsigned int i = 0; i < p_num_args; i++) { 149 | api->godot_string_destroy(&p_args[i].name); 150 | api->godot_string_destroy(&p_args[i].hint_string); 151 | } 152 | }; 153 | 154 | //Base class 155 | 156 | TTSDriver *TTSDriver::get_singleton() { 157 | return singleton; 158 | }; 159 | 160 | void TTSDriver::set_singleton() { 161 | singleton = this; 162 | }; 163 | 164 | void TTSDriver::register_methods(void *p_handle) { 165 | register_nativescript_class(p_handle, "TTSDriver", "Reference", reinterpret_cast(&gdn_tts_init), reinterpret_cast(&gdn_tts_terminate), "Native text-to-speech interface"); 166 | godot_method_arg gdn_tts_speak_args[] = { 167 | { 168 | api->godot_string_chars_to_utf8("text"), 169 | GODOT_VARIANT_TYPE_STRING, 170 | GODOT_PROPERTY_HINT_NONE, 171 | api->godot_string_chars_to_utf8("") 172 | }, 173 | { 174 | api->godot_string_chars_to_utf8("interrupt"), 175 | GODOT_VARIANT_TYPE_BOOL, 176 | GODOT_PROPERTY_HINT_NONE, 177 | api->godot_string_chars_to_utf8("") 178 | } 179 | }; 180 | register_nativescript_method(p_handle, "TTSDriver", "speak", reinterpret_cast(&gdn_tts_speak), 2, gdn_tts_speak_args, "Begins speaking synthesized text."); 181 | register_nativescript_method(p_handle, "TTSDriver", "stop", reinterpret_cast(&gdn_tts_stop), 0, NULL, "Stops synthesis in progress."); 182 | register_nativescript_method(p_handle, "TTSDriver", "is_speaking", reinterpret_cast(&gdn_tts_is_speaking), 0, NULL, "Returns [code]true[/code] if the synthesizer is generating speech, [code]false[/code] otherwise."); 183 | register_nativescript_method(p_handle, "TTSDriver", "get_voices", reinterpret_cast(&gdn_tts_get_voices), 0, NULL, "Returns an Array of voice information Dictionaries.\n\"name\" - String, voice identifier\n\"language\" - String, language code"); 184 | godot_method_arg gdn_tts_set_voice_args[] = { 185 | { 186 | api->godot_string_chars_to_utf8("voice"), 187 | GODOT_VARIANT_TYPE_STRING, 188 | GODOT_PROPERTY_HINT_NONE, 189 | api->godot_string_chars_to_utf8("") 190 | } 191 | }; 192 | register_nativescript_method(p_handle, "TTSDriver", "set_voice", reinterpret_cast(&gdn_tts_set_voice), 1, gdn_tts_set_voice_args, "Sets the speech synthesizer’s current voice."); 193 | godot_method_arg gdn_tts_set_volume_args[] = { 194 | { 195 | api->godot_string_chars_to_utf8("volume"), 196 | GODOT_VARIANT_TYPE_INT, 197 | GODOT_PROPERTY_HINT_NONE, 198 | api->godot_string_chars_to_utf8("") 199 | } 200 | }; 201 | register_nativescript_method(p_handle, "TTSDriver", "set_volume", reinterpret_cast(&gdn_tts_set_volume), 1, gdn_tts_set_volume_args, "Sets the speech synthesizer’s volume"); 202 | register_nativescript_method(p_handle, "TTSDriver", "get_volume", reinterpret_cast(&gdn_tts_get_volume), 0, NULL, "Returns the speech synthesizer’s volume"); 203 | godot_method_arg gdn_tts_set_pitch_args[] = { 204 | { 205 | api->godot_string_chars_to_utf8("pitch"), 206 | GODOT_VARIANT_TYPE_REAL, 207 | GODOT_PROPERTY_HINT_NONE, 208 | api->godot_string_chars_to_utf8("") 209 | } 210 | }; 211 | register_nativescript_method(p_handle, "TTSDriver", "set_pitch", reinterpret_cast(&gdn_tts_set_pitch), 1, gdn_tts_set_pitch_args, "Sets the speech synthesizer’s pitch"); 212 | register_nativescript_method(p_handle, "TTSDriver", "get_pitch", reinterpret_cast(&gdn_tts_get_pitch), 0, NULL, "Returns the speech synthesizer’s pitch"); 213 | godot_method_arg gdn_tts_set_rate_args[] = { 214 | { 215 | api->godot_string_chars_to_utf8("rate"), 216 | GODOT_VARIANT_TYPE_INT, 217 | GODOT_PROPERTY_HINT_NONE, 218 | api->godot_string_chars_to_utf8("") 219 | } 220 | }; 221 | register_nativescript_method(p_handle, "TTSDriver", "set_rate", reinterpret_cast(&gdn_tts_set_rate), 1, gdn_tts_set_rate_args, "Sets the speech synthesizer’s rendering rate adjustment."); 222 | register_nativescript_method(p_handle, "TTSDriver", "get_rate", reinterpret_cast(&gdn_tts_get_rate), 0, NULL, "Returns the speech synthesizer’s rendering rate adjustment."); 223 | }; 224 | 225 | TTSDriver::TTSDriver() { 226 | set_singleton(); 227 | }; 228 | 229 | TTSDriver::~TTSDriver() {}; 230 | --------------------------------------------------------------------------------