Fixes: #1547 - Implement building and testing with cmake - Download libsonic from repository (when enabled and not available as system library), ref: #650 - Migrate android native build to cmake - Migrate CI to cmake - Add windows CI TODO (might be done in separate PR): - Building as [cmake external project](https://cmake.org/cmake/help/latest/module/ExternalProject.html) - Building for package managers (deb, rpm, ...) - Strip out `vcproj` - _to be discussed_ - Strip out `autotools` after next release - Update build documentation for `cmake`master
config: [debug, release] | config: [debug, release] | ||||
include: | include: | ||||
- config: debug | - config: debug | ||||
target: assembleDebug | |||||
apkname: espeak-debug.apk | apkname: espeak-debug.apk | ||||
- config: release | - config: release | ||||
target: assembleRelease | |||||
apkname: espeak-release-unsigned.apk | apkname: espeak-release-unsigned.apk | ||||
name: Build ${{ matrix.config }} APK | name: Build ${{ matrix.config }} APK | ||||
steps: | steps: | ||||
- uses: actions/checkout@v3 | - uses: actions/checkout@v3 | ||||
- name: autoconf | |||||
run: ./autogen.sh ; chmod -x INSTALL m4/*.m4 | |||||
- name: configure | |||||
run: ./configure --with-gradle=$(pwd)/android/gradlew | |||||
- name: make | - name: make | ||||
run: make apk-${{ matrix.config }} | |||||
run: cd android ; ./gradlew ${{ matrix.target }} | |||||
- name: upload | - name: upload | ||||
uses: actions/upload-artifact@v3 | uses: actions/upload-artifact@v3 | ||||
with: | with: |
- sanitizer: "memory" | - sanitizer: "memory" | ||||
configenv: "CC=clang CXX=clang++" | configenv: "CC=clang CXX=clang++" | ||||
sanitizer_cflags: "-fsanitize=memory -fsanitize-memory-track-origins=2" | sanitizer_cflags: "-fsanitize=memory -fsanitize-memory-track-origins=2" | ||||
config: "--without-pcaudiolib --without-sonic" | |||||
config: "-DUSE_LIBPCAUDIO:BOOL=OFF -DUSE_LIBSONIC:BOOL=OFF" | |||||
sanitize_env: "MSAN_OPTIONS=exitcode=42" | sanitize_env: "MSAN_OPTIONS=exitcode=42" | ||||
- sanitizer: "thread" | - sanitizer: "thread" | ||||
sanitize_env: "UBSAN_OPTIONS=halt_on_error=1" | sanitize_env: "UBSAN_OPTIONS=halt_on_error=1" | ||||
- sanitizer: "valgrind" | - sanitizer: "valgrind" | ||||
sanitize_env: 'VALGRIND="libtool --mode=execute valgrind --track-origins=yes --leak-check=full --error-exitcode=1"' | |||||
sanitize_env: 'VALGRIND="valgrind --track-origins=yes --leak-check=full --error-exitcode=1"' | |||||
exclude: | exclude: | ||||
- os: macos | - os: macos | ||||
sudo apt-get install ronn kramdown python3 | sudo apt-get install ronn kramdown python3 | ||||
- name: apt-arch-deps | - name: apt-arch-deps | ||||
if: matrix.os == 'ubuntu' && matrix.cross == 'no' | if: matrix.os == 'ubuntu' && matrix.cross == 'no' | ||||
run: "sudo apt-get install libtool-bin valgrind g++-12-multilib linux-libc-dev:${{ matrix.arch }} libpcaudio-dev:${{ matrix.arch }} libsonic-dev:${{ matrix.arch }} libstdc++-12-dev:${{ matrix.arch }} libc6-dbg:${{ matrix.arch }}" | |||||
run: "sudo apt-get install cmake valgrind g++-12-multilib linux-libc-dev:${{ matrix.arch }} libpcaudio-dev:${{ matrix.arch }} libsonic-dev:${{ matrix.arch }} libstdc++-12-dev:${{ matrix.arch }} libc6-dbg:${{ matrix.arch }}" | |||||
- name: apt-cross-deps | - name: apt-cross-deps | ||||
if: matrix.cross == 'yes' | if: matrix.cross == 'yes' | ||||
run: sudo apt-get install qemu-user g++-12-${{ matrix.arch }}-${{ matrix.sys }} | run: sudo apt-get install qemu-user g++-12-${{ matrix.arch }}-${{ matrix.sys }} | ||||
# MacOS - dependencies | # MacOS - dependencies | ||||
- name: brew-deps | - name: brew-deps | ||||
if: matrix.os == 'macos' | if: matrix.os == 'macos' | ||||
run: brew install libtool automake ronn OJFord/homebrew-formulae/kramdown && | |||||
brew install --HEAD anarchivist/espeak-ng/waywardgeek-sonic | |||||
run: brew install cmake ronn OJFord/homebrew-formulae/kramdown | |||||
- name: brew-compile-deps | - name: brew-compile-deps | ||||
if: matrix.os == 'macos' && matrix.compiler == 'gcc' | if: matrix.os == 'macos' && matrix.compiler == 'gcc' | ||||
run: brew install gcc@12 | run: brew install gcc@12 | ||||
- uses: actions/checkout@v3 | - uses: actions/checkout@v3 | ||||
# Configure | # Configure | ||||
- name: autoconf | |||||
run: | | |||||
./autogen.sh | |||||
chmod -x INSTALL m4/*.m4 | |||||
- name: configure | - name: configure | ||||
run: | | run: | | ||||
[ 'x${{ matrix.compiler }}' = 'xgcc' ] && export CC="${{ matrix.ccarch }}-${{ matrix.sys }}-gcc-12" | [ 'x${{ matrix.compiler }}' = 'xgcc' ] && export CC="${{ matrix.ccarch }}-${{ matrix.sys }}-gcc-12" | ||||
[ 'x${{ matrix.cross }}' = 'xyes' ] && export LD="${{ matrix.ccarch }}-${{ matrix.sys }}-ld" | [ 'x${{ matrix.cross }}' = 'xyes' ] && export LD="${{ matrix.ccarch }}-${{ matrix.sys }}-ld" | ||||
export CFLAGS="-g -Og -fno-omit-frame-pointer ${{ matrix.compiler_flags }} ${{ matrix.sanitizer_cflags }} ${{ matrix.archcflags }}" | export CFLAGS="-g -Og -fno-omit-frame-pointer ${{ matrix.compiler_flags }} ${{ matrix.sanitizer_cflags }} ${{ matrix.archcflags }}" | ||||
export CXXFLAGS="-g -Og -fno-omit-frame-pointer ${{ matrix.compiler_flags }} ${{ matrix.sanitizer_cflags }} ${{ matrix.archcflags }}" | export CXXFLAGS="-g -Og -fno-omit-frame-pointer ${{ matrix.compiler_flags }} ${{ matrix.sanitizer_cflags }} ${{ matrix.archcflags }}" | ||||
./configure --host=${{ matrix.arch }}-${{ matrix.sys }} ${{ matrix.config }} | |||||
cmake -Bbuild ${{ matrix.config }} -DCMAKE_SYSTEM_PROCESSOR=${{ matrix.arch }} -DUSE_ASYNC:BOOL=OFF | |||||
- name: config-failed-upload | - name: config-failed-upload | ||||
if: ${{ failure() }} | if: ${{ failure() }} | ||||
uses: actions/upload-artifact@v3 | uses: actions/upload-artifact@v3 | ||||
with: | with: | ||||
name: config-${{ matrix.os }}-${{ matrix.arch }}-${{ matrix.compiler }}-${{ matrix.sanitizer }}.log | |||||
path: config.log | |||||
name: cmake-${{ matrix.os }}-${{ matrix.arch }}-${{ matrix.compiler }}-${{ matrix.sanitizer }}-builddir | |||||
path: build | |||||
# Build and test | # Build and test | ||||
- name: make-binary | - name: make-binary | ||||
run: make src/speak-ng src/espeak-ng | |||||
run: cmake --build build --target espeak-ng | |||||
- name: make-data | - name: make-data | ||||
run: ${{ matrix.sanitize_env }} ${{ matrix.runenv }} make all-am | |||||
run: ${{ matrix.sanitize_env }} ${{ matrix.runenv }} cmake --build build --target data | |||||
- name: test | - name: test | ||||
run: ${{ matrix.sanitize_env }} ${{ matrix.runenv }} make check | |||||
run: | | |||||
${{ matrix.sanitize_env }} ${{ matrix.runenv }} cmake --build build --target tests | |||||
${{ matrix.sanitize_env }} ${{ matrix.runenv }} ctest --test-dir build -Ttest -j1 --output-on-failure |
branches: [ master ] | branches: [ master ] | ||||
env: | env: | ||||
deps: libpcaudio-dev libsonic-dev ronn kramdown python3 | |||||
deps: cmake libpcaudio-dev libsonic-dev ronn kramdown python3 | |||||
jobs: | jobs: | ||||
if: matrix.arch == 'x86-32' | if: matrix.arch == 'x86-32' | ||||
- name: dependencies | - name: dependencies | ||||
run: sudo apt-get update && sudo apt-get install ${{ env.deps }} reprotest ${{ matrix.archdeps }} | run: sudo apt-get update && sudo apt-get install ${{ env.deps }} reprotest ${{ matrix.archdeps }} | ||||
- name: autoconf | |||||
run: ./autogen.sh ; chmod -x INSTALL m4/*.m4 | |||||
- name: run | - name: run | ||||
run: reprotest 'CFLAGS="${{ matrix.archconfigflags }}" CXXFLAGS="${{ matrix.archconfigflags }}" ./configure --without-gradle && make clean && make && make check && touch success' success | |||||
run: reprotest 'CFLAGS="${{ matrix.archconfigflags }}" CXXFLAGS="${{ matrix.archconfigflags }}" rm -rf build && cmake -Bbuild -DUSE_ASYNC:BOOL=OFF && cmake --build build && ctest --test-dir build -Ttest -j1 --output-on-failure && touch success' success | |||||
distcheck: | distcheck: | ||||
- uses: actions/checkout@v3 | - uses: actions/checkout@v3 | ||||
- name: dependencies | - name: dependencies | ||||
run: sudo apt-get update && sudo apt-get install ${{ env.deps }} | run: sudo apt-get update && sudo apt-get install ${{ env.deps }} | ||||
- name: autoconf | |||||
run: ./autogen.sh ; chmod -x INSTALL m4/*.m4 | |||||
- name: configure | - name: configure | ||||
run: ./configure | |||||
run: cmake -Bbuild -DUSE_ASYNC:BOOL=OFF | |||||
- name: make | - name: make | ||||
run: make | |||||
run: cmake --build build | |||||
- name: make check | - name: make check | ||||
run: make check | |||||
- name: make dist | |||||
run: make dist | |||||
run: ctest --test-dir build -Ttest -j1 --output-on-failure | |||||
- name: make package_source | |||||
run: cmake --build build --target package_source | |||||
- name: unpack | - name: unpack | ||||
run: mkdir unpack && tar -C unpack -xf *.tar.gz | |||||
run: mkdir unpack && tar -C unpack -xf build/espeak-ng-*-Source.tar.bz2 | |||||
- name: re-configure | - name: re-configure | ||||
run: cd unpack/espeak-ng-* && ./configure | |||||
run: cd unpack/espeak-ng-* && cmake -Bbuild -DUSE_ASYNC:BOOL=OFF | |||||
- name: re-make | - name: re-make | ||||
run: cd unpack/espeak-ng-* && make | |||||
run: cd unpack/espeak-ng-* && cmake --build build | |||||
- name: re-make check | - name: re-make check | ||||
run: cd unpack/espeak-ng-* && make check | |||||
run: cd unpack/espeak-ng-* && ctest --test-dir build -Ttest -j1 --output-on-failure |
name: Windows | |||||
on: | |||||
workflow_dispatch: | |||||
push: | |||||
branches: [ master ] | |||||
pull_request: | |||||
branches: [ master ] | |||||
jobs: | |||||
build: | |||||
runs-on: windows-latest | |||||
name: build | |||||
steps: | |||||
- uses: actions/checkout@v3 | |||||
- name: configure | |||||
run: cmake -Bbuild -DUSE_ASYNC:BOOL=OFF | |||||
- name: make | |||||
run: cmake --build build | |||||
- name: make check | |||||
run: ctest --test-dir build -Ttest -C Debug -j1 --output-on-failure |
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(CTest) | |||||
include(cmake/deps.cmake) | |||||
include(cmake/config.cmake) | |||||
add_subdirectory(src) | |||||
include(cmake/data.cmake) | |||||
include(cmake/package.cmake) | |||||
include(CPack) | |||||
include(CTest) | |||||
add_subdirectory(tests) | |||||
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}") |
androidTestImplementation 'org.hamcrest:hamcrest-all:1.3' | androidTestImplementation 'org.hamcrest:hamcrest-all:1.3' | ||||
} | } | ||||
android { | |||||
compileSdk 33 | |||||
ndkVersion "25.1.8937393" | |||||
namespace "com.reecedunn.espeak" | |||||
defaultConfig { | |||||
minSdk 19 | |||||
targetSdk 33 | |||||
versionCode 22 | |||||
versionName "1.52-dev" | |||||
externalNativeBuild { | |||||
cmake { | |||||
arguments "-DUSE_ASYNC:BOOL=OFF", "-DUSE_MBROLA:BOOL=OFF" | |||||
targets "ttsespeak", "espeak-data" | |||||
} | |||||
} | |||||
} | |||||
externalNativeBuild { | |||||
cmake { | |||||
path "jni/CMakeLists.txt" | |||||
version "3.22.1" | |||||
} | |||||
} | |||||
sourceSets { | |||||
main { | |||||
manifest.srcFile 'AndroidManifest.xml' | |||||
java.srcDirs = ['src'] | |||||
res.srcDirs = ['res'] | |||||
} | |||||
androidTest.setRoot('eSpeakTests') | |||||
androidTest { | |||||
java.srcDirs = ['eSpeakTests/src'] | |||||
} | |||||
} | |||||
lint { | |||||
abortOnError false | |||||
} | |||||
} | |||||
tasks.register('checkData') { | tasks.register('checkData') { | ||||
doFirst { | doFirst { | ||||
assert file("../espeak-ng-data/en_dict").exists() | assert file("../espeak-ng-data/en_dict").exists() | ||||
} | } | ||||
tasks.register('createDataArchive', Zip) { | tasks.register('createDataArchive', Zip) { | ||||
dependsOn tasks.checkData | |||||
preserveFileTimestamps = false | preserveFileTimestamps = false | ||||
reproducibleFileOrder = true | reproducibleFileOrder = true | ||||
archiveFileName = "espeakdata.zip" | archiveFileName = "espeakdata.zip" | ||||
destinationDirectory = file("res/raw") | destinationDirectory = file("res/raw") | ||||
from("../espeak-ng-data/") { | |||||
from("build/generated/espeak-ng-data/") { | |||||
into "espeak-ng-data" | into "espeak-ng-data" | ||||
} | } | ||||
} | } | ||||
rename { return 'espeakdata_version' } | rename { return 'espeakdata_version' } | ||||
into file("./res/raw") | into file("./res/raw") | ||||
} | } | ||||
preBuild.dependsOn createDataVersion | |||||
android { | |||||
compileSdk 33 | |||||
ndkVersion "25.1.8937393" | |||||
namespace "com.reecedunn.espeak" | |||||
defaultConfig { | |||||
minSdk 19 | |||||
targetSdk 30 | |||||
versionCode 22 | |||||
versionName "1.52-dev" | |||||
} | |||||
externalNativeBuild { | |||||
ndkBuild { | |||||
path "jni/Android.mk" | |||||
} | |||||
} | |||||
sourceSets { | |||||
main { | |||||
manifest.srcFile 'AndroidManifest.xml' | |||||
java.srcDirs = ['src'] | |||||
res.srcDirs = ['res'] | |||||
} | |||||
androidTest.setRoot('eSpeakTests') | |||||
androidTest { | |||||
java.srcDirs = ['eSpeakTests/src'] | |||||
} | |||||
} | |||||
lint { | |||||
abortOnError false | |||||
} | |||||
project.afterEvaluate { | |||||
tasks.checkData.dependsOn(externalNativeBuildDebug) | |||||
tasks.createDataArchive.dependsOn(externalNativeBuildDebug) | |||||
javaPreCompileDebug.dependsOn(tasks.createDataVersion) | |||||
javaPreCompileRelease.dependsOn(tasks.createDataVersion) | |||||
} | } |
LOCAL_PATH:= $(call my-dir) | |||||
include $(CLEAR_VARS) | |||||
#LOCAL_CFLAGS = -std=c11 # speechplayer is c++ code | |||||
# ucd-tools wide-character compatibility support: | |||||
UCDTOOLS_SRC_PATH := ../../src/ucd-tools/src | |||||
UCDTOOLS_SRC_FILES := \ | |||||
$(subst $(LOCAL_PATH)/$(UCDTOOLS_SRC_PATH),$(UCDTOOLS_SRC_PATH),$(wildcard $(LOCAL_PATH)/$(UCDTOOLS_SRC_PATH)/*.c*)) | |||||
LOCAL_SRC_FILES += $(UCDTOOLS_SRC_FILES) | |||||
SPEECHPLAYER_SRC_FILES := \ | |||||
../../src/speechPlayer/src/frame.cpp \ | |||||
../../src/speechPlayer/src/speechPlayer.cpp \ | |||||
../../src/speechPlayer/src/speechWaveGenerator.cpp | |||||
LOCAL_SRC_FILES += $(SPEECHPLAYER_SRC_FILES) | |||||
ESPEAK_SOURCES := \ | |||||
src/libespeak-ng/common.c \ | |||||
src/libespeak-ng/compiledata.c \ | |||||
src/libespeak-ng/compiledict.c \ | |||||
src/libespeak-ng/compilembrola.c \ | |||||
src/libespeak-ng/dictionary.c \ | |||||
src/libespeak-ng/encoding.c \ | |||||
src/libespeak-ng/error.c \ | |||||
src/libespeak-ng/espeak_api.c \ | |||||
src/libespeak-ng/ieee80.c \ | |||||
src/libespeak-ng/intonation.c \ | |||||
src/libespeak-ng/klatt.c \ | |||||
src/libespeak-ng/langopts.c \ | |||||
src/libespeak-ng/mnemonics.c \ | |||||
src/libespeak-ng/numbers.c \ | |||||
src/libespeak-ng/phoneme.c \ | |||||
src/libespeak-ng/phonemelist.c \ | |||||
src/libespeak-ng/readclause.c \ | |||||
src/libespeak-ng/setlengths.c \ | |||||
src/libespeak-ng/soundicon.c \ | |||||
src/libespeak-ng/spect.c \ | |||||
src/libespeak-ng/speech.c \ | |||||
src/libespeak-ng/sPlayer.c \ | |||||
src/libespeak-ng/ssml.c \ | |||||
src/libespeak-ng/synthdata.c \ | |||||
src/libespeak-ng/synthesize.c \ | |||||
src/libespeak-ng/synth_mbrola.c \ | |||||
src/libespeak-ng/translate.c \ | |||||
src/libespeak-ng/translateword.c \ | |||||
src/libespeak-ng/tr_languages.c \ | |||||
src/libespeak-ng/voices.c \ | |||||
src/libespeak-ng/wavegen.c | |||||
ESPEAK_SRC_PATH := ../../src | |||||
ESPEAK_SRC_FILES := \ | |||||
$(subst src/,$(ESPEAK_SRC_PATH)/,$(ESPEAK_SOURCES)) | |||||
LOCAL_CFLAGS += -DINCLUDE_KLATT -DINCLUDE_SPEECHPLAYER -DINCLUDE_SONIC -DHAVE_ENDIAN_H | |||||
LOCAL_SRC_FILES += \ | |||||
$(filter-out $(BLACKLIST_SRC_FILES),$(ESPEAK_SRC_FILES)) | |||||
# JNI | |||||
LOCAL_SRC_FILES += \ | |||||
$(subst $(LOCAL_PATH)/jni,jni,$(wildcard $(LOCAL_PATH)/jni/*.c)) | |||||
# Common | |||||
LOCAL_C_INCLUDES += \ | |||||
$(LOCAL_PATH)/include \ | |||||
$(LOCAL_PATH)/$(UCDTOOLS_SRC_PATH)/include \ | |||||
$(LOCAL_PATH)/../../src/speechPlayer/include \ | |||||
$(LOCAL_PATH)/$(ESPEAK_SRC_PATH)/include \ | |||||
$(LOCAL_PATH)/$(ESPEAK_SRC_PATH)/include/compat | |||||
LOCAL_LDLIBS := \ | |||||
-llog | |||||
LOCAL_MODULE := libttsespeak | |||||
LOCAL_MODULE_TAGS := optional | |||||
LOCAL_PRELINK_MODULE := false | |||||
include $(BUILD_SHARED_LIBRARY) |
cmake_minimum_required(VERSION 3.8) | |||||
project(espeak-android) | |||||
add_subdirectory(../../ espeakng) | |||||
add_custom_target( | |||||
espeak-data | |||||
COMMAND ${CMAKE_COMMAND} -B${CMAKE_CURRENT_BINARY_DIR}/../espeak-data ${CMAKE_CURRENT_SOURCE_DIR}/../../ | |||||
COMMAND ${CMAKE_COMMAND} --build ${CMAKE_CURRENT_BINARY_DIR}/../espeak-data --target data | |||||
COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_CURRENT_BINARY_DIR}/../espeak-data/espeak-ng-data ${CMAKE_SOURCE_DIR}/../build/generated/espeak-ng-data | |||||
) | |||||
message(STATUS "Data: ${CMAKE_CURRENT_SOURCE_DIR}") | |||||
find_library(A_LOG log) | |||||
add_library(ttsespeak SHARED | |||||
jni/eSpeakService.c | |||||
) | |||||
target_include_directories(ttsespeak PRIVATE | |||||
include | |||||
) | |||||
target_link_libraries(ttsespeak PRIVATE | |||||
espeak-ng ${A_LOG} | |||||
) |
#include <unistd.h> | #include <unistd.h> | ||||
#include <stdlib.h> | #include <stdlib.h> | ||||
#include <stdbool.h> | #include <stdbool.h> | ||||
#include <string.h> | |||||
#include <jni.h> | #include <jni.h> | ||||
#include <espeak/speak_lib.h> | #include <espeak/speak_lib.h> |
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) | |||||
if (HAVE_PTHREAD) | |||||
option(USE_ASYNC "Support asynchronous speech synthesis" ON) | |||||
else() | |||||
set(USE_ASYNC OFF) | |||||
endif() |
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} "$<TARGET_FILE:espeak-ng-bin>") | |||||
add_custom_command( | |||||
OUTPUT "${DATA_DIST_DIR}/intonations" | |||||
COMMAND ${ESPEAK_RUN_CMD} --compile-intonations | |||||
WORKING_DIRECTORY "${PHONEME_SRC_DIR}" | |||||
COMMENT "Compile intonations" | |||||
DEPENDS | |||||
"$<TARGET_FILE:espeak-ng-bin>" | |||||
"${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 | |||||
"$<TARGET_FILE:espeak-ng-bin>" | |||||
${_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 | |||||
"$<TARGET_FILE:espeak-ng-bin>" | |||||
"${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 "$<TARGET_FILE:espeak-ng-bin>" "${_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) |
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() |
list(APPEND CPACK_SOURCE_IGNORE_FILES | |||||
/\.git/ | |||||
/\.gitignore | |||||
/\.github/ | |||||
/\.tx/ | |||||
/\.travis.yml | |||||
/_layouts/ | |||||
/android/ | |||||
/build/ | |||||
/chromium_extension/ | |||||
/data/ | |||||
/docs/ | |||||
/emscripten/ | |||||
/fastlane/ | |||||
/tools/ | |||||
/vim/ | |||||
) |
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 | |||||
) | |||||
if (MSVC) | |||||
target_sources(espeak-ng-bin PRIVATE compat/getopt.c) | |||||
endif() | |||||
if (NOT WIN32) | |||||
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 | |||||
) | |||||
endif() | |||||
install(TARGETS espeak-ng-bin) | |||||
install(DIRECTORY include/espeak include/espeak-ng TYPE INCLUDE) |
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 | |||||
) | |||||
if (NOT MSVC) | |||||
target_compile_options(espeak-ng PRIVATE | |||||
"-fPIC" | |||||
"-fvisibility=hidden" | |||||
"-fno-exceptions" | |||||
"-fwrapv" | |||||
"-pedantic" | |||||
"-Wunused-parameter" | |||||
"-Wunused" | |||||
"-Wuninitialized" | |||||
"-Wreturn-type" | |||||
"-Wmissing-prototypes" | |||||
"-Wint-conversion" | |||||
"-Wimplicit" | |||||
) | |||||
endif() | |||||
target_compile_definitions(espeak-ng PRIVATE "LIBESPEAK_NG_EXPORT=1") | |||||
if (NOT BUILD_SHARED_LIBS) | |||||
target_compile_definitions(espeak-ng INTERFACE "LIBESPEAK_NG_EXPORT=1") | |||||
endif() | |||||
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) | |||||
if (NOT MSVC) | |||||
target_link_libraries(espeak-ng PRIVATE m) | |||||
endif() | |||||
target_link_libraries(espeak-ng PUBLIC espeak-include) | |||||
install(TARGETS espeak-ng LIBRARY) |
#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}" |
add_library(speechPlayer STATIC | |||||
src/frame.cpp | |||||
src/speechPlayer.cpp | |||||
src/speechWaveGenerator.cpp | |||||
) | |||||
target_include_directories(speechPlayer PUBLIC include) | |||||
if(NOT MSVC) | |||||
target_compile_options(speechPlayer PRIVATE "-fPIC") | |||||
endif() | |||||
install(TARGETS speechPlayer ARCHIVE) |
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) |
include(CTest) | |||||
list(APPEND _binary_tests) | |||||
macro(compiled_test _test_name) | |||||
add_executable(test_${_test_name} | |||||
$<TARGET_OBJECTS:espeak-ng> | |||||
${_test_name}.c | |||||
) | |||||
target_link_libraries(test_${_test_name} PRIVATE | |||||
$<TARGET_PROPERTY:espeak-ng,LINK_LIBRARIES> | |||||
) | |||||
target_include_directories( | |||||
test_${_test_name} PRIVATE | |||||
$<TARGET_PROPERTY:espeak-ng,SOURCE_DIR> | |||||
$<TARGET_PROPERTY:espeak-ng,SOURCE_DIR>/include/compat | |||||
$<TARGET_PROPERTY:espeak-ng,INTERFACE_INCLUDE_DIRECTORIES> | |||||
$<TARGET_PROPERTY:espeak-ng-config,INTERFACE_INCLUDE_DIRECTORIES> | |||||
) | |||||
add_dependencies(test_${_test_name} data) | |||||
add_test( | |||||
NAME ${_test_name} | |||||
COMMAND ${ESPEAK_RUN_ENV} $<TARGET_FILE:test_${_test_name}> | |||||
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/.. | |||||
) | |||||
list(APPEND _binary_tests test_${_test_name}) | |||||
endmacro(compiled_test) | |||||
find_program(SHELL bash) | |||||
macro(shell_test _test_name) | |||||
add_test( | |||||
NAME ${_test_name} | |||||
COMMAND ${ESPEAK_RUN_ENV} ESPEAK_BIN=$<TARGET_FILE:espeak-ng-bin> ${SHELL} ${CMAKE_CURRENT_SOURCE_DIR}/${_test_name}.test | |||||
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/.. | |||||
) | |||||
endmacro(shell_test) | |||||
compiled_test(api) | |||||
compiled_test(encoding) | |||||
compiled_test(ieee80) | |||||
compiled_test(readclause) | |||||
if (SHELL AND UNIX) | |||||
shell_test(bom) | |||||
shell_test(non-executable-files-with-executable-bit) | |||||
shell_test(cmd_options) | |||||
shell_test(dictionary) | |||||
shell_test(language-numbers-cardinal) | |||||
shell_test(language-numbers-ordinal) | |||||
shell_test(language-phonemes) | |||||
shell_test(language-pronunciation) | |||||
shell_test(language-replace) | |||||
shell_test(ssml) | |||||
shell_test(translate) | |||||
shell_test(variants) | |||||
shell_test(voices) | |||||
# shell_test(windows-data) | |||||
# shell_test(windows-installer) | |||||
if (USE_KLATT) | |||||
shell_test(klatt) | |||||
endif() | |||||
if (USE_MBROLA) | |||||
shell_test(mbrola) | |||||
endif() | |||||
endif() | |||||
add_custom_target(tests DEPENDS ${_binary_tests}) |
# TODO checking for other encodings (e.g. UTF-16, UTF-32, UTF-7) needs more elaborate search, | # TODO checking for other encodings (e.g. UTF-16, UTF-32, UTF-7) needs more elaborate search, | ||||
# otherwise it shows many false positives | # otherwise it shows many false positives | ||||
grep -ErlI $'\xEF\xBB\xBF' * | tee tests/bom.check >/dev/null # UTF-8 | |||||
cd $(dirname $0)/.. | |||||
grep -ErlI $'\xEF\xBB\xBF' * | grep -v '^build/' | tee tests/bom.check >/dev/null # UTF-8 | |||||
if [ -s tests/bom.check ] ; then | if [ -s tests/bom.check ] ; then | ||||
echo "found:" | echo "found:" | ||||
echo "none found" | echo "none found" | ||||
true | true | ||||
fi | fi | ||||
# read column 5 (File) of espeak-ng --voices, skip the directory | # read column 5 (File) of espeak-ng --voices, skip the directory | ||||
echo "checking if all supported languages have phoneme tests ..." | echo "checking if all supported languages have phoneme tests ..." | ||||
for lang in $(ESPEAK_DATA_PATH=`pwd` LD_LIBRARY_PATH=src:${LD_LIBRARY_PATH} src/espeak-ng --voices | awk '{print $5}' | cut -d "/" -f 2-2 | tail -n+2); | for lang in $(ESPEAK_DATA_PATH=`pwd` LD_LIBRARY_PATH=src:${LD_LIBRARY_PATH} src/espeak-ng --voices | awk '{print $5}' | cut -d "/" -f 2-2 | tail -n+2); | ||||
do if ! grep -q "test_phwav $lang" tests/language-phonemes.test | |||||
do if ! grep -q "test_phwav $lang" $0 | |||||
then | then | ||||
echo "$lang missing a phoneme test in tests/language-phonemes.test"; | echo "$lang missing a phoneme test in tests/language-phonemes.test"; | ||||
exit 1; | exit 1; |
echo -n "testing files for executable bits that shouldn't be executable ... " | echo -n "testing files for executable bits that shouldn't be executable ... " | ||||
cd $(dirname $0)/.. | |||||
find * -executable -type f | \ | find * -executable -type f | \ | ||||
grep -vE "compile|config\.(guess|status|sub)|configure|depcomp|install-sh|libtool|missing" | # Ignore autotools output \ | grep -vE "compile|config\.(guess|status|sub)|configure|depcomp|install-sh|libtool|missing" | # Ignore autotools output \ | ||||
grep -vE "build" | # Ignore cmake output \ | |||||
grep -vE ".*\.test|tests/common|src(/\.libs)?/(e?speak-ng|.*\.so\..*)|src/\.libs/lt-espeak-ng|android" | # Ignore built programs and libraries \ | grep -vE ".*\.test|tests/common|src(/\.libs)?/(e?speak-ng|.*\.so\..*)|src/\.libs/lt-espeak-ng|android" | # Ignore built programs and libraries \ | ||||
grep -vE ".*\.sh|tools/emoji" | # Ignore helper scripts \ | grep -vE ".*\.sh|tools/emoji" | # Ignore helper scripts \ | ||||
grep -vE "src/ucd-tools/tools/(.*\.py|mkencodingtable)" | # Ignore ucd-tools helper scripts \ | grep -vE "src/ucd-tools/tools/(.*\.py|mkencodingtable)" | # Ignore ucd-tools helper scripts \ | ||||
echo "none found" | echo "none found" | ||||
true | true | ||||
fi | fi | ||||
diff expected.txt actual.txt || exit 1 | diff expected.txt actual.txt || exit 1 | ||||
} | } | ||||
for i in `ls tests/ssml/*.ssml` ; do test_ssml $i; done | |||||
for i in `ls tests/ssml/*.ssml2` ; do test_ssml $i punct; done | |||||
for i in `dirname $0`/ssml/*.ssml ; do test_ssml $i; done | |||||
for i in `dirname $0`/ssml/*.ssml2 ; do test_ssml $i punct; done | |||||
test_ssml_audio "<prosody>" 88fccb35536158f25f4ae44a03fb005fef95c99b "<speak><prosody rate=\"x-slow\" pitch=\"low\"> Slow and low </prosody><prosody rate=\"x-fast\" pitch=\"x-high\"> Fast and high.</prosody></speak>" | test_ssml_audio "<prosody>" 88fccb35536158f25f4ae44a03fb005fef95c99b "<speak><prosody rate=\"x-slow\" pitch=\"low\"> Slow and low </prosody><prosody rate=\"x-fast\" pitch=\"x-high\"> Fast and high.</prosody></speak>" | ||||
# #410 is a bug in SSML. Sentence termination causes prosody stack to misfunction. | # #410 is a bug in SSML. Sentence termination causes prosody stack to misfunction. |