Initial commit
This commit is contained in:
133
espeak
Executable file
133
espeak
Executable file
@@ -0,0 +1,133 @@
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
set -x
|
||||
|
||||
# This wrapper is a drop-in replacement for espeak, which does the following:
|
||||
# 1. If the language is not Japanese, it calls espesk from the system directly.
|
||||
# 2. If the language is Japanese, it calls open_jtalk. Models and voice are
|
||||
# provided in the japanese/ folder. They're licensed under BSD-3-Clause, so
|
||||
# we can redistribute them with the game.
|
||||
# 3. If open_jtalk is not available, fall back to espeak.
|
||||
|
||||
ROOT="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)"
|
||||
|
||||
REAL_ESPEAK="$(command -v espeak-ng || true)"
|
||||
if [[ -z "${REAL_ESPEAK}" ]]; then
|
||||
REAL_ESPEAK="$(command -v /usr/bin/espeak || command -v espeak)"
|
||||
fi
|
||||
|
||||
if [[ -z "${REAL_ESPEAK}" ]]; then
|
||||
echo "ERROR: could not find espeak or espeak-ng in PATH" >&2
|
||||
exit 127
|
||||
fi
|
||||
|
||||
OPENJTALK="$(command -v open_jtalk || true)"
|
||||
if [[ -z "${OPENJTALK}" ]]; then
|
||||
echo "WARNING: could not find open_jtalk in PATH" >&2
|
||||
echo "Falling back to espeak for all languages, including Japanese." >&2
|
||||
fi
|
||||
|
||||
PLAYER="$(command -v paplay || command -v aplay || true)"
|
||||
if [[ -z "${PLAYER}" ]]; then
|
||||
echo "WARNING: could not find paplay or aplay in PATH" >&2
|
||||
echo "Falling back to espeak for all languages, including Japanese." >&2
|
||||
fi
|
||||
|
||||
JTALK_DIC="${ROOT}/japanese/dict"
|
||||
JTALK_VOICE="${ROOT}/japanese/voice/nitech_jp_atr503_m001.htsvoice"
|
||||
|
||||
voice=""
|
||||
declare -a passthrough=()
|
||||
text=""
|
||||
|
||||
while (($#)); do
|
||||
case "$1" in
|
||||
-v)
|
||||
shift
|
||||
voice="${1:-}"
|
||||
;;
|
||||
-v*)
|
||||
voice="${1#-v}"
|
||||
;;
|
||||
# keep other flags for espeak fallback (-a is used by Ren'Py)
|
||||
-*)
|
||||
passthrough+=("$1")
|
||||
# capture arg for flags like -a <amp>
|
||||
if [[ "$1" == "-a" && $# -ge 2 ]]; then
|
||||
shift
|
||||
passthrough+=("$1")
|
||||
fi
|
||||
;;
|
||||
*)
|
||||
# Ren'Py appends the full text as one argument.
|
||||
# If something else adds extra args, last one wins.
|
||||
text="$1"
|
||||
;;
|
||||
esac
|
||||
shift || true
|
||||
done
|
||||
|
||||
|
||||
fallback() {
|
||||
echo "ERROR: OpenJTalk failed; falling back to espeak" >&2
|
||||
}
|
||||
|
||||
voice_openjtalk() {
|
||||
[[ -n "${OPENJTALK}" && -x "${OPENJTALK}" ]] || return 2
|
||||
[[ -d "${JTALK_DIC}" ]] || return 3
|
||||
[[ -f "${JTALK_VOICE}" ]] || return 4
|
||||
[[ -n "${text}" ]] || return 5
|
||||
|
||||
local tmpwav
|
||||
tmpwav="$(mktemp --suffix=.wav)"
|
||||
|
||||
set +e
|
||||
printf "%s" "${text}" | "${OPENJTALK}" \
|
||||
-x "${JTALK_DIC}" \
|
||||
-m "${JTALK_VOICE}" \
|
||||
-ow "${tmpwav}" \
|
||||
>/dev/null 2>&1
|
||||
local rc=$?
|
||||
set -e
|
||||
|
||||
# If a generated wav file is empty, fallback.
|
||||
if [[ $rc -ne 0 || ! -s "${tmpwav}" ]]; then
|
||||
rm -f "${tmpwav}"
|
||||
return 10
|
||||
fi
|
||||
|
||||
# If there is no player, fallback to espeak.
|
||||
if [[ -z "${PLAYER}" ]]; then
|
||||
rm -f "${tmpwav}"
|
||||
return 11
|
||||
fi
|
||||
|
||||
set +e
|
||||
"${PLAYER}" "${tmpwav}" >/dev/null 2>&1
|
||||
local prc=$?
|
||||
set -e
|
||||
|
||||
rm -f "${tmpwav}"
|
||||
[[ $prc -eq 0 ]] || return 12
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
if [[ "${voice}" == "ja" ]]; then
|
||||
if voice_openjtalk; then
|
||||
exit 0
|
||||
else
|
||||
fallback
|
||||
fi
|
||||
fi
|
||||
|
||||
# Fallback to espeak.
|
||||
if [[ -n "${voice}" ]]; then
|
||||
exec "${REAL_ESPEAK}" -v "${voice}" "${passthrough[@]}" "${text}"
|
||||
else
|
||||
exec "${REAL_ESPEAK}" "${passthrough[@]}" "${text}"
|
||||
fi
|
||||
Reference in New Issue
Block a user