繫結外部函式庫

模組

以 C++ 自訂模組 中的 Summator 範例適合小型自訂模組,但如果你想使用較大型的外部函式庫呢?以下將以 Festival 為例示範。Festival 是一個以 C++ 編寫的語音合成(TTS,文字轉語音)函式庫。

要繫結外部函式庫,請像 Summator 範例一樣建立一個模組資料夾:

godot/modules/tts/

接著,請建立一個包含 TTS 類別的標頭檔:

godot/modules/tts/tts.h
#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 檔案。

godot/modules/tts/tts.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

重要

這些檔案必須放在模組的最上層資料夾(與 SCsubconfig.py 同一層),模組才能正確註冊。

這些檔案應包含以下內容:

godot/modules/tts/register_types.h
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 */
godot/modules/tts/register_types.cpp
#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 檔案,讓建置系統能夠編譯此模組:

godot/modules/tts/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 目錄,可將其附加至建置環境的路徑中:

godot/modules/tts/SCsub
# 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 範例:

godot/modules/tts/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