繫結外部函式庫

模組

以 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/reference.h"

class TTS : public Reference {
    GDCLASS(TTS, Reference);

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 register_tts_types();
void unregister_tts_types();
/* yes, the word in the middle must be the same as the module folder name */
/* register_types.cpp */

#include "register_types.h"

#include "core/class_db.h"
#include "tts.h"

void register_tts_types() {
    ClassDB::register_class<TTS>();
}

void unregister_tts_types() {
    // 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

You'll need to install the external library on your machine to get the .a library files. See the library's official documentation for specific instructions on how to do this for your operation system. We've included the installation commands for Linux below, for reference.

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 來說可能會有點問題。請務必檢查授權條款與服務條款。

The external library will also need to be installed inside your module to make the source files accessible to the compiler, while also keeping the module code self-contained. The festival and speech_tools libraries can be installed from the modules/tts/ directory via git using the following commands:

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

重要

Please note that Git submodules are not used in the Godot repository. If you are developing a module to be merged into the main Godot repository, you should not use submodules. If your module doesn't get merged in, you can always try to implement the external library as a GDNative C++ plugin.

要新增讓編譯器搜尋的 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