Up to date

This page is up to date for Godot 4.2. If you still find outdated information, please open an issue.

繫結外部函式庫

模組

doc_custom_modules_in_c++ 中的 Summator 範例適合用於小型的自定模組,但如果想要使用大型的外部函式庫呢?來看看這個使用 Festival 的例子。Festival 是一個以 C++ 撰寫的語音合成 (TTS - 文字轉語音, Text-to-Speech) 函式庫。

要繫結到外部函式庫,我們先建立一個類似 Summator 範例的模組資料夾:

godot/modules/tts/

接著,需要建立含有一個簡易 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 檔案。

/* 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 檔的旁邊),這樣一來模組才能被正確註冊。

這幾個檔案的內容如下:

/* 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 */
/* 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 檔案,這樣一來建置系統才能編譯此模組:

# 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 所使用的語音檔 (以及其他外部與第三方的資源) 使用到了各種不同的授權條款與服務條款 (Terms of Use)。就算 Festival 函式庫本身是與 MIT 授權條款相容的,有些檔案對於 Godot 來說可能會有點問題。請務必檢查授權條款與服務條款。

還需要將外部函式庫安裝到模組內,以確保編譯器能存取原始檔並保持模組的程式碼是自封閉 (Self-Contained) 的。Festival 與 speech_tools 函式庫通過下列指令來用 Git 安裝進 modules/tts/ 資料夾:

git clone https://github.com/festvox/festival
git clone https://github.com/festvox/speech_tools

若不想將外部儲存庫的原始碼 Commit 進儲存庫中,可以通過將其 (在 modules/tts/資料夾內) 加為子模組 (Submodule) 來將這些函式庫連結過來,如下所示:

git submodule add https://github.com/festvox/festival
git submodule add https://github.com/festvox/speech_tools

重要

請注意,Godot 儲存庫中未使用到 Git 子模組。若你正在開發即將合併進 Godot 主儲存庫的模組,則不應使用子模組。若模組沒有被合併,則可以試著將其作為 GDNative C++ 外掛來實作。

要新增讓編譯器搜尋的 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 a path relative to /modules/tts/ where your .a libraries reside.
# If you are compiling the module externally (not in the godot source tree),
# these will need to be full paths.
env.Append(LIBPATH=['libpath'])

# 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 範例:

# 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