Vinculación a bibliotecas externas

Módulos

El ejemplo del Summator en Módulos personalizados en C++ es genial para módulos pequeños y personalizados, pero ¿qué pasa si deseas utilizar una biblioteca externa más grande? Veamos un ejemplo utilizando Festival <http://www.cstr.ed.ac.uk/projects/festival/>, una biblioteca de síntesis de voz (texto a voz) escrita en C++.

Para vincular una biblioteca externa, configura un directorio de módulos similar al ejemplo del Summator:

godot/modules/tts/

A continuación, crearás un archivo de encabezado con una clase TTS simple:

/* 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

Y luego agregarás el archivo 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.
}

Al igual que antes, la nueva clase necesita ser registrada de alguna manera, por lo que se deben crear dos archivos más:

register_types.h
register_types.cpp

Importante

Los archivos anteriores deben estar en la carpeta de nivel superior de tu módulo (junto a tus archivos SCsub y config.py) para que el módulo se registre correctamente.

Estos archivos deberán contener lo siguiente:

/* 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.
}

A continuación, debes crear un archivo SCsub para que el sistema de compilación compile este módulo:

# SCsub

Import('env')

env_tts = env.Clone()
env_tts.add_source_files(env.modules_sources, "*.cpp") # Add all cpp files to the build

Necesitarás instalar la biblioteca externa en tu máquina para obtener los archivos de biblioteca .a. Consulta la documentación oficial de la biblioteca para obtener instrucciones específicas sobre cómo hacerlo para tu sistema operativo. Hemos incluido los comandos de instalación para Linux a continuación, como referencia.

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

Importante

Las voces que utiliza Festival (y cualquier otro recurso externo o de terceros potencial) tienen diferentes licencias y términos de uso; algunos (si no la mayoría) de ellos pueden ser problemáticos con Godot, incluso si la propia biblioteca de Festival es compatible con la Licencia MIT. Asegúrate de verificar las licencias y los términos de uso.

La biblioteca externa también deberá ser instalada dentro de tu módulo para que los archivos fuente sean accesibles al compilador, al mismo tiempo que mantienes el código del módulo autocontenido. Las bibliotecas festival y speech_tools se pueden instalar desde el directorio modules/tts/ a través de git utilizando los siguientes comandos:

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

Si no deseas que los archivos fuente del repositorio externo se incluyan en tu propio repositorio, puedes enlazarlos agregándolos como submódulos (desde el directorio modules/tts/), como se muestra a continuación:

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

Importante

Ten en cuenta que los submódulos de Git no se utilizan en el repositorio de Godot. Si estás desarrollando un módulo para ser fusionado en el repositorio principal de Godot, no debes usar submódulos. Si tu módulo no se fusiona, siempre puedes intentar implementar la biblioteca externa como un complemento GDNative en C++.

Puedes agregar directorios de inclusión a las rutas de entorno para que sean vistos por el compilador:

# 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'])

Si deseas agregar banderas personalizadas del compilador al construir tu módulo, primero debes clonar env, para evitar que agregue esas banderas a toda la compilación de Godot (lo que puede causar errores). A continuación, te muestro un ejemplo de SCsub con banderas personalizadas:

# 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.

El módulo debería verse así:

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

Usar el módulo

Ahora puede usar su módulo recién creado desde cualquier script:

var t = TTS.new()
var script = "Hello world. This is a test!"
var is_spoken = t.say_text(script)
print('is_spoken: ', is_spoken)

Y la salida será is_spoken: True si el texto es hablado.