繫結外部函式庫
模組
以 C++ 自訂模組 中的 Summator 範例適合小型自訂模組,但如果你想使用較大型的外部函式庫呢?以下將以 Festival 為例示範。Festival 是一個以 C++ 編寫的語音合成(TTS,文字轉語音)函式庫。
要繫結外部函式庫,請像 Summator 範例一樣建立一個模組資料夾:
godot/modules/tts/
接著,請建立一個包含 TTS 類別的標頭檔:
#ifndef GODOT_TTS_H
#define GODOT_TTS_H
#include "core/object/ref_counted.h"
class TTS : public RefCounted {
GDCLASS(TTS, RefCounted);
protected:
static void _bind_methods();
public:
bool say_text(String p_txt);
TTS();
};
#endif // GODOT_TTS_H
然後再加入 cpp 檔案。
#include "tts.h"
#include <festival.h>
bool TTS::say_text(String p_txt) {
//convert Godot String to Godot CharString to C string
return festival_say_text(p_txt.ascii().get_data());
}
void TTS::_bind_methods() {
ClassDB::bind_method(D_METHOD("say_text", "txt"), &TTS::say_text);
}
TTS::TTS() {
festival_initialize(true, 210000); //not the best way to do it as this should only ever be called once.
}
一樣地,新的類別需要註冊,因此還需要再建立兩個檔案:
register_types.h
register_types.cpp
重要
這些檔案必須放在模組的最上層資料夾(與 SCsub 和 config.py 同一層),模組才能正確註冊。
這些檔案應包含以下內容:
void initialize_tts_module(ModuleInitializationLevel p_level);
void uninitialize_tts_module(ModuleInitializationLevel p_level);
/* yes, the word in the middle must be the same as the module folder name */
#include "register_types.h"
#include "core/object/class_db.h"
#include "tts.h"
void initialize_tts_module(ModuleInitializationLevel p_level) {
if (p_level != MODULE_INITIALIZATION_LEVEL_SCENE) {
return;
}
ClassDB::register_class<TTS>();
}
void uninitialize_tts_module(ModuleInitializationLevel p_level) {
// Nothing to do here in this example.
}
接下來,你需要建立一個 SCsub 檔案,讓建置系統能夠編譯此模組:
Import('env')
env_tts = env.Clone()
env_tts.add_source_files(env.modules_sources, "*.cpp") # Add all cpp files to the build
你需要在本機安裝外部函式庫以取得 .a 函式庫檔。請參考該函式庫的官方文件,瞭解如何在你的作業系統上安裝。我們已在下方提供 Linux 系統的安裝指令供參考。
sudo apt-get install festival festival-dev # Installs festival and speech_tools libraries
apt-cache search festvox-* # Displays list of voice packages
sudo apt-get install festvox-don festvox-rablpc16k festvox-kallpc16k festvox-kdlpc16k # Installs voices
重要
Festival 所使用的語音(以及其他外部或第三方資源)都有不同的授權條款與使用條件;即使 Festival 函式庫本身符合 MIT 授權,有些資源在 Godot 中仍可能產生授權問題,請務必詳閱各自的授權與使用條款。
外部函式庫也需要安裝在你的模組內,讓編譯器能夠存取其原始檔,並保持模組的程式碼自足(self-contained)。Festival 與 speech_tools 函式庫可在 modules/tts/ 資料夾下用下列 git 指令安裝:
git clone https://github.com/festvox/festival
git clone https://github.com/festvox/speech_tools
如果你不想將外部儲存庫的原始檔提交到你的儲存庫,可以在 modules/tts/ 資料夾中將它們加為 git 子模組來連結,如下所示:
git submodule add https://github.com/festvox/festival
git submodule add https://github.com/festvox/speech_tools
重要
請注意,Godot 官方儲存庫不使用 Git 子模組。如果你開發的模組預計要合併進 Godot 主儲存庫,請不要用子模組。若模組未被合併,可以考慮改用 GDExtension 來實作外部函式庫。
若要新增編譯器搜尋的 include 目錄,可將其附加至建置環境的路徑中:
# These paths are relative to /modules/tts/
env_tts.Append(CPPPATH=["speech_tools/include", "festival/src/include"])
# LIBPATH and LIBS need to be set on the real "env" (not the clone)
# to link the specified libraries to the Godot executable.
# This is an absolute path where your .a libraries reside.
# If using a relative path, you must convert it to a
# full path using an utility function, such as `Dir('...').abspath`.
env.Append(LIBPATH=[Dir('libpath').abspath])
# Check with the documentation of the external library to see which library
# files should be included/linked.
env.Append(LIBS=['Festival', 'estools', 'estbase', 'eststring'])
若要為模組新增自訂編譯器旗標,請先複製 env,以避免將旗標加入整個 Godot 建置(否則可能導致錯誤)。以下是含自訂旗標的 SCsub 範例:
Import('env')
env_tts = env.Clone()
env_tts.add_source_files(env.modules_sources, "*.cpp")
# Append CCFLAGS flags for both C and C++ code.
env_tts.Append(CCFLAGS=['-O2'])
# If you need to, you can:
# - Append CFLAGS for C code only.
# - Append CXXFLAGS for C++ code only.
最終的模組結構應如下:
godot/modules/tts/festival/
godot/modules/tts/libpath/libestbase.a
godot/modules/tts/libpath/libestools.a
godot/modules/tts/libpath/libeststring.a
godot/modules/tts/libpath/libFestival.a
godot/modules/tts/speech_tools/
godot/modules/tts/config.py
godot/modules/tts/tts.h
godot/modules/tts/tts.cpp
godot/modules/tts/register_types.h
godot/modules/tts/register_types.cpp
godot/modules/tts/SCsub
使用模組
你現在可以在任何腳本中使用你新建立的模組:
var t = TTS.new()
var script = "Hello world. This is a test!"
var is_spoken = t.say_text(script)
print('is_spoken: ', is_spoken)
若文字被朗讀時,輸出將為 is_spoken: True 。