diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 00000000..1f283929 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,23 @@ +cmake_minimum_required(VERSION 3.8) + +project(espeak-ng + VERSION 1.52.0.1 + DESCRIPTION "open source speech synthesizer that supports more than hundred languages and accents" + HOMEPAGE_URL "https://github.com/espeak-ng/espeak-ng" +) + +include(cmake/deps.cmake) +include(cmake/config.cmake) +add_subdirectory(src) +include(cmake/data.cmake) + +option(BUILD_SHARED_LIBS "Build shared libraries" OFF) + +message(STATUS "Configuration:") +message(STATUS " shared: ${BUILD_SHARED_LIBS}") +message(STATUS " mbrola: ${USE_MBROLA} (${MBROLA_BIN})") +message(STATUS " libsonic: ${USE_LIBSONIC} (${SONIC_LIB} ${SONIC_INC})") +message(STATUS " libpcaudio: ${USE_LIBPCAUDIO} (${PCAUDIO_LIB} ${PCAUDIO_INC})") +message(STATUS " klatt: ${USE_KLATT}") +message(STATUS " speech-player: ${USE_SPEECHPLAYER}") +message(STATUS " async: ${USE_ASYNC}") diff --git a/cmake/config.cmake b/cmake/config.cmake new file mode 100644 index 00000000..11942fba --- /dev/null +++ b/cmake/config.cmake @@ -0,0 +1,10 @@ +include(CheckSymbolExists) +check_symbol_exists(mkstemp "stdlib.h" HAVE_MKSTEMP) + +option(USE_MBROLA "Use mbrola for speech synthesis" ${HAVE_MBROLA}) +option(USE_LIBSONIC "Use libsonit for faster speech rates" ${HAVE_LIBSONIC}) +option(USE_LIBPCAUDIO "Use libPcAudio for sound output" ${HAVE_LIBPCAUDIO}) + +option(USE_KLATT "Use klatt for speech synthesis" ON) +option(USE_SPEECHPLAYER "Use speech-player for speech synthesis" ON) +option(USE_ASYNC "Support asynchronous speech synthesis" ON) diff --git a/cmake/data.cmake b/cmake/data.cmake new file mode 100644 index 00000000..7c792768 --- /dev/null +++ b/cmake/data.cmake @@ -0,0 +1,168 @@ +list(APPEND _dict_compile_list + af am an ar as az + ba be bg bn bpy bs + ca chr cmn cs cv cy + da de + el en eo es et eu + fa fi fr + ga gd gn grc gu + hak haw he hi hr ht hu hy + ia id io is it + ja jbo + ka kk kl kn kok ko ku ky + la lb lfn lt lv + mi mk ml mr ms mto mt my + nci ne nl nog no + om or + pap pa piqd pl pt py + qdb quc qu qya + ro ru + sd shn si sjn sk sl smj sq sr sv sw + ta te th tk tn tr tt + ug uk ur uz + vi + yue +) + +list(APPEND _mbrola_lang_list + af1 ar1 ar2 + ca cmn cr1 cs + de2 de4 de6 de8 + ee1 en1 es es3 es4 + fr + gr1 gr2 grc-de6 + he hn1 hu1 + ic1 id1 in ir1 it1 it3 + jp + la1 lt + ma1 mx1 mx2 + nl nz1 + pl1 pt1 ptbr ptbr4 + ro1 + sv sv2 + tl1 tr1 + us us3 + vz +) + +set(DATA_DIST_ROOT ${CMAKE_CURRENT_BINARY_DIR}) +set(DATA_DIST_DIR ${DATA_DIST_ROOT}/espeak-ng-data) +set(PHONEME_TMP_DIR ${DATA_DIST_ROOT}/phsource) +set(DICT_TMP_DIR ${DATA_DIST_ROOT}/dictsource) + +set(DATA_SRC_DIR ${CMAKE_CURRENT_SOURCE_DIR}/espeak-ng-data) +set(PHONEME_SRC_DIR ${CMAKE_CURRENT_SOURCE_DIR}/phsource) +set(DICT_SRC_DIR ${CMAKE_CURRENT_SOURCE_DIR}/dictsource) + +file(MAKE_DIRECTORY "${DATA_DIST_DIR}") +file(MAKE_DIRECTORY "${DICT_TMP_DIR}") +file(COPY "${DATA_SRC_DIR}/lang" DESTINATION "${DATA_DIST_DIR}") +file(COPY "${DATA_SRC_DIR}/voices/!v" DESTINATION "${DATA_DIST_DIR}/voices") +file(COPY "${PHONEME_SRC_DIR}" DESTINATION "${DATA_DIST_ROOT}") + +set(ESPEAK_RUN_ENV ${CMAKE_COMMAND} -E env "ESPEAK_DATA_PATH=${DATA_DIST_ROOT}") +set(ESPEAK_RUN_CMD ${ESPEAK_RUN_ENV} $ENV{VALGRIND} "$") + +add_custom_command( + OUTPUT "${DATA_DIST_DIR}/intonations" + COMMAND ${ESPEAK_RUN_CMD} --compile-intonations + WORKING_DIRECTORY "${PHONEME_SRC_DIR}" + COMMENT "Compile intonations" + DEPENDS + "$" + "${PHONEME_SRC_DIR}/intonation" +) + +set(_phon_deps "") + +function(check_phon_deps _file) + set(_file "${PHONEME_SRC_DIR}/${_file}") + list(APPEND _phon_deps "${_file}") + + file(STRINGS "${_file}" _phon_incs REGEX "include .+") + list(TRANSFORM _phon_incs REPLACE "^[ \t]*include[ \t]+" "") + foreach(_inc ${_phon_incs}) + check_phon_deps(${_inc}) + endforeach(_inc) + set(_phon_deps ${_phon_deps} PARENT_SCOPE) +endfunction(check_phon_deps) + +check_phon_deps("phonemes") + +add_custom_command( + OUTPUT + "${DATA_DIST_DIR}/phondata" + "${DATA_DIST_DIR}/phondata-manifest" + "${DATA_DIST_DIR}/phonindex" + "${DATA_DIST_DIR}/phontab" + COMMAND ${ESPEAK_RUN_CMD} --compile-phonemes + WORKING_DIRECTORY "${PHONEME_SRC_DIR}" + COMMENT "Compile phonemes" + DEPENDS + "$" + ${_phon_deps} +) + +list(APPEND _dict_targets) +list(APPEND _mbr_targets) + +foreach(_dict_name ${_dict_compile_list}) + set(_dict_target "${DATA_DIST_DIR}/${_dict_name}_dict") + set(_dict_deps "") + list(APPEND _dict_targets ${_dict_target}) + list(APPEND _dict_deps + "${DICT_SRC_DIR}/${_dict_name}_rules" + "${DICT_SRC_DIR}/${_dict_name}_list" + ) + + if(EXISTS "${DICT_SRC_DIR}/extra/${_dict_name}_listx") + option(EXTRA_${_dict_name} "Compile extra ${_dict_name} dictionary" ON) + if(EXTRA_${_dict_name}) + list(APPEND _dict_deps "${DICT_SRC_DIR}/extra/${_dict_name}_listx") + else() + file(REMOVE "${DICT_TMP_DIR}/${_dict_name}_listx") + endif() + elseif(EXISTS "${DICT_SRC_DIR}/${_dict_name}_listx") + list(APPEND _dict_deps "${DICT_SRC_DIR}/${_dict_name}_listx") + endif() + if(EXISTS "${DICT_SRC_DIR}/${_dict_name}_emoji") + list(APPEND _dict_deps "${DICT_SRC_DIR}/${_dict_name}_emoji") + endif() + + add_custom_command( + OUTPUT "${_dict_target}" + COMMAND ${CMAKE_COMMAND} -E copy ${_dict_deps} "${DICT_TMP_DIR}/" + COMMAND ${ESPEAK_RUN_CMD} --compile=${_dict_name} + WORKING_DIRECTORY "${DICT_TMP_DIR}" + DEPENDS + "$" + "${DATA_DIST_DIR}/phondata" + "${DATA_DIST_DIR}/intonations" + ${_dict_deps} + ) +endforeach() + +if (HAVE_MBROLA AND USE_MBROLA) + file(COPY "${DATA_SRC_DIR}/voices/mb" DESTINATION "${DATA_DIST_DIR}/voices") + file(MAKE_DIRECTORY "${DATA_DIST_DIR}/mbrola_ph") + foreach(_mbl ${_mbrola_lang_list}) + set(_mbl_src "${PHONEME_SRC_DIR}/mbrola/${_mbl}") + set(_mbl_out "${DATA_DIST_DIR}/mbrola_ph/${_mbl}_phtrans") + list(APPEND _mbr_targets ${_mbl_out}) + add_custom_command( + OUTPUT "${_mbl_out}" + COMMAND ${ESPEAK_RUN_CMD} --compile-mbrola="${_mbl_src}" + DEPENDS "$" "${_mbl_src}" + ) + endforeach(_mbl) +endif() + +add_custom_target( + data ALL + DEPENDS + "${DATA_DIST_DIR}/intonations" + "${DATA_DIST_DIR}/phondata" + ${_dict_targets} + ${_mbr_targets} +) +install(DIRECTORY ${DATA_DIST_DIR} DESTINATION share) diff --git a/cmake/deps.cmake b/cmake/deps.cmake new file mode 100644 index 00000000..b63ced21 --- /dev/null +++ b/cmake/deps.cmake @@ -0,0 +1,33 @@ +find_library(SONIC_LIB sonic) +find_path(SONIC_INC "sonic.h") +find_library(PCAUDIO_LIB pcaudio) +find_path(PCAUDIO_INC "pcaudiolib/audio.h") +find_library(PTHREAD_LIB pthread) +find_program(MBROLA_BIN mbrola) + +include(FetchContent) + +if (PTHREAD_LIB) + set(HAVE_PTHREAD ON) +endif(PTHREAD_LIB) +if (MBROLA_BIN) + set(HAVE_MBROLA ON) +endif(MBROLA_BIN) +if (SONIC_LIB AND SONIC_INC) + set(HAVE_LIBSONIC ON) +else() + FetchContent_Declare(sonic-git + GIT_REPOSITORY https://github.com/waywardgeek/sonic.git + GIT_TAG fbf75c3d6d846bad3bb3d456cbc5d07d9fd8c104 + ) + FetchContent_MakeAvailable(sonic-git) + FetchContent_GetProperties(sonic-git) + add_library(sonic OBJECT ${sonic-git_SOURCE_DIR}/sonic.c) + target_include_directories(sonic PUBLIC ${sonic-git_SOURCE_DIR}) + set(HAVE_LIBSONIC ON) + set(SONIC_LIB sonic) + set(SONIC_INC ${sonic-git_SOURCE_DIR}) +endif() +if (PCAUDIO_LIB AND PCAUDIO_INC) + set(HAVE_LIBPCAUDIO ON) +endif() diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt new file mode 100644 index 00000000..d1288955 --- /dev/null +++ b/src/CMakeLists.txt @@ -0,0 +1,20 @@ +add_library(espeak-include INTERFACE) +target_include_directories(espeak-include INTERFACE include include/compat) + +add_subdirectory(ucd-tools) +add_subdirectory(speechPlayer) +add_subdirectory(libespeak-ng) + +add_executable(espeak-ng-bin espeak-ng.c) +set_target_properties(espeak-ng-bin PROPERTIES OUTPUT_NAME espeak-ng) +target_link_libraries( + espeak-ng-bin PRIVATE espeak-ng espeak-ng-config +) +add_custom_target( + speak-ng ALL + ${CMAKE_COMMAND} -E create_symlink espeak-ng ${CMAKE_CURRENT_BINARY_DIR}/speak-ng + COMMENT "Link espeak-ng to speak-ng" + DEPENDS espeak-ng-bin +) +install(TARGETS espeak-ng-bin) +install(DIRECTORY include/espeak include/espeak-ng TYPE INCLUDE) diff --git a/src/libespeak-ng/CMakeLists.txt b/src/libespeak-ng/CMakeLists.txt new file mode 100644 index 00000000..c23eda4d --- /dev/null +++ b/src/libespeak-ng/CMakeLists.txt @@ -0,0 +1,97 @@ +set(ESPEAK_CONFIG_DIR ${CMAKE_CURRENT_BINARY_DIR}/include) +set(ESPEAK_CONFIG_H ${ESPEAK_CONFIG_DIR}/config.h) +configure_file(config.h.in ${ESPEAK_CONFIG_H}) + +add_library(espeak-ng-config INTERFACE) +target_include_directories(espeak-ng-config INTERFACE ${ESPEAK_CONFIG_DIR}) + +add_library(espeak-ng + common.c + mnemonics.c + error.c + ieee80.c + + compiledata.c + compiledict.c + + dictionary.c + encoding.c + intonation.c + langopts.c + numbers.c + phoneme.c + phonemelist.c + readclause.c + setlengths.c + soundicon.c + spect.c + ssml.c + synthdata.c + synthesize.c + tr_languages.c + translate.c + translateword.c + voices.c + wavegen.c + speech.c + + espeak_api.c +) + +target_compile_options(espeak-ng PRIVATE + "-fPIC" + "-fvisibility=hidden" + "-fno-exceptions" + "-fwrapv" + + "-pedantic" + + "-Wunused-parameter" + "-Wunused" + "-Wuninitialized" + "-Wreturn-type" + "-Wmissing-prototypes" + "-Wint-conversion" + "-Wimplicit" +) +target_compile_definitions(espeak-ng PRIVATE "LIBESPEAK_NG_EXPORT") + +if (USE_ASYNC) + target_sources(espeak-ng PRIVATE + event.c + fifo.c + espeak_command.c + ) +endif(USE_ASYNC) + +if (USE_MBROLA) + target_sources(espeak-ng PRIVATE + mbrowrap.c + compilembrola.c + synth_mbrola.c + ) +endif(USE_MBROLA) + +if (USE_KLATT) + target_sources(espeak-ng PRIVATE klatt.c) +endif(USE_KLATT) + +if (USE_SPEECHPLAYER) + target_sources(espeak-ng PRIVATE sPlayer.c) + target_link_libraries(espeak-ng PRIVATE speechPlayer) +endif(USE_SPEECHPLAYER) + +if (HAVE_LIBSONIC AND USE_LIBSONIC) + target_link_libraries(espeak-ng PRIVATE ${SONIC_LIB}) + target_include_directories(espeak-ng PRIVATE ${SONIC_INC}) +endif() + +if (HAVE_LIBPCAUDIO AND USE_LIBPCAUDIO) + target_link_libraries(espeak-ng PRIVATE ${PCAUDIO_LIB}) + target_include_directories(espeak-ng PRIVATE ${PCAUDIO_INC}) +endif() + +target_link_libraries(espeak-ng PRIVATE espeak-ng-config ucd m) +target_link_libraries(espeak-ng PUBLIC espeak-include) + +install(TARGETS espeak-ng LIBRARY) diff --git a/src/libespeak-ng/config.h.in b/src/libespeak-ng/config.h.in new file mode 100644 index 00000000..9933b671 --- /dev/null +++ b/src/libespeak-ng/config.h.in @@ -0,0 +1,10 @@ +#cmakedefine01 HAVE_MKSTEMP + +#cmakedefine01 USE_ASYNC +#cmakedefine01 USE_KLATT +#cmakedefine01 USE_LIBPCAUDIO +#cmakedefine01 USE_LIBSONIC +#cmakedefine01 USE_MBROLA +#cmakedefine01 USE_SPEECHPLAYER + +#define PACKAGE_VERSION "${PROJECT_VERSION}" diff --git a/src/speechPlayer/CMakeLists.txt b/src/speechPlayer/CMakeLists.txt new file mode 100644 index 00000000..c80cc519 --- /dev/null +++ b/src/speechPlayer/CMakeLists.txt @@ -0,0 +1,8 @@ +add_library(speechPlayer STATIC + src/frame.cpp + src/speechPlayer.cpp + src/speechWaveGenerator.cpp +) +target_include_directories(speechPlayer PUBLIC include) +target_compile_options(speechPlayer PRIVATE "-fPIC") +install(TARGETS speechPlayer ARCHIVE) diff --git a/src/ucd-tools/CMakeLists.txt b/src/ucd-tools/CMakeLists.txt new file mode 100644 index 00000000..6ff61022 --- /dev/null +++ b/src/ucd-tools/CMakeLists.txt @@ -0,0 +1,10 @@ +add_library(ucd STATIC + src/case.c + src/categories.c + src/ctype.c + src/proplist.c + src/scripts.c + src/tostring.c +) +target_include_directories(ucd PUBLIC src/include) +install(TARGETS ucd ARCHIVE)