diff --git a/CMakeLists.txt b/CMakeLists.txt index f96ec440..9c9b3c04 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -19,6 +19,7 @@ endif() add_executable(piper src/cpp/main.cpp src/cpp/piper.cpp) add_executable(test_piper src/cpp/test.cpp src/cpp/piper.cpp) +add_library(piperlib SHARED src/cpp/piperlib.cpp src/cpp/piper.cpp) # NOTE: external project prefix are shortened because of path length restrictions on Windows # NOTE: onnxruntime is pulled from piper-phonemize @@ -39,6 +40,7 @@ if(NOT DEFINED FMT_DIR) ) add_dependencies(piper fmt_external) add_dependencies(test_piper fmt_external) + add_dependencies(piperlib fmt_external) endif() # ---- spdlog --- @@ -54,6 +56,7 @@ if(NOT DEFINED SPDLOG_DIR) ) add_dependencies(piper spdlog_external) add_dependencies(test_piper spdlog_external) + add_dependencies(piperlib spdlog_external) endif() # ---- piper-phonemize --- @@ -68,6 +71,7 @@ if(NOT DEFINED PIPER_PHONEMIZE_DIR) ) add_dependencies(piper piper_phonemize_external) add_dependencies(test_piper piper_phonemize_external) + add_dependencies(piperlib piper_phonemize_external) endif() # ---- Declare executable ---- @@ -81,7 +85,7 @@ if((NOT MSVC) AND (NOT APPLE)) set(PIPER_EXTRA_LIBRARIES "pthread") endif() -target_link_libraries(piper +target_link_libraries(piperlib fmt spdlog espeak-ng @@ -90,13 +94,13 @@ target_link_libraries(piper ${PIPER_EXTRA_LIBRARIES} ) -target_link_directories(piper PUBLIC +target_link_directories(piperlib PUBLIC ${FMT_DIR}/lib ${SPDLOG_DIR}/lib ${PIPER_PHONEMIZE_DIR}/lib ) -target_include_directories(piper PUBLIC +target_include_directories(piperlib PUBLIC ${FMT_DIR}/include ${SPDLOG_DIR}/include ${PIPER_PHONEMIZE_DIR}/include @@ -137,7 +141,10 @@ target_link_libraries(test_piper PUBLIC ) # ---- Declare install targets ---- - +install( + TARGETS piperlib + LIBRARY DESTINATION ${CMAKE_INSTALL_PREFIX} +) install( TARGETS piper DESTINATION ${CMAKE_INSTALL_PREFIX}) diff --git a/src/cpp/piperlib.cpp b/src/cpp/piperlib.cpp new file mode 100644 index 00000000..9675723d --- /dev/null +++ b/src/cpp/piperlib.cpp @@ -0,0 +1,122 @@ +#include "piper.hpp" +#include "piperlib.hpp" +#include + +extern "C" +{ + eSpeakConfig* create_eSpeakConfig() { + return new eSpeakConfig(); + } + + void destroy_eSpeakConfig(eSpeakConfig* config) { + delete config; + } + + PiperConfig* create_PiperConfig(char* eSpeakDataPath) { + auto config = new PiperConfig(); + config->eSpeakDataPath = eSpeakDataPath; + return config; + } + + void destroy_PiperConfig(PiperConfig* config) { + delete config; + } + + PhonemizeConfig* create_PhonemizeConfig() { + return new PhonemizeConfig(); + } + + void destroy_PhonemizeConfig(PhonemizeConfig* config) { + delete config; + } + + SynthesisConfig* create_SynthesisConfig() { + return new SynthesisConfig(); + } + + void destroy_SynthesisConfig(SynthesisConfig* config) { + delete config; + } + + ModelConfig* create_ModelConfig() { + return new ModelConfig(); + } + + void destroy_ModelConfig(ModelConfig* config) { + delete config; + } + + ModelSession* create_ModelSession() { + return new ModelSession(); + } + + void destroy_ModelSession(ModelSession* config) { + delete config; + } + + SynthesisResult* create_SynthesisResult() { + return new SynthesisResult(); + } + + void destroy_SynthesisResult(SynthesisResult* config) { + delete config; + } + + Voice* create_Voice() { + return new Voice(); + } + + void destroy_Voice(Voice* voice) { + delete voice; + } + + bool isSingleCodepoint(const char* s) { + std::string str(s); + return piper::isSingleCodepoint(str); + } + + char32_t getCodepoint(const char* s) { + return piper::getCodepoint(s); + } + + char* getVersion() { + auto version = piper::getVersion(); + char* cstr = new char[version.size() + 1]; + std::strcpy(cstr, version.c_str()); + return cstr; + } + + void initializePiper(PiperConfig* config) { + piper::initialize(*config); + } + + void terminatePiper(PiperConfig* config) { + piper::terminate(*config); + } + + SynthesisConfig getSynthesisConfig(Voice* voice) { + return voice->synthesisConfig; + } + + void loadVoice(PiperConfig* config, const char* modelPath, const char* modelConfigPath, Voice* voice, int64_t* speakerId) { + std::optional optSpeakerId; + if (speakerId) { + optSpeakerId = *speakerId; + } + piper::loadVoice(*config, modelPath, modelConfigPath, *voice, optSpeakerId); + } + + void textToAudio(PiperConfig* config, Voice* voice, const char* text, SynthesisResult* result, AudioCallback audioCallback) { + std::vector audioBuf; + piper::textToAudio(*config, *voice, text, audioBuf, *result, [&audioBuf, audioCallback] { audioCallback(audioBuf.data(), audioBuf.size()); }); + } + + void textToWavFile(PiperConfig* config, Voice* voice, const char* text, const char* audioFile, SynthesisResult* result) { + std::string audioFilePath = audioFile; + std::ofstream audioFileStream(audioFilePath, std::ios::binary); + piper::textToWavFile(*config, *voice, text, audioFileStream, *result); + audioFileStream << "opened"; + audioFileStream.flush(); + audioFileStream.close(); + } +} \ No newline at end of file diff --git a/src/cpp/piperlib.hpp b/src/cpp/piperlib.hpp new file mode 100644 index 00000000..fe8215a7 --- /dev/null +++ b/src/cpp/piperlib.hpp @@ -0,0 +1,42 @@ +#include +#include +#include "piper.hpp" + +using namespace piper; + +#if defined(_WIN32) && !defined(__MINGW32__) +# define PIPER_API __declspec(dllexport) +#else +# define PIPER_API __attribute__ ((visibility ("default"))) +#endif + +extern "C" { + typedef void (*AudioCallback)(int16_t* audioBuffer, int length); + + PIPER_API eSpeakConfig* create_eSpeakConfig(); + PIPER_API void destroy_eSpeakConfig(eSpeakConfig* config); + PIPER_API PiperConfig* create_PiperConfig(char* eSpeakDataPath); + PIPER_API void destroy_PiperConfig(PiperConfig* config); + PIPER_API PhonemizeConfig* create_PhonemizeConfig(); + PIPER_API void destroy_PhonemizeConfig(PhonemizeConfig* config); + PIPER_API SynthesisConfig* create_SynthesisConfig(); + PIPER_API void destroy_SynthesisConfig(SynthesisConfig* config); + PIPER_API ModelConfig* create_ModelConfig(); + PIPER_API void destroy_ModelConfig(ModelConfig* config); + PIPER_API ModelSession* create_ModelSession(); + PIPER_API void destroy_ModelSession(ModelSession* config); + PIPER_API SynthesisResult* create_SynthesisResult(); + PIPER_API void destroy_SynthesisResult(SynthesisResult* config); + PIPER_API Voice* create_Voice(); + PIPER_API void destroy_Voice(Voice* voice); + + PIPER_API bool isSingleCodepoint(const char* s); + PIPER_API char32_t getCodepoint(const char* s); + PIPER_API char* getVersion(); + PIPER_API void initializePiper(PiperConfig* config); + PIPER_API void terminatePiper(PiperConfig* config); + PIPER_API SynthesisConfig getSynthesisConfig(Voice* voice); + PIPER_API void loadVoice(PiperConfig* config, const char* modelPath, const char* modelConfigPath, Voice* voice, SpeakerId* speakerId); + PIPER_API void textToAudio(PiperConfig* config, Voice* voice, const char* text, SynthesisResult* result, AudioCallback audioCallback); + PIPER_API void textToWavFile(PiperConfig* config, Voice* voice, const char* text, const char* audioFile, SynthesisResult* result); +} \ No newline at end of file